如何在concatenate-stringify级联中推迟宏替换

时间:2018-05-30 01:56:09

标签: c macros c-preprocessor evaluation

这是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()FOOFOO_*定义也无法修改。

3 个答案:

答案 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")