我的代码如何区分编译时常量和变量?

发布于 2024-11-27 15:07:34 字数 1347 浏览 1 评论 0原文

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

后者很好,但没有编译时检查。当然,我希望尽可能进行编译时检查,否则进行运行时检查。在任何时候,我都希望运行时开销尽可能小,因此当可以进行编译时检查时,我不希望进行函数调用(可能不会被内联)。

我看到这个问题,但看起来并不是同一个问题。

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

Here's my problem. I have a BINARY_FLAG macro:

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

Which can be used either like this ("constant" scenario):

static const SomeConstant = BINARY_FLAG( 5 );

or like this ("variable" scenario):

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

This macro is not foolproof at all - one can pass -1 or 34 there and there will at most be a warning yet behavior will be undefined. I'd like to make it more foolproof.

For the 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 go for the "variable" scenario - I'd need a runtime assertion there:

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'd like a compile-time check where possible and a runtime check otherwise. At all times I want as little runtime overhead as possible so I don't want a function call (that maybe won't be inlined) when a compile-time check is possible.

I saw this question, but it doesn't look like it is about the same problem.

Is there some construct that would allow to alternate between the two depending on whether the expression passed as a flag number is a compile-time constant or a variable?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

往事随风而去 2024-12-04 15:07:34

这比你想象的要简单:)

让我们看看:

#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 是否做同样的事情...但是看 - 编译器计算常量表达式并删除未使用的条件分支真的很容易。如果您想确定,请检查它...但一般情况下- 相信你的工具。

This is simpler than you think :)

Let's have a look:

#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 don't use MSVC, but I compiled with Mingw GCC 4.5:

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

The resulting code for first method looks like:

__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

See? The compiler is smart enough to do it for you. No need for macros, no need for metaprogramming, no need for C++0x. As simple as that.

Check if MSVC does the same... But look - it's really easy for the compiler to evaluate a constant expression and drop the unused conditional branch. Check it if you want to be sure... But generally - trust your tools.

雅心素梦 2024-12-04 15:07:34

不可能将参数传递给宏或函数并确定它是编译时常量还是变量。

最好的方法是使用编译时代码#define BINARY_FLAG(n)并将该宏放置在各处,然后进行编译。您将在 n 运行时的地方收到编译器错误。现在,您可以将这些宏替换为运行时宏 BINARY_FLAG_RUNTIME(n)。这是唯一可行的办法。

It's not possible to pass an argument to a macro or function and determine if it's compile time constant or a variable.

The best way is that you #define BINARY_FLAG(n) with compile time code and place that macro everywhere and then compile it. You will receive compiler-errors at the places where n is going to be runtime. Now, you can replace those macros with your runtime macro BINARY_FLAG_RUNTIME(n). This is the only feasible way.

时光匆匆的小流年 2024-12-04 15:07:34

我建议你使用两个宏。
BINARY_FLAG
CONST_BINARY_FLAG

这将使您的代码更容易被其他人掌握。在撰写本文时,您确实知道它是否是 const。
而且我在任何情况下都不会担心运行时开销。你的优化器,至少在 VS 中,为你解决这个问题。

I suggest you use two macros.
BINARY_FLAG
CONST_BINARY_FLAG

That will make your code easier to grasp for others. You do know, at the time of writing, if it is a const or not.
And I would in no case worry about runtime overhead. Your optimizer, at least in VS, will sort that out for you.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文