static_assert()
是自C11以来非常强大的功能。
但是对于C11之前的编译器,必须模拟此功能。 并不难,Internet上有很多示例。
例如:
#define STATIC_ASSERT(CONDITION, MSG) \
typedef char static_assert_##MSG[(CONDITION)?1:-1]
这使得可以在这种情况下传输错误消息,这很容易解释一旦触发就会出问题。
但是,此MSG
与C11
的{{1}}中的static_assert()
有很大不同:
这与C11
的{{1}}截然不同,以至于似乎无法创建一个宏,该宏将根据需要在static_assert()
和C11
版本之间透明地切换编译器。
为了接受“看起来像C90
”的错误消息(又称带双引号的字符串),我测试了一个新的宏:
C11
使用#define STATIC_ASSERT(CONDITION, MSG) \
typedef char static_assert[((void)(MSG), ((CONDITION)?1:-1))]
逗号运算符,此宏应接受,
作为字符串,而不必理会它。但这会在出现错误的情况下显示,这是有意的。
它在MSG
,but not of gcc
:clang
上正常工作。
我试图了解原因,以及是否可以解决
答案 0 :(得分:2)
如果用enum-trick替换typedef-array-trick,那么您会得到一些似乎对clang和gcc都适用的东西:
#define CONDITION 1
#define TOKENPASTE(a, b) a ## b // "##" is the "Token Pasting Operator"
#define TOKENPASTE2(a,b) TOKENPASTE(a, b) // expand then paste
#define static_assert(x, msg) enum { TOKENPASTE2(ASSERT_line_,__LINE__) \
= 1 / (msg && (x)) }
static_assert( CONDITION, "This should pass");
static_assert(!CONDITION, "This should fail");
这使我在foo.c的第9行上有了gcc,例如:
foo.c:9: warning: division by zero [-Wdiv-by-zero]
static_assert(!CONDITION, "This should fail");
^
foo.c:9: error: enumerator value for 'ASSERT_line_9' is not an integer constant
static_assert(!CONDITION, "This should fail");
^~~~~~~~~~~~~
(这里使用了gcc开关-ftrack-macro-expansion=0
,因为额外的错误消息没有帮助,只是增加了噪音。)
请注意,仍然需要对名称进行一些修饰,因此省略了。此处,文本ASSERT_line_
与变量__LINE__
组合在一起。这样可以确保唯一的名称,前提是:
ASSERT_line_9
之类的标识符。对于头文件,您将需要在某个位置添加仅包含标识符字符的单个单词。例如:
#define static_assert3(x, msg, file) enum { TOKENPASTE2(file,__LINE__) = \
1 / (msg && (x)) }
#define static_assert(x, msg) static_assert3(x, msg, my_header_h_)
如果在第17行上失败,则gcc将给出错误,例如:
error: enumerator value for 'my_header_h_17' is not an integer constant
处理头文件的另一种方法是将__LINE__
替换为__COUNTER__
。我没有使用过它,因为它是非标准的,并且因为使用clang的速度很慢。但是现在它已经在gcc,msvc和clang中使用了大约五年了。
您可以尝试使用typedef-array想法进行相同的修改,并用&&
替换逗号运算符。然后,您的gcc错误变为警告。例如,将您的Godbolt示例修改为:
typedef char static_assert_2["hello world!" && (CONDITION) ? 1 : -1];
将不需要的warning: variably modified 'static_assert_2' at file scope
用于gcc。
答案 1 :(得分:1)
单线
#define STATIC_ASSERT(CONDITION, MSG) { typedef char test[(CONDITION)?1:-1]; (void)(test*) #MSG; } (void)0