【问题标题】:Compile time assertion as part of an expression but without _Static_assert编译时断言作为表达式的一部分但没有 _Static_assert
【发布时间】:2018-10-20 18:54:57
【问题描述】:

最终,我想要一个本身包含断言的编译时常量宏。

有了真正的_Static_assert,我可以做类似的事情

#define CEXPR_MACRO_WITH_ASSERTION(Assertion) sizeof(struct{char c; _Static_assert(Assertion,""); })?0:42

(用于“编译时断言”之类的东西,宏值计算不会在任何目标上溢出,我想将断言保留在宏中,以便它与值紧密耦合)但是像 tcc 这样的编译器没有静态断言,所以我需要模拟它。

#define STATIC_ASSERT(Cexpr,Msg) extern STATIC_ASSERT[(Cexpr)?1:-1]

是一种常见的方法,但使用 extern 我不能在结构中使用它,所以我可以将它一分为二

#define STATIC_ASSERT(Cexpr,Msg) extern STATIC_ASSERT_(Cexpr,Msg)
#define STATIC_ASSERT_(Cexpr,Msg) char STATIC_ASSERT[sizeof(char [((Cexpr))?1:-1])] /*ignore Msg for simplicity's sake*/

并在CEXPR_MACRO_WITH_ASSERTION 中使用下划线版本,但在函数上下文中,这将对支持其中包含 VLA 的结构的编译器产生误报:

#define STATIC_ASSERT(Cexpr,Msg) extern STATIC_ASSERT_(Cexpr,Msg)
#define STATIC_ASSERT_(Cexpr,Msg) char STATIC_ASSERT[sizeof(char [((Cexpr))?1:-1])]
#define CEXPR_MACRO_WITH_ASSERTION(Assert) (sizeof(struct{char c; STATIC_ASSERT_(Assert,""); })?0:42)

int main(void)
{
    int x = 0;
    CEXPR_MACRO_WITH_ASSERTION(x);
} //compiles on tcc and gcc (clang rejects it because of the vla in a struct)

所以我实际上需要:

#define STATIC_ASSERT_(Cexpr,Msg) char STATIC_ASSERT[sizeof(char [((Cexpr)&&ENFORCE_ICEXPR(Cexpr))?1:-1])]

现在我意识到,特别是在 tcc 上,ENFORCE_ICEXPR(强制整数常量表达式)可以简单地替换为 __builtin_constant_p,但我很好奇是否可以在不依赖平台的情况下做到这一点。

所以我想我可以通过尝试将Cexpr 分配给enum 常量来测试它,我想出了:

#define ENFORCE_Z(X) _Generic(0LL+(X),ullong:(X),llong:(X)) /*could be just `+(X)` cuz I don't care about floats*/
#define ENFORCE_ICEXPR(X) sizeof( void (*)(enum { ENFORCE_ICEXPR = (int)ENFORCE_Z(X) }  ) )

但这让 gcc 和 clang 抱怨(在 gcc 的情况下不可沉默)枚举在声明之外不可见(顺便说一句,这是这里的意图)所以我求助于

#define ENFORCE_ICEXPR(X) sizeof(enum { BX_cat(ENFORCE_ICEXPR__,__COUNTER__) = (int)ENFORCE_Z(X) })

依赖于非标准的魔术宏,__COUNTER__

我的问题是,有没有更好的写ENFORCE_ICEXPR(X)的方法?

【问题讨论】:

  • Perl uses a bit-field instead of an array。没有编译器实现可变长度位域。
  • @melpomene 非常好。 #define ENFORCE_ICEXPR(X) sizeof(struct { int f:!!(X); }) 工作出色。如果您想收集积分,我会接受它作为答案。 :)

标签: c


【解决方案1】:

Perl uses a bit-field instead of an array 定义一个static_assert 后备:

#define STATIC_ASSERT_2(COND, SUFFIX) \
    typedef struct { \
        unsigned int _static_assertion_failed_##SUFFIX : (COND) ? 1 : -1; \
    } _static_assertion_failed_##SUFFIX PERL_UNUSED_DECL
#define STATIC_ASSERT_1(COND, SUFFIX) STATIC_ASSERT_2(COND, SUFFIX)
#define STATIC_ASSERT_DECL(COND) STATIC_ASSERT_1(COND, __LINE__)

没有编译器实现可变长度位域。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-17
    相关资源
    最近更新 更多