【问题标题】:Suppress C Macro Variable Substitution禁止 C 宏变量替换
【发布时间】:2013-08-11 01:50:54
【问题描述】:

我有这段代码(实际上是垃圾收集 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 等。我希望字段名称不被替换。

那么,除了简单地重命名宏参数之外,还有什么办法可以解决这个问题?

如需回答,请说明在一般情况下是否有一种方法可以禁止在宏主体中替换宏参数名称。不要回答“就这样做”(请)。

【问题讨论】:

  • 更改结构字段名称? (必须在这结束之前重命名一些东西,一种或另一种方式)。
  • @JimBalter 我只提供 n 替代方案,因为 OP 对潜在解决方案施加了限制。我不是第一次看到这种情况,尽管我同意重命名该论点是正确,但肯定不完全如此。当然,我也同意 OP 否决的答案不是好举措。
  • 回到问题,既然预处理器正在做这一切,我并不羞于说我想不出办法“告诉”cpp它应该不 i> 替换它设计 所针对的事物的一个或多个实例。简而言之,可以更改宏参数的名称,也可以更改被替换为进攻的项目。注意:消除也是可能的,但高度不鼓励(即,您可以避开标准的边界,但仍留在其中,如果所讨论的结构字段是 第一个结构的成员。
  • @JimBalter 你真的希望我仅仅因为 认为你回答了我的问题而删除了我的反对票吗?你显然没有;否则我不会投反对票。
  • @WhozCraig 我对 Jim Balter 的回答投了反对票,因为它没有回答我的问题。没有其他原因。

标签: c c-preprocessor substitution


【解决方案1】:

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

#define Name name

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

#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 语言中没有其他方法可以做到这一点。根据 C 2011 (N1570) 6.10.3.1 1,当识别出类似函数的宏时,会立即替换参数名称,除非存在 ###,并且没有其他例外:

在确定了调用类函数宏的参数后,将进行参数替换。替换列表中的参数,除非前面有 # 或 ## 预处理标记或后跟 ## 预处理标记(见下文),否则在其中包含的所有宏都已展开后,将替换为相应的参数。

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

【讨论】:

  • @LovesProbability: Name 出现在 entry->Name = … 中。
  • 非常感谢!
【解决方案2】:

不,没有。

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

  1. 宏的参数已完全展开,除非宏对其使用 ### 运算符(在示例中不相关,因为它们是单个标记)
  2. 整个替换列表被扫描,任何出现的参数名称都被相应的参数替换
  3. 第 2 步完成后,扩展的替换列表本身会被重新扫描,此时出现的任何宏都会被扩展

标准第 6.10.3 节(C11 和 C99)对此进行了概述。

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

如果您真的决定不重命名宏参数,我可以建议的最好方法是将name 作为单独的参数传递给连接宏;标记 name 只会在替换完成后形成,并且不再检查列表中的参数名称。

编辑希望我打字更快。

【讨论】:

  • 为什么你的回答一开始就断言没有办法,然后给一个办法呢?
【解决方案3】:

不,没有办法禁止在宏的主体内替换与所述宏的声明参数相同的标记。除了跳转到预处理器代码之外,每个可能的解决方案都需要您重命名某些东西,无论是参数名称还是字段名称(可能只是为了该宏的目的,正如 Eric 的回答所做的那样)。

【讨论】:

    猜你喜欢
    • 2011-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-04
    • 2019-05-17
    相关资源
    最近更新 更多