使用##和__LINE__创建C宏(使用定位宏进行标记连接)

时间:2009-10-20 20:11:43

标签: c macros concatenation token

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

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

我希望将其扩展为:

static void Unique_23(void) {}

这不起作用。使用令牌连接,定位宏 按字面意思对待,最后扩展到:

static void Unique___LINE__(void) {}

这可能吗?

(是的,无论这看起来多么无用,我都有理由这样做。)

2 个答案:

答案 0 :(得分:163)

问题在于,当您进行宏替换时,如果字符串化运算符#和令牌粘贴运算符##都未应用于预处理器,则预处理器将仅递归扩展宏。因此,您必须使用一些额外的间接层,您可以使用带有递归扩展参数的token-pasting运算符:

#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宏上进行多个实例化。同一行。注意:MS Visual Studio,GCC(自V4.3起)和Clang支持__COUNTER__,但不是标准C.

答案 1 :(得分:-1)

GCC并不要求"包装" (或实现)除非结果需要"字符串化"。 Gcc有功能,但ALL可以使用普通的C版本1(有些人认为Berkeley 4.3 C的速度要快得多,并且值得学习如何使用)。

** Clang(llvm)不做白色空间正确的宏扩展 - 它增加了空白(这肯定会破坏结果作为进一步预处理的C标识符)**,clang只是没有&# 39;作为C预处理器预计将进行#或*宏扩展几十年。最好的例子是编译X11,宏" Concat3"它被打破了,它的结果现在是MISNAMED C Identifier,当然无法构建。我开始构建失败的是他们的专业。

我认为这里的答案是"新的C打破标准是坏的C",这些黑客总是选择(clobber名称空间)他们无缘无故地更改默认值但不真正"改善C" #34; (除了他们自己的说法:我说这是为了解释为什么他们逃脱了所有破坏而没有人让他们负责的装置)。

早期的C预处理器不支持 UNIq _ ()__这不是问题,因为它们支持#pragma,它允许代码中的编译器品牌hackery成为被标记为hackery"在没有影响标准的情况下也可以正常工作:正如改变默认值是无用的馄饨破损,就像改变函数在使用相同名称(命名空间破坏)时的作用一样......在我看来是恶意软件