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__总是定义明确的。
宏说明:
"",它仅包含一个空终止符,因此大小为1。注意:这个版本的答案是重大重写的结果。一些主张已被删除,另一些主张已被重大修改,以便集中精力并更好地证明最重要的观点。
[有争议的、备受争议的立场已被删除。这更让人分心,而不是有帮助。]
我想我已经找到了一个既紧凑又标准的简单解决方案:
Run Code Online (Sandbox Code Playgroud)#define is_empty(...) ( sizeof( (char[]){#__VA_ARGS__} ) == 1 )
我们可以通过考虑这种变化来回避任何不确定性的问题:
#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,产生无效的表达式。