116

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

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

我希望将其扩展到以下内容:

static void Unique_23(void) {}

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

static void Unique___LINE__(void) {}

这可能吗?

4

2 回答 2

194

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

#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。

于 2009-10-20T20:33:26.013 回答
-3

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”,并且在不影响标准的情况下也能正常工作:就像改变一样默认值是无用的馄饨破损,就像在使用相同名称(命名空间破坏)时更改函数的功能一样......在我看来是恶意软件

于 2017-03-15T01:43:32.293 回答