【问题标题】:How does expansion of the macro parameters work in c++宏参数的扩展如何在c ++中工作
【发布时间】:2019-08-13 10:52:13
【问题描述】:

我刚刚注意到一个关于 C++ 中宏参数扩展的有趣的事情。

我定义了 4 个宏;其中 2 个将给定参数转换为字符串,另外 2 个尝试分隔 2 个参数。我用扩展为 , 的宏向它们传递了参数,得到了以下结果:

#define Quote(x) #x
#define String(x) Quote(x)
#define SeparateImpl(first, second) first + second
#define Separate(pair) SeparateImpl(pair)
#define comma ,

int main(){
Quote(1 comma 2);  // -> "1 comma 2"
String(1 comma 2); // -> "1 , 2"
SeparateImpl(1 comma 2); // -> 1 , 2 + *empty arg*
Separate(1 comma 2);     // -> 1 , 2 + *empty arg*
return 0;
}

所以,我们看到宏字符串变成了"1 , 2",这意味着宏逗号首先被解包。但是,宏分离变成了1 , 2 + **empty arg**,这意味着宏comma没有被先解包,我想知道为什么?我在 VS2019 中试过这个。

【问题讨论】:

  • 幸运的是,实际上没有人需要编写真正的 C 或 C++ 代码,所以这不是很重要。
  • AFAIK,宏是从上到下处理的,但这可能会重复。因此,宏可以扩展为其他宏,然后扩展为其他宏。 ("Blue painting" 可能与这种情况有关。)我做了google "c preprocessor blue painting" 并找到了一些有趣的链接(例如Is the C preprocessor Turing complete?),尽管我没有找到(或看到)我希望的最终解释。
  • ... 但是,除此之外,我想强调@SamVarshavchik 的建议:C++ 中的宏是危险的。虽然可能会很好地添加到 C 中,但在 C++ 中,它们可能会引入可怕的影响,因为它们与名称空间完全无关,并且可能会在作者不期望的地方更改代码。 (这件事已经发生在我身上,在我明白这一点之前,它让我有些头疼......)
  • 不知道 (a) 标准怎么说,但 CLANG 的行为不同。 String(1 comma 2); 给出了“too many arguments”(因为它调用了Quote())和SeparateImpl(1 comma 2); 给出了“too few 参数”。调用Separate(1 comma 2);确实扩展为SeparateImpl() 的两个参数调用。看起来 VS2019 总是将单个参数视为单个参数(即使它包含逗号)。

标签: c++ c-preprocessor


【解决方案1】:
#define Quote(x) #x
#define String(x) Quote(x)
#define SeparateImpl(first, second) first + second
#define Separate(pair) SeparateImpl(pair)
#define comma ,

宏调用如下:

  • 参数替换 (a.s),如果在替换列表中提到了一个参数,并且该参数不参与粘贴或字符串化,则它会被完全扩展,并且替换列表中提到的参数被替换为结果.
  • 字符串化
  • 粘贴
  • 重新扫描和进一步替换 (r.a.f.r.),其中重新扫描生成的替换列表,在此期间宏的名称被标记为无效扩展(“涂成蓝色”)。

每个案例的扩展方式如下:

Quote(1 comma 2)

a.s.无操作(仅提及参数是字符串化)。字符串化适用。结果:"1 comma 2"

String(1 comma 2)

a.s.适用;产生Quote(1 , 2)。在 r.a.f.r. 期间,Quote 被识别为宏,但参数计数不匹配。这是无效的。但见下文。

SeparateImpl(1 comma 2)

无效的宏调用。使用一个参数调用宏,但它应该有 2 个参数。请注意,将 comma 定义为宏是无关紧要的;在宏调用级别,您只是在查看令牌。

Separate(1 comma 2)

a.s.适用;产生SeparateImpl(1 , 2)。在 r.a.f.r. 期间,SeparateImpl 被调用...该调用的 a.s.应用,产生1 + 2

我在 VS2019 中试过这个。

我一眼就能看出它是 2020 年之前的 VS,那里的墙壁告诉我他们最终将致力于预处理器的合规性。特别是 VS 似乎有这种奇怪的状态,其中带有逗号的标记仍然被视为单个参数(就好像参数识别发生在扩展之前但继续应用或其他东西);所以在这种情况下,1 , 2 在你的String(1 comma 2) 电话中会很奇怪;即,Quote1 , 2 调用,但在这种情况下,它实际上是一个参数。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-04-02
    • 2019-11-07
    • 2012-01-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多