• 14
• 12
• 9
• 10
• 9

# [C++] Compile time error in default case

This topic is 3521 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

I need to map certain OpenGL enums to indices in range 0..N-1 where N is the number of enums. This should happen in compile time and in such a way that unsupported enums generate an compilation error. This would be used in OpenGL state cache/stack 'library'. Partial solution for mapping function might be:
inline EnableState GetState(GLenum glenum) {
switch(glenum) {
case GL_ALPHA_TEST: 	  return ALPHA_TEST;    // 0
..
case GL_VERTEX_ARRAY:   return VERTEX_ARRAY;  // N-1
default: 		  <GENERATE_COMPILE_TIME_ERROR>
}
}

this is called by other inline functions that enable/disable/push/pop opengl states. For example Enable function:
inline void Enable(GLenum glenum) {
const EnableState state = GetState(glenum);
if(!*EnableStackPtrs[state]) {
glEnable(EnableEnums[state]);
*EnableStackPtrs[state] = true;
}
}

This works and all inlines collapse into tight code so that's good so far. Problem is I haven't been able to work out satisfactory solution to that second requirement: error generation at compile-time when 'wrong' GLenum is used. Either A) error happens runtime or B) error happens ALWAYS at compile-time. I just can't get it to work _only_ when that invalid enum is used. Part of the problem is that constant value given as argument to function is not an constant expression anymore inside the function. If it was I could fex. use template specialization instead to do the mapping and errors would be automatically caught:
template<GLenum> struct MapEnum {};
template<> struct MapEnum<GL_ALPHA_TEST> { enum { value = DEPTH_TEST }; };
..
template<> struct MapEnum<GL_VERTEX_ARRAY> { enum { value = VERTEX_ARRAY }; };

inline void Enable(GLenum glenum) {
const EnableState state = MapEnum<glenum>::value;
..
}


I could give up and just throw error at runtime, but that feels somehow wrong. I hope there might be an easy solution and it's just my ignorance of this cursed run/compile time boundary thing that's confusing me. I would appreciate any comments or ideas here.

##### Share on other sites
It's easy: if you wish to check the value at compile-time, you must know the value at compile time. The entire point of a function parameter is that its value is not known at compile time. Therefore, it's impossible to check if the value of a function parameter is correct at compile time.

If you wish to guarantee that only a certain set of enumeration values will be used, use an enumeration with exactly that many values everywhere in your code, and you'll never get other values because the type system will take care of you.

##### Share on other sites

I was somehow hoping there might be a way to bypass those C++ constant expression requirements when functions are inline. But guess not.

I actually have the system setup as you suggest and it works well enough. It's just that rest of the code uses GLenums as arguments (and other GLtypes) so it would have been nice to have unified appearance in the interface and still have errors reported when trying to compile. But it works so can't really complain...

##### Share on other sites
Quick guess: you could try creating templates with an integer non-type parameter, specializing them for each valid case, and giving a compile-time error in the default case.

##### Share on other sites
Quote:
 Original post by ToohrVykIf you wish to guarantee that only a certain set of enumeration values will be used, use an enumeration with exactly that many values everywhere in your code, and you'll never get other values because the type system will take care of you.

Almost. You forgot that you don't have to initialize enums :(

MyEnum i;foo(i);

##### Share on other sites
It is also possible to assign any value to an enum variable with a cast.

As already said, a function receiving an incorrect argument is a run-time error which you cannot detect at compile-time. You might consider throwing an exception (some other run-time error handling mechanism) in the default case.

##### Share on other sites
Quote:
 Original post by MadKeithVQuick guess: you could try creating templates with an integer non-type parameter, specializing them for each valid case, and giving a compile-time error in the default case.

Something like this ?

<in client code>
lib::Enable(lib::Map<GL_DEPTH_TEST>::index);

i guess that would work, but kind of defeats the whole purpose of making things
appear more simple to user :/

If I put that template inside Enable() function

inline void Enable(GLenum glenum) {	const int i = Map<glenum>::index;	..}

my compiler gives:

test.cpp:55: error: ‘glenum’ cannot appear in a constant-expression
test.cpp:55: error: template argument 1 is invalid

I'm not sure how to make it work that way. Maybe it's impossible at present. Sure would be nice feature to have.

##### Share on other sites
You can't do that, that is a runtime variable. You can however use tags. That function would become:

struct gl_vertex_array_tag {   static GLenum value = GL_VERTEX_ARRAY;};template <typename T>inline void Enable() {	const int i = T::value;	..}// Calling codeEnable<gl_vertex_array_tag>();

##### Share on other sites
Quote:
Original post by jkv
Quote:
 Original post by MadKeithVQuick guess: you could try creating templates with an integer non-type parameter, specializing them for each valid case, and giving a compile-time error in the default case.

Something like this ?

<in client code>
lib::Enable(lib::Map<GL_DEPTH_TEST>::index);

i guess that would work, but kind of defeats the whole purpose of making things
appear more simple to user :/

Why not something like
lib::Enable<GL_DEPTH_TEST>();

Quote:
 If I put that template inside Enable() function*** Source Snippet Removed ***my compiler gives:test.cpp:55: error: ‘glenum’ cannot appear in a constant-expressiontest.cpp:55: error: template argument 1 is invalid

Yep, as already said, the function argument is a runtime expression. While compiling the function, it is not known which arguments it is called with. (And even if the compiler can figure this out through some global optimizations, the language doesn't permit it to treat the argument as a compile-time expression)
You've made a function which takes an argument at runtime. So it's impossible to stuff that runtime argument into a compile-time template.

##### Share on other sites
Quote:
 Original post by jkvI need to map certain OpenGL enums to indices in range 0..N-1 where N is the number of enums.

Why can't you just use the actual enum values?

Are you trying to combine a few OpenGL enums into some kind of super-enum? If so, what on earth problem do you think it will solve to do so?

Quote:
 This should happen in compile time

You're passing a parameter to the function, which is what you're switching on. The parameter's value isn't necessarily known at compile time, so how can you expect the compiler to make a determination at compile time?

Quote:
 Part of the problem is that constant value given as argument to function is not an constant expression anymore inside the function. If it was I could fex. use template specialization instead to do the mapping and errors would be automatically caught:

Did you know that you can template on (integral) values, not only types?

Quote:
 I could give up and just throw error at runtime, but that feels somehow wrong.

Why?