这是this one的后续问题(也更接近实际问题)。
如果我有以下情况:
#include <stdio.h>
#define FOO_ONE 12
#define FOO_TWO 34
#define BAR_ONE 56
#define BAR_TWO 78
#define FOO 99
#define STRINGIFY(mac) #mac
#define CONCAT(mac1, mac2) STRINGIFY(mac1) STRINGIFY(mac2)
#define MAKE_MAC(mac) CONCAT(mac##_ONE, mac##_TWO)
#define PRINT(mac) printf(#mac ": " MAKE_MAC(mac) "\n")
void main(int argc, char *argv[])
{
PRINT(FOO);
PRINT(BAR);
}
可以看出,字符串化的连接宏然后在printf()
语句中被替换,该语句本身位于宏内。
由于FOO
已定义(99
),因此在与_ONE
和_TWO
进行连接之前会进行扩展,从而有效地创建了令牌{{1} }和99_ONE
。
该程序输出:
99_TWO
我如何推迟FOO宏的扩展(有效地,完全消除它,以获得所需的输出:
FOO: 99_ONE99_TWO
BAR: 5678
注意:假设FOO: 1234
BAR: 5678
宏签名无法更改(即无法添加参数等)。但是它的实现可以改变。此外,PRINT()
,FOO
和FOO_*
定义也无法修改。
答案 0 :(得分:2)
如果您每次使用FOO
时都可以执行某些操作,则可以先取消定义宏,以便根据需要进行扩展,然后重新定义,如
#undef FOO
PRINT(FOO);
#define FOO 99
在这种情况下,它将扩展为
printf("FOO" ": " "12" "34" "\n");
printf("BAR" ": " "56" "78" "\n");
打印你想要的东西。
答案 1 :(得分:2)
如何推迟FOO宏的扩展...注意:假设PRINT()宏签名无法更改(即无法添加参数等)
你不能。
宏扩展经历了一系列步骤:
参数替换与您的参数一起发生;如果是调用PRINT(FOO)
,FOO
;这是第一步。当你甚至让预处理器认识到你的替换列表中的某些东西是一个宏时,你很久就会超越参数。
参数替换的规则是,如果您的替换列表中提到了任何参数,并且这些参数既未被字符串化也未被粘贴,则相应的参数将被完全评估,并且参数的提及将替换为结果。在这种情况下,PRINT(FOO)
在参数替换后产生替换列表:
printf(#mac ": " MAKE_MAC(99) "\n")
同样,MAKE_MAC
的定义并不重要;在重新扫描和进一步更换之前,它甚至不被认为是宏。
现在,在没有您限制的情况下,您可以通过向FOO
添加第二个参数并将其粘贴到PRINT
以及通过a.s.
取消资格来推迟{{1}}扩展...时间重新扫描和更换随之而来的是调用你的下一个宏。但是有了你的限制,你就是DOA。
答案 2 :(得分:1)
FOO到99的实际扩展发生在PRINT扩展时以及再次扫描其主体之前。 ANSI标准的相关部分是:
6.10.3.1参数替换
1在确定了调用类函数宏的参数之后, 参数替换发生。替换列表中的参数,除非在前面 通过#或##预处理令牌或后跟##预处理令牌(见下文),是 在其中包含的所有宏之后用相应的参数替换 扩大。在被替换之前,每个参数的预处理标记都是 完全宏被替换,好像它们形成了预处理文件的其余部分;没有其他 预处理令牌可用。
在您的情况下,您可以通过替换
来避免扩展FOO(在mac内部)#define PRINT(mac) printf(#mac ": " MAKE_MAC(mac) "\n")
与
#define PRINT(mac) printf(#mac ": " CONCAT(mac##_ONE, mac##_TWO) "\n")