这是我的问题。我有一个BINARY_FLAG
宏:
#define BINARY_FLAG( n ) ( static_cast<DWORD>( 1 << ( n ) ) )
可以像这样使用(“常量”场景):
static const SomeConstant = BINARY_FLAG( 5 );
或者像这样(“变量”场景):
for( int i = 0; i < 10; i++ ) {
DWORD flag = BINARY_FLAG( i );
// do something with the value
}
这个宏根本不是万无一失的 - 可以在那里传递-1
或34
,并且最多会出现警告,但行为将是未定义的。我想让它变得更加万无一失。
对于常量场景,我可以使用模板:
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
但这不会用于“变量”场景 - 我需要在那里运行时断言:
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)
后者很好,但没有编译时检查。当然,我希望尽可能进行编译时检查,否则进行运行时检查。在任何时候我都希望尽可能少的运行时开销,所以当可能进行编译时检查时,我不希望进行函数调用(可能不会内联)。
我看到了this question,但看起来并不是同样的问题。
是否有一些构造允许在两者之间交替,这取决于作为标志号传递的表达式是编译时常量还是变量?
答案 0 :(得分:2)
这比你想象的要简单:)
我们来看看:
#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);
}
我不使用MSVC,但我使用Mingw GCC 4.5编译:
g ++ -c -S -O3 08042.cpp
第一种方法的结果代码如下:
__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
第二个:
__Z5test2v:
pushl %ebp
movl %esp, %ebp
movl $32, %eax
leave
ret
请参阅?编译器非常聪明,可以为您完成。不需要宏,不需要元编程,不需要C ++ 0x。就这么简单。
检查MSVC是否也这样做......但是看看 - 编译器很容易评估常量表达式并删除未使用的条件分支。如果你想确定它,请检查它。但一般来说 - 相信你的工具。
答案 1 :(得分:1)
我建议你使用两个宏。 的 BINARY_FLAG CONST_BINARY_FLAG 强> 这将使您的代码更容易为他人掌握。在撰写本文时,您确实知道它是不是const。 我绝不会担心运行时开销。你的优化器,至少在VS中,将为你排序。
答案 2 :(得分:1)
不可能将参数传递给宏或函数,并确定它是编译时常量还是变量。
最好的方法是使用编译时代码#define BINARY_FLAG(n)
并将该宏放在任何地方然后编译它。您将在n
将成为运行时的位置收到编译器错误。现在,您可以使用运行时宏BINARY_FLAG_RUNTIME(n)
替换这些宏。这是唯一可行的方法。