我的代码如何告诉编译时常量与变量?

时间:2011-08-04 09:34:46

标签: c++ templates visual-c++ macros metaprogramming

这是我的问题。我有一个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
}

这个宏根本不是万无一失的 - 可以在那里传递-134,并且最多会出现警告,但行为将是未定义的。我想让它变得更加万无一失。

对于常量场景,我可以使用模板:

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,但看起来并不是同样的问题。

是否有一些构造允许在两者之间交替,这取决于作为标志号传递的表达式是编译时常量还是变量?

3 个答案:

答案 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)替换这些宏。这是唯一可行的方法。