这是检查可变宏参数列表是否为空的有效方法吗?
Is this a valid way of checking if a variadic macro argument list is empty?
我一直在寻找一种方法来检查可变宏参数列表是否为空。我发现的所有解决方案似乎都非常复杂或使用非标准扩展。
我想我找到了一个既紧凑又标准的简单解决方案:
#define is_empty(...) ( sizeof( (char[]){#__VA_ARGS__} ) == 1 )
问:在某些情况下我的解决方案会失败或调用定义不明确的行为吗?
基于 C17 6.10.3.2/2(# 运算符):"The character string literal corresponding to an empty argument is ""
",我相信 #__VA_ARGS__
始终是明确定义的。
宏的解释:
- 这将创建一个复合文字 char 数组并使用字符串文字对其进行初始化。
- 无论传递给宏的是什么,所有参数都将被翻译成一个长字符串文字。
- 如果宏列表为空,字符串文字将变为
""
,它只包含一个空终止符,因此大小为 1。
- 在所有其他情况下,它的大小将大于 1。
就我个人而言,我不喜欢混合使用 macro/preprocessor-level 评估和编译级测试。
在宏观层面似乎没有标准的方法来做到这一点,但这里存在黑客:
C++ preprocessor __VA_ARGS__ number of arguments
注意:此答案的这个版本是重大重写的结果。一些声明已被删除,而另一些则进行了重大修改,以便专注于并更好地证明最重要的观点。
可变参数宏及其可变参数
[有争议的,有争议的立场已删除。它比帮助更让人分心。]
提议的宏
I think I've found an easy solution that is both compact and standard:
#define is_empty(...) ( sizeof( (char[]){#__VA_ARGS__} ) == 1 )
我们可以通过考虑这种变化来回避任何不确定性问题:
#define is_empty(dummy, ...) ( sizeof( (char[]){#__VA_ARGS__} ) == 1 )
。同样的考虑适用于空 与 非空变量参数的解释,就像在你的原始版本中一样。具体来说,
Based on C17 6.10.3.2/2 (the # operator): "The character string
literal corresponding to an empty argument is """, I believe that
#__VA_ARGS__
is always well-defined.
我同意。此处也相关的是第 6.10.3.1/2 节:"An identifier __VA_ARGS__
that occurs in the replacement list shall be treated as if it were a parameter [...]."
Explanation of the macro:
- This creates a compound literal char array and initializes it by using a string literal.
是的。
- No matter what is passed to the macro, all arguments will be translated to one long string literal.
是的。 __VA_ARGS__
被视为 a (一个)参数。如果有多个可变参数,则可能会影响重新扫描,但字符串化运算符会在重新扫描之前的宏扩展点产生影响。
- In case the macro list is empty, the string literal will become "", which consists only of a null terminator and therefore has size 1.
是的。
- In all other cases, it will have a size greater than 1.
是的。即使在变量参数列表 is_empty(dummy,,)
中有两个零标记参数的情况下也是如此,其中 #__VA_ARGS__
将扩展为 ","
。它也适用于由空字符串文字 is_empty(dummy, "")
组成的参数,其中 #__VA_ARGS__
将扩展为 "\"\""
.
然而,这可能仍然无法满足您的目的。特别是,您不能在条件编译指令中使用它。尽管 sizeof
表达式 通常 允许出现在整型常量表达式中,例如形成此类指令的控制表达式,
- 词法上,作为预处理记号,
sizeof
归类为标识符(预处理记号不区分关键字和标识符),
根据标准paragraph 6.10.1/4,在处理条件编译指令的控制表达式时,
After all replacements due to macro expansion and the defined unary operator have been performed, all remaining identifiers (including those lexically identical to keywords) are replaced with the pp-number 0
(强调已添加)。
因此,如果您的宏被用作或用于条件编译指令的控制表达式,那么它将被计算为好像其中的 sizeof
运算符被 0
替换,产生无效的表达式。
我一直在寻找一种方法来检查可变宏参数列表是否为空。我发现的所有解决方案似乎都非常复杂或使用非标准扩展。
我想我找到了一个既紧凑又标准的简单解决方案:
#define is_empty(...) ( sizeof( (char[]){#__VA_ARGS__} ) == 1 )
问:在某些情况下我的解决方案会失败或调用定义不明确的行为吗?
基于 C17 6.10.3.2/2(# 运算符):"The character string literal corresponding to an empty argument is ""
",我相信 #__VA_ARGS__
始终是明确定义的。
宏的解释:
- 这将创建一个复合文字 char 数组并使用字符串文字对其进行初始化。
- 无论传递给宏的是什么,所有参数都将被翻译成一个长字符串文字。
- 如果宏列表为空,字符串文字将变为
""
,它只包含一个空终止符,因此大小为 1。 - 在所有其他情况下,它的大小将大于 1。
就我个人而言,我不喜欢混合使用 macro/preprocessor-level 评估和编译级测试。
在宏观层面似乎没有标准的方法来做到这一点,但这里存在黑客: C++ preprocessor __VA_ARGS__ number of arguments
注意:此答案的这个版本是重大重写的结果。一些声明已被删除,而另一些则进行了重大修改,以便专注于并更好地证明最重要的观点。
可变参数宏及其可变参数
[有争议的,有争议的立场已删除。它比帮助更让人分心。]
提议的宏
I think I've found an easy solution that is both compact and standard:
#define is_empty(...) ( sizeof( (char[]){#__VA_ARGS__} ) == 1 )
我们可以通过考虑这种变化来回避任何不确定性问题:
#define is_empty(dummy, ...) ( sizeof( (char[]){#__VA_ARGS__} ) == 1 )
。同样的考虑适用于空 与 非空变量参数的解释,就像在你的原始版本中一样。具体来说,
Based on C17 6.10.3.2/2 (the # operator): "The character string literal corresponding to an empty argument is """, I believe that
#__VA_ARGS__
is always well-defined.
我同意。此处也相关的是第 6.10.3.1/2 节:"An identifier __VA_ARGS__
that occurs in the replacement list shall be treated as if it were a parameter [...]."
Explanation of the macro:
- This creates a compound literal char array and initializes it by using a string literal.
是的。
- No matter what is passed to the macro, all arguments will be translated to one long string literal.
是的。 __VA_ARGS__
被视为 a (一个)参数。如果有多个可变参数,则可能会影响重新扫描,但字符串化运算符会在重新扫描之前的宏扩展点产生影响。
- In case the macro list is empty, the string literal will become "", which consists only of a null terminator and therefore has size 1.
是的。
- In all other cases, it will have a size greater than 1.
是的。即使在变量参数列表 is_empty(dummy,,)
中有两个零标记参数的情况下也是如此,其中 #__VA_ARGS__
将扩展为 ","
。它也适用于由空字符串文字 is_empty(dummy, "")
组成的参数,其中 #__VA_ARGS__
将扩展为 "\"\""
.
然而,这可能仍然无法满足您的目的。特别是,您不能在条件编译指令中使用它。尽管 sizeof
表达式 通常 允许出现在整型常量表达式中,例如形成此类指令的控制表达式,
- 词法上,作为预处理记号,
sizeof
归类为标识符(预处理记号不区分关键字和标识符), 根据标准paragraph 6.10.1/4,在处理条件编译指令的控制表达式时,
After all replacements due to macro expansion and the defined unary operator have been performed, all remaining identifiers (including those lexically identical to keywords) are replaced with the pp-number 0
(强调已添加)。
因此,如果您的宏被用作或用于条件编译指令的控制表达式,那么它将被计算为好像其中的 sizeof
运算符被 0
替换,产生无效的表达式。