【发布时间】:2011-10-19 19:50:33
【问题描述】:
这是我的问题。我有一个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,但看起来不是同一个问题。
是否有一些结构可以根据作为标志号传递的表达式是编译时常量还是变量来在两者之间交替?
【问题讨论】:
-
assert( 0 <= flag && flag < 32 )... 你的意思是flag==means==>shift这里吗?另外,如果您在运行时将争论传递给ProduceBinaryFlag(),那么您如何在编译时进行检查?还是我错过了什么? -
另一个例子:
const int myflag = 5; BINARY_FLAG(myflag);。myflag是编译时常量,但不是文字。问题中的示例仅使用编译时常量的一种特殊情况。 -
一个想法 - 如果你愿意假设一个支持 VLA 的 C++ 实现,那么宏可以首先进行运行时检查,然后如果
n在范围内,则声明一个大小为正的数组否则为负。在n是整数常量表达式的情况下,如果表示大小的表达式也是 ICE,这将在编译时失败。在n不是常量的情况下,UB 将被运行时检查阻止。由于该阵列很小且未使用,因此希望它不会产生巨大的成本。 -
@iammilind:我修复了代码。好吧,是的,如果我将它传递给一个函数,它就不再是编译时检查,这就是我想要避免的。
-
在 C++0x 实现的当前状态下,这完全不可能。当编译器实现
constexpr时,您将有办法。与此同时,我不明白为什么你的宏不正常。
标签: c++ templates visual-c++ macros metaprogramming