gcc

时间:2019-01-25 01:52:11

标签: c gcc static-assert

static_assert()是自C11以来非常强大的功能。

但是对于C11之前的编译器,必须模拟此功能。 并不难,Internet上有很多示例。

例如:

#define STATIC_ASSERT(CONDITION, MSG) \
typedef char static_assert_##MSG[(CONDITION)?1:-1]

这使得可以在这种情况下传输错误消息,这很容易解释一旦触发就会出问题。

但是,此MSGC11的{​​{1}}中的static_assert()有很大不同:

  • 必须是一个单词
  • 它只能使用标识符字符
  • 它不能是带双引号的字符串

这与C11的{​​{1}}截然不同,以至于似乎无法创建一个宏,该宏将根据需要在static_assert()C11版本之间透明地切换编译器。

为了接受“看起来像C90”的错误消息(又称带双引号的字符串),我测试了一个新的宏:

C11

使用#define STATIC_ASSERT(CONDITION, MSG) \ typedef char static_assert[((void)(MSG), ((CONDITION)?1:-1))] 逗号运算符,此宏应接受,作为字符串,而不必理会它。但这会在出现错误的情况下显示,这是有意的。

它在MSGbut not of gccclang上正常工作。

我试图了解原因,以及是否可以解决

2 个答案:

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