最终,我想要一个编译时常量宏,它本身包含一个断言。
使用真实的_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)
的方法?
答案 0 :(得分: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__)
没有编译器实现可变长度的位域。