How can my code indicate a compile-time constant compared to a variable?

Here is my problem. I have a BINARY_FLAG macro:

 #define BINARY_FLAG( n ) ( static_cast<DWORD>( 1 << ( n ) ) ) 

Which can be used either like this ("persistent" script):

 static const SomeConstant = BINARY_FLAG( 5 ); 

or something like this (script "variable"):

 for( int i = 0; i < 10; i++ ) { DWORD flag = BINARY_FLAG( i ); // do something with the value } 

This macro is not absolutely reliable - you can go -1 or 34 there, and there will be the biggest warning, but the behavior will be undefined. I would like to make it more reliable.

For a constant scenario, I could use a template:

 template<int Shift> class BinaryFlag { staticAssert( 0 <= Shift && Shift < sizeof( DWORD) * CHAR_BIT ); public: static const DWORD FlagValue = static_cast<DWORD>( 1 << Shift ); }; #define BINARY_FLAG( n ) CBinaryFlag<n>::FlagValue 

but this will not mean the script "variable" - I will need a runtime statement:

 inline DWORD ProduceBinaryFlag( int shift ) { assert( 0 <= shift && shift < sizeof( DWORD) * CHAR_BIT ); return static_cast<DWORD>( 1 << shift ); } #define BINARY_FLAG( n ) ProduceBinaryFlag(n) 

The latter is good, but has no compile-time checks. Of course, I would like to check the compilation time, when possible, and check the runtime. In any case, I want as little overhead as possible at runtime, so I don’t want the function call (maybe not inline) when compile time checking is possible.

I saw this question , but it does not seem to be the same issue.

Is there any construction that would allow alternating between them depending on whether the expression passed as the number of flags is a compile-time constant or a variable?

+5
c ++ macros visual-c ++ templates metaprogramming
source share
3 answers

Impossible pass an argument to a macro or function and determine if it compiles a time constant or variable.

The best way is that you #define BINARY_FLAG(n) with compile-time code and put this macro everywhere and then compile it. You will get compiler errors in the places where n will be executed. Now you can replace these macros with your runtime macro BINARY_FLAG_RUNTIME(n) . This is the only possible way.

+1
source share

It is easier than you think :)

We'll see:

 #include <cassert> static inline int FLAG(int n) { assert(n>=0 && n<32); return 1<<n; } int test1(int n) { return FLAG(n); } int test2() { return FLAG(5); } 

I do not use MSVC, but compiled with Mingw GCC 4.5:

g ++ -c -S -O3 08042.cpp

The resulting code for the first method looks like this:

 __Z5test1i: pushl %ebp movl %esp, %ebp subl $24, %esp movl 8(%ebp), %ecx cmpl $31, %ecx ja L4 movl $1, %eax sall %cl, %eax leave ret L4: movl $4, 8(%esp) movl $LC0, 4(%esp) movl $LC1, (%esp) call __assert .p2align 2,,3 

And the second:

 __Z5test2v: pushl %ebp movl %esp, %ebp movl $32, %eax leave ret 

Cm? The compiler is smart enough to do this for you. No need for macros, no need for metaprogramming, no need for C ++ 0x. So simple.

Check if MSVC does the same ... But look - it is very easy for the compiler to evaluate a constant expression and discard an unused conditional branch. Check it out if you want to be sure. But in general - trust your tools.

+2
source share

I suggest you use two macros. BINARY_FLAG CONST_BINARY_FLAG This will simplify your code for others. You know, at the time of writing, if it is const or not. And by no means will I worry about exceeding the lead time. Your optimizer, at least in VS, will be sorted for you.

+1
source share

All Articles