【问题标题】:Creating C macro with ## and __LINE__ (token concatenation with positioning macro)使用 ## 和 __LINE__ 创建 C 宏(与定位宏的标记连接)
【发布时间】:2010-12-08 11:52:24
【问题描述】:

我想创建一个 C 宏来创建一个基于名称的函数 行号上。 我想我可以做类似的事情(真正的函数会在大括号内声明):

#define UNIQUE static void Unique_##__LINE__(void) {}

我希望可以扩展为:

static void Unique_23(void) {}

这行不通。使用令牌连接,定位宏 被逐字处理,最终扩展为:

static void Unique___LINE__(void) {}

这样可以吗?

【问题讨论】:

标签: c macros concatenation token


【解决方案1】:

问题是当你有一个宏替换时,如果字符串化操作符#和标记粘贴操作符##都没有应用到它,预处理器只会递归地扩展宏。因此,您必须使用一些额外的间接层,您可以使用带有递归扩展参数的标记粘贴运算符:

#define TOKENPASTE(x, y) x ## y
#define TOKENPASTE2(x, y) TOKENPASTE(x, y)
#define UNIQUE static void TOKENPASTE2(Unique_, __LINE__)(void) {}

然后,__LINE__UNIQUE 的扩展过程中被扩展为行号(因为它不涉及###),然后令牌粘贴发生在TOKENPASTE 的扩展过程中.

还应注意,还有__COUNTER__ 宏,它在每次求值时扩展为一个新整数,以防您需要在同一行上有多个UNIQUE 宏的实例化。注意:__COUNTER__ 受 MS Visual Studio、GCC(自 V4.3 起)和 Clang 支持,但不是标准 C。

【讨论】:

  • 恐怕这不适用于 GNU cpp。 TOKENPASTE 使用 LINE 作为文字。 TOKENPASTE(Unique_, LINE) 扩展为 Unique___LINE__
  • @DD:哦,现在修复了。它需要 2 层间接,而不是 1 层。
  • __COUNTER__ 宏在 gcc 中对我不起作用;尽管__LINE__ 确实像宣传的那样工作。
  • 对于尝试 COUNTER 的任何人的一些额外信息,根据msdn.microsoft.com/en-us/library/b0084kay(v=vs.80).aspx,这是一个特定于 Microsoft 的宏。
  • 任何解释为什么你需要2级间接?我只尝试了一个,没有#和##,并且没有在VS2017上扩展它。显然,GCC 也是如此。但是,如果您添加第二级间接,那么它确实会扩展。魔法?
【解决方案2】:

GCC 不需要“包装”(或实现),除非结果需要“字符串化”。 Gcc 有一些功能,但所有功能都可以使用纯 C 版本 1 完成(有些人认为 Berkeley 4.3 C 速度快得多,值得学习如何使用)。

**Clang (llvm) 没有正确地为宏扩展做空白 - 它添加了空白(这肯定会破坏结果作为进一步预处理的 C 标识符)**,clang 根本不做 # 或* 作为 C 预处理器的宏扩展预计将持续数十年。主要的例子是编译 X11,宏“Concat3”被破坏,它的结果现在是 MISNAMED C Identifier,当然无法构建。我开始认为构建失败是他们的职业。

我认为这里的答案是“打破标准的新 C 是糟糕的 C”,这些黑客总是选择(破坏命名空间)他们无缘无故地更改默认值,但并没有真正“改进 C”(除非他们自己这么说:我说这是一个装置,用来解释为什么他们逃脱了没有人让他们负责的所有破损)。


早期的 C 预处理器不支持 UNIq_()__ 这不是问题,因为它们支持 #pragma,它允许“将代码中的编译器品牌hackery 标记为hackery”并且还可以运行在不影响标准的情况下也一样:就像更改默认值是无用的馄饨破坏一样,就像在使用相同名称(命名空间破坏)时更改函数的功能一样......在我看来是恶意软件

【讨论】:

    猜你喜欢
    • 2013-10-21
    • 2020-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多