【问题标题】:Understanding macro expansion [duplicate]了解宏扩展[重复]
【发布时间】:2015-09-26 19:49:36
【问题描述】:

我最近看到一些这样的代码:

#define JOIN(lhs, rhs)   JOIN_(lhs, rhs)
#define JOIN_(lhs, rhs)  JOIN__(lhs, rhs)
#define JOIN__(lhs, rhs) lhs##rhs

我测试了代码,调用如下:

JOIN(Foo, 0);
JOIN_(Foo, 1);
JOIN__(Foo, 2);

JOIN(Foo, JOIN(A,B));
JOIN_(Foo, JOIN(A,B));
JOIN__(Foo, JOIN(A,B));

宏扩展为以下符号:

Foo0
Foo1
Foo2
FooAB
FooAB
FooJOIN

我明白了目的,它以不同的方式解决争论。在最后一种情况下,调用JOIN 的任何变体显然不一样。但是这些宏是如何扩展的呢?为什么参数的行为不同?

编辑:Here's 文件

【问题讨论】:

  • 我认为,最后一个宏 JOIN__(Foo, JOIN(A,B));' should be expanded as FooJOIN(A,B)` 而不仅仅是 FooJOIN,请澄清。
  • 有可能。我得到warning: implicit declaration of function 'FooJOIN' is invalid in C99,所以我真的看不出它得到了哪些论据。但是你说的有道理

标签: c macros


【解决方案1】:

编辑

3.9.6 参数预扫描

宏参数在宏扩展之前是完全宏扩展的 替换到宏体中,除非它们被字符串化或粘贴 与其他代币。替换后,整个宏体, 包括替换的参数,再次扫描宏 扩大。结果是参数被扫描两次以展开 宏调用它们。

调用字符串化或连接的其他宏的宏。如果 如果参数被字符串化或连接,则不会发生预扫描。 如果要扩展宏,则将其字符串化或连接 扩展,您可以通过使一个宏调用另一个宏来做到这一点 进行字符串化或连接。

例如,如果你有

#define AFTERX(x) X_ ## x
#define XAFTERX(x) AFTERX(x)
#define TABLESIZE 1024
#define BUFSIZE TABLESIZE

然后 AFTERX(BUFSIZE) 扩展为 X_BUFSIZE,XAFTERX(BUFSIZE) 扩展为 X_1024。 (不是 X_TABLESIZE。预扫描总是做一个完整的扩展。)

案例1

#define JOIN__(lhs, rhs) lhs##rhs => 因为它有一个标记粘贴操作符,它会在替换之前连接那些不是完全宏扩展的宏参数。 --> 这是一种不好的扩展方式,首先我们不知道它将传递给它的参数是什么,它不会等待它的扩展,它会简单地连接它。

因此,当您调用 JOIN__(Foo, JOIN(A,B)); 时,它不会允许 JOIN(A,B) 扩展,它会将其连接到 FOOJOIN(A,B)。

案例2 现在,另一方面, #define JOIN_(lhs, rhs) JOIN__(lhs, rhs) => 这里没有标记粘贴操作符,宏参数在被替换成宏体之前被完全宏扩展。因此,它将允许 lhs 和 rhs 扩展并使用扩展参数JOIN__(FOO,AB) 调用,因此现在,JOIN__ 有一个标记粘贴运算符,它将简单地连接它的参数 FOO 和 AB,即 FOOAB。这是正确的做法。

案例3 #define JOIN(lhs, rhs) JOIN_(lhs, rhs) => 与 CASE2 相同。

希望,它解释了多级扩展范式背后的原因。

原创 预处理器运算符## 提供了一种在宏扩展期间连接实际参数的方法。如果替换文本中的参数与## 相邻,则将参数替换为实际参数,删除## 和周围的空白,并重新扫描结果。例如,宏粘贴连接它的两个参数:

#define  paste(front, back)  front ## back

so paste(name, 1) creates the token  name1.

【讨论】:

  • 我知道令牌粘贴运算符。我问的是这种多级扩展范式,而不是串联
  • @AndréFratelli:请找到multi-level expansion paradigm 的更新答案。希望它能解释你在寻找什么。
  • 很棒的答案,非常彻底。
【解决方案2】:

## tokenize 运算符不评估(宏扩展)其参数。然而,类似函数的宏扩展确实评估参数,这就是为什么您会在第一种情况下获得预期(评估)输出。

从技术上讲,宏 JOIN_ 是不必要的,因为在扩展 JOIN__ 时会评估 JOIN 中的 lhsrhs。这样就足够了:

#define JOIN(lhs, rhs)   JOIN__(lhs, rhs)
#define JOIN__(lhs, rhs) lhs##rhs

【讨论】:

  • 老实说我找不到你错的情况,但为什么代码会这样写?
  • 请看我的编辑。我包含了原始代码
  • @AndréFratelli - 谁知道?人们会做奇怪的事情......但我看不出有任何理由让代码这样写。
  • 我想...我会再给它一些时间再接受,也许我们错过了什么...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-31
  • 2013-01-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多