抑制C宏变量替换

时间:2013-08-11 01:50:54

标签: c c-preprocessor substitution

我有这段代码(实际上是垃圾收集的Forth系统的解释器的一部分):

#define PRIMITIVE(name) \
    do \
    { \
        VocabEntry* entry = (VocabEntry*)gc_alloc(sizeof(VocabEntry)); \
        entry->code = name; \
        entry->name = cstr_to_pstr(#name); \
        entry->prev = latest_vocab_entry; \
        latest_vocab_entry = entry; \
    } \ 
    while (false)

PRIMITIVE(dup);
PRIMITIVE(drop);
PRIMITIVE(swap);
// and a lot more

但是有一个问题:在行

entry->name = cstr_to_pstr(#name);

name字段代替dupdropswap,其余字段。我希望字段名称不被替换。

那么,有没有办法解决这个问题,除了简单地重命名宏参数?

对于答案,请解释一般是否有一种方法来抑制宏体中宏参数名称的替换。不要回答“只是这样做”(请)。

3 个答案:

答案 0 :(得分:16)

您可以定义一个不同的宏来扩展为name,如下所示:

#define Name name

并更改name宏中的PRIMITIVE字段以使用新宏,如下所示:

#define PRIMITIVE(name) \
    do \
    { \
        VocabEntry* entry = (VocabEntry*)gc_alloc(sizeof(VocabEntry)); \
        entry->code = name; \
        entry->Name = cstr_to_pstr(#name); \
        entry->prev = latest_vocab_entry; \
        latest_vocab_entry = entry; \
    } \ 
    while (false)

除了使用与宏体中的参数名称不同的内容或更改参数名称之外,在C语言中没有其他方法可以执行此操作。 Per C 2011(N1570)6.10.3.1 1,当识别出类似函数的宏时,参数名称会立即被替换,除非存在###,并且没有其他例外:

  

在识别出类似函数的宏的调用参数之后,发生参数替换。替换列表中的参数,除非前面带有#或##预处理标记或后跟##预处理标记(见下文),在其中包含的所有宏都已扩展后,将被相应的参数替换。

#标记将参数名称更改为字符串,在这种情况下无效。 ##令牌扩展参数名称并将其与相邻令牌粘贴在一起,在这种情况下也没用。

答案 1 :(得分:5)

不,没有。

要了解原因,您需要考虑宏扩展实际发生的方式。扩展类似函数的宏需要三个主要步骤:

  1. 宏的参数完全展开,除非宏使用###运算符(因为它们是单个标记,因此在示例中不相关)
  2. 扫描整个替换列表,并且任何出现的参数名称都会被相应的参数替换
  3. 在第2步完成后,扩展的替换列表本身将被重新扫描,此时出现的任何宏都会被扩展
  4. 标准6.10.3节(C11和C99)概述了这一点。

    这样做的结果是,不可能编写某种可以采用name并滥用'##'或类似内容的抑制规则的宏,因为这是一个替换步骤。 PRIMITIVE 必须完全运行 ,然后才允许识别正文中的任何宏。您无法在替换列表中标记令牌以进行抑制,因为只有在替换步骤已经运行后才能检查您可以放置​​的标记。由于订单是在标准中指定的,因此您发现的任何可以让您以这种方式标记令牌的漏洞都是编译器错误。

    最好我可以建议,如果你真的没有重命名宏参数,那就是将name作为单独的参数传递给连接宏;只有在替换完成后才会形成令牌name,并且不再检查列表中的参数名称。

    编辑希望我输入更快。

答案 2 :(得分:1)

不,没有办法在宏的体内抑制与所述宏的声明参数相同的替换。跳入预处理器代码的每个可能的解决方案都需要您重命名某些内容,可以是参数名称或字段名称(可能只是出于该宏的目的,正如Eric的回答所做的那样)。