我最近看到了一些像这样的代码:
#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文件
答案 0 :(得分:1)
##
tokenize运算符不会对其参数进行求值(宏扩展)。类似于函数的宏扩展执行评估参数,这就是为什么你得到第一种情况的预期(评估)输出。
从技术上讲,宏JOIN_
是不必要的,因为在展开lhs
时会评估rhs
中的JOIN
和JOIN__
。这就足够了:
#define JOIN(lhs, rhs) JOIN__(lhs, rhs)
#define JOIN__(lhs, rhs) lhs##rhs
答案 1 :(得分:1)
修改强>
3.9.6论据Prescan
宏参数在它们出现之前完全是宏扩展的 替换为宏体,除非它们被字符串化或粘贴 与其他令牌。替换后,整个宏体, 包括替换的参数,再次扫描宏 扩大。结果是参数被扫描两次以进行扩展 宏调用它们。
调用其他字符串化或连接的宏的宏。如果 参数是字符串化或连接的,预扫描不会发生。 如果要扩展宏,则将其字符串化或连接 扩展,你可以通过使一个宏调用另一个宏来做到这一点 进行字符串化或连接。
例如,如果你有
#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。预装可以完全扩展。)
<强> CASE1 强>
#define JOIN__(lhs, rhs) lhs##rhs
=&gt;因为它有一个令牌粘贴操作符,它将连接那些在替换之前不完全宏扩展的宏参数。 - &GT;这是一个不好的扩展方式,首先我们不知道,它将传递给它的参数是什么,它不会等待它的扩展,它只会连接它。
因此,当你调用JOIN__(Foo, JOIN(A,B));
时,它将不允许JOIN(A,B)
扩展,它会将它连接到FOOJOIN(A,B)。
<强> CASE2 强>
另一方面,现在,
#define JOIN_(lhs, rhs) JOIN__(lhs, rhs)
=&gt;这里没有令牌粘贴操作符,宏参数在被替换为宏体之前完全是宏扩展的。因此,它将允许lhs和rhs扩展并用扩展参数JOIN__(FOO,AB)
调用,因此现在,JOIN__有一个标记粘贴操作符,它将简单地连接它的参数FOO和AB,即FOOAB。这是适当的方式。
<强> CASE3 强>
#define JOIN(lhs, rhs) JOIN_(lhs, rhs)
=&gt;与CASE2相同。
希望,它解释了多层次扩张范式背后的原因。
<强> ORIGINAL 强> 预处理程序运算符##提供了一种在宏扩展期间连接实际参数的方法。如果替换文本中的参数与##相邻,则参数将替换为实际参数,##和周围的空白区域将被删除,并重新扫描结果。例如,宏粘贴连接其两个参数:
#define paste(front, back) front ## back
so paste(name, 1) creates the token name1.