好吧,我很惊讶没有提到该语法的替代方法。另一种常见的(但较旧的)机制是调用未定义的函数,并且在断言正确的情况下依靠优化器编译出该函数调用。
#define MY_COMPILETIME_ASSERT(test) \
do { \
extern void you_did_something_bad(void); \
if (!(test)) \
you_did_something_bad(void); \
} while (0)
虽然此机制有效(只要启用了优化),但它的缺点是直到您链接后才报告错误,这时它将找不到函数you_did_something_bad()的定义。这就是内核开发人员开始使用诸如负数大小的位域宽度和负数大小的数组之类的技巧的原因(后者后来停止了破坏GCC 4.4中的构建)。
出于对编译时断言的需求的同情,GCC 4.3引入了error
function属性,该属性允许您扩展这个较旧的概念,但会生成编译时错误,并带有您选择的消息-不再使用神秘的“负大小”数组错误消息!
#define MAKE_SURE_THIS_IS_FIVE(number) \
do { \
extern void this_isnt_five(void) __attribute__((error( \
"I asked for five and you gave me " #number))); \
if ((number) != 5) \
this_isnt_five(); \
} while (0)
实际上,从Linux 3.9开始,我们现在有一个名为的宏compiletime_assert
,它使用此功能,并且其中的大多数宏bug.h
都已相应更新。尽管如此,该宏仍不能用作初始化程序。但是,使用by 语句表达式(另一个GCC C扩展名),您可以!
#define ANY_NUMBER_BUT_FIVE(number) \
({ \
typeof(number) n = (number); \
extern void this_number_is_five(void) __attribute__(( \
error("I told you not to give me a five!"))); \
if (n == 5) \
this_number_is_five(); \
n; \
})
该宏将仅对它的参数进行一次评估(以防它产生副作用),并产生一个编译时错误,提示“我告诉过你不要给我五个!” 如果表达式的计算结果为5或不是编译时常量。
那么,为什么不使用它而不是负大小的位域呢?遗憾的是,当前使用语句表达式有很多限制,包括将它们用作常量初始化程序(用于枚举常量,位域宽度等),即使语句表达式本身是完全恒定的(即可以完全求值)在编译时,否则通过__builtin_constant_p()
测试)。此外,它们不能在功能主体之外使用。
希望GCC会尽快修正这些缺点,并允许将常量语句表达式用作常量初始化程序。这里的挑战是定义什么是合法常量表达式的语言规范。C ++ 11仅为此类型或事物添加了constexpr关键字,但C11中没有对应的关键字。尽管C11确实获得了静态断言,这将解决部分问题,但它无法解决所有这些缺点。因此,我希望gcc可以通过-std = gnuc99和-std = gnuc11或类似的方式使constexpr功能作为扩展使用,并允许其在语句表达式等上使用。等