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

问题描述 投票:10回答:2

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

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

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

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

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

宏的解释:

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

注意:此答案的此版本是重写的结果。一些声明已被删除,其他声明已被大幅修改,以便集中注意并更好地证明最重要的观点。

变体宏及其变量参数

[有争议的,有争议的立场被删除。这更令人分心而不是有用。]


拟议的宏

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

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

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

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

。同样的注意事项适用于空白与非空变量参​​数的解释,就像在原始版本中一样。特别,

基于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被归类为标识符(预处理令牌的关键字和标识符之间没有区别),以及
  • 根据标准的paragraph 6.10.1/4,在处理条件编译指令的控制表达式时, 在由于宏扩展和已定义的一元运算符执行的所有替换之后,所有剩余的标识符(包括与关键字词法相同的标识符)将替换为pp-number 0 (重点补充)。

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


1
投票

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

似乎没有标准的方法在宏观层面上做到这一点,但这里存在黑客攻击:C++ preprocessor __VA_ARGS__ number of arguments

© www.soinside.com 2019 - 2024. All rights reserved.