编译时断言作为表达式的一部分,但不包含_Static_assert

时间:2018-10-20 18:54:57

标签: c

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

使用真实的_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)的方法?

1 个答案:

答案 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__)

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