代码之家  ›  专栏  ›  技术社区  ›  ysap

如何在级联stringify级联中延迟宏替换

  •  2
  • ysap  · 技术社区  · 6 年前

    这是一个后续问题 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 ,有效创建令牌 99_ONE 99_TWO .

    该程序输出:

    FOO: 99_ONE99_TWO
    BAR: 5678
    

    如何推迟foo宏的扩展(有效地,完全消除它,以获得所需的输出:

    FOO: 1234
    BAR: 5678
    

    注:假设 PRINT() 不能更改宏签名(即不能添加参数等)。但是它的实现是可以改变的。也, , FOO_* BAR_* 定义也不能修改。

    3 回复  |  直到 6 年前
        1
  •  1
  •   Marian    6 年前

    FOO的实际扩展到99发生在打印扩展时,在再次扫描其主体之前。ANSI标准的相关部分是:

    6.10.3.1参数替换

    参数替换发生。替换列表中的参数,除非前面有 通过或预处理令牌或后跟预处理令牌(见下文),是 在其中包含的所有宏 扩大。在被替换之前,每个参数预处理标记是 完全替换的宏,就像它们构成了预处理文件的其余部分一样;没有其他宏 预处理标记可用。

    在您的情况下,您可以通过替换来避免foo(在mac内部)的扩展

    #define PRINT(mac) printf(#mac ": " MAKE_MAC(mac) "\n")
    

    具有

    #define PRINT(mac) printf(#mac ": " CONCAT(mac##_ONE, mac##_TWO) "\n")
    
        2
  •  2
  •   J...S    6 年前

    如果你每次都能做点什么 FOO 使用时,可以先取消宏的定义,使其按需展开,然后重新定义为

    #undef FOO
    PRINT(FOO);
    #define FOO 99
    

    在这种情况下,它将扩展到

    printf("FOO" ": " "12" "34" "\n");
    
    printf("BAR" ": " "56" "78" "\n");
    

    打印你想要的。

        3
  •  2
  •   H Walters    6 年前
    如何推迟foo宏的扩展…注意:假设print()宏签名不能更改(即不能添加参数等)

    你不能。

    宏扩展要经过一系列步骤:

    • 参数替换
    • 无特定顺序的粘贴和架线
    • 重新扫描并进一步更换

    参数替换发生在参数中;在调用的情况下 PRINT(FOO) , FOO 这是第一步。当您甚至让预处理器识别出替换列表中的某个东西是宏时,您已经很久没有进行参数替换了。

    参数替换的规则是,如果在替换列表中提到任何参数,并且这些参数既没有被字符串化也没有被粘贴,则会对相应的参数进行完全求值,并用结果替换那些提到的参数。在这种情况下, 打印(foo) ,在参数替换后,将生成替换列表:

    printf(#mac ": " MAKE_MAC(99) "\n")
    

    再一次, MAKE_MAC 的定义无关紧要;在重新扫描和进一步替换之前,它甚至不能被识别为宏。

    现在,没有你的限制,你可以推迟 从展开…通过将第二个参数添加到 PRINT 贴在上面(这样就取消了 a.s. ,当rescan和replacement出现时,它将调用下一个宏)。但是有了你的限制,你就是DOA。