这是检查可变参数宏参数列表是否为空的有效方法吗?

Lun*_*din 10 c c-preprocessor variadic-macros c11

我一直在寻找一种方法来检查可变参数宏参数列表是否为空。我发现所有解决方案似乎都非常复杂,或者使用了非标准扩展。

我想我找到了既紧凑又标准的简单解决方案:

#define is_empty(...) ( sizeof( (char[]){#__VA_ARGS__} ) == 1 )
Run Code Online (Sandbox Code Playgroud)

问:在任何情况下我的解决方案都会失败或调用定义不明确的行为吗?

基于C17 6.10.3.2/2(#运算符):“对应于一个空参数的字符串文字是"",我相信它#__VA_ARGS__总是定义明确的。

宏说明:

  • 这将创建一个复合文字char数组,并使用字符串文字对其进行初始化。
  • 无论将什么传递给宏,所有参数都将转换为一个长字符串文字。
  • 如果宏列表为空,则字符串文字将变为"",它仅包含一个空终止符,因此大小为1。
  • 在所有其他情况下,其大小将大于1。

Joh*_*ger 7

注意:这个版本的答案是重大重写的结果。一些主张已被删除,另一些主张已被重大修改,以便集中精力并更好地证明最重要的观点。

可变参数宏及其可变参数

[有争议的、备受争议的立场已被删除。这更让人分心,而不是有帮助。]


拟议的宏

我想我已经找到了一个既紧凑又标准的简单解决方案:

#define is_empty(...) ( sizeof( (char[]){#__VA_ARGS__} ) == 1 )
Run Code Online (Sandbox Code Playgroud)

我们可以通过考虑这种变化来回避任何不确定性的问题:

#define is_empty(dummy, ...) ( sizeof( (char[]){#__VA_ARGS__} ) == 1 )
Run Code Online (Sandbox Code Playgroud)

。与原始版本中一样,同样的注意事项也适用于此处对空非空变量参​​数的解释。具体来说,

基于 C17 6.10.3.2/2(# 运算符):“与空参数对应的字符串文字是“” ”,我相信它 #__VA_ARGS__总是定义良好的。

我同意。与此相关的还有第 6.10.3.1/2 节:“替换列表中出现的标识符__VA_ARGS__应被视为参数 [...]”。

宏的解释:

  • 这将创建一个复合文字 char 数组并使用字符串文字对其进行初始化。

是的。

  • 无论传递给宏的是什么,所有参数都将被转换为一个长字符串文字。

是的。 __VA_ARGS__被视为一个(一个)参数。如果有多个变量参数,则可能会影响重新扫描,但字符串化运算符在重新扫描之前的宏扩展点起作用。

  • 如果宏列表为空,则字符串文字将变为“”,它仅由空终止符组成,因此大小为 1。

是的。

  • 在所有其他情况下,它的大小都大于 1。

是的。即使在变量参数列表 中存在两个零标记参数的情况下,这也成立,is_empty(dummy,,)其中#__VA_ARGS__将扩展为","。它也适用于由空字符串文字 组成的参数的情况,is_empty(dummy, "")其中#__VA_ARGS__将扩展为"\"\""

但是,这仍然可能无法达到您的目的。特别是,您不能在条件编译指令中使用它。虽然整数常量表达式中通常sizeof允许表达式,例如形成此类指令的控制表达式,

  • 从词法上来说,作为预处理标记,sizeof被归类为标识符(预处理标记的关键字和标识符之间没有区别),并且
  • 根据标准第6.10.1/4段,在处理条件编译指令的控制表达式时,

    在执行了由于宏扩展和定义的一元运算符而进行的所有替换后,所有剩余的标识符(包括词法上与关键字相同的标识符)都将替换为 pp-number 0

    (强调已添加)。

因此,如果您的宏用作或在条件编译指令的控制表达式中,那么它将被评估,就像sizeof其中的运算符被替换一样0,产生无效的表达式。