【发布时间】: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