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

时间:2019-03-29 12:10:41

标签: c c-preprocessor c11 variadic-macros

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

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

#define is_empty(...) ( sizeof( (char[]){#__VA_ARGS__} ) == 1 )

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

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

宏的说明:

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

2 个答案:

答案 0 :(得分:2)

注意:此版本的答案是重大重写的结果。一些主张已被删除,而其他主张则进行了重大修改,以便专注于并更好地证明最重要的观点。

可变参数宏及其可变参数

[有争议的位置被删除。分散注意力而不是有所帮助。]


建议的宏

  

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

#define is_empty(...) ( sizeof( (char[]){#__VA_ARGS__} ) == 1 )

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

#define is_empty(dummy, ...) ( sizeof( (char[]){#__VA_ARGS__} ) == 1 )

。同样的注意事项也适用于解释空的 vs。非空变量参​​数。具体来说,

  

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

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

  

宏的说明:

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

是的

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

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

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

是的

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

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

如何,可能仍然无法满足您的目的。特别是,您不能在条件编译指令中使用它。尽管sizeof表达式通常在整型常量表达式中是允许使用的,例如整数形式的指令的控制表达式,

  • 从字面上讲,sizeof作为预处理令牌被分类为标识符(预处理令牌的关键字和标识符之间没有区别),并且
  • 根据标准的paragraph 6.10.1/4,在处理条件编译指令的控制表达式时,

      

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

    (添加了重点)。

因此,如果将您的宏用作条件编译指令的控制表达式或在其中使用,则将其评估为好像其中的sizeof运算符被0替换,产生了无效的表达式

答案 1 :(得分:1)

我个人不喜欢混合宏/预处理器级别的评估和编译级别的测试。

似乎没有在宏级别执行此操作的标准方法,但是此处存在一些hacks: C++ preprocessor __VA_ARGS__ number of arguments