使用##和__LINE__创建C宏(与定位宏的令牌串联)


107

我想创建一个C宏,该宏创建一个具有基于行号的名称的函数。我以为我可以做类似的事情(真正的功能在花括号内有语句):

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

我希望可以扩展为:

static void Unique_23(void) {}

那不行 使用令牌连接时,将按实际方式处理定位宏,最终扩展为:

static void Unique___LINE__(void) {}

这可能吗?

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


我认为您可以将其与间接宏扩展一起使用
本·斯蒂格利茨

4
如何与C预处理程序连接两次并扩展宏(如“ arg ## _ ## MACRO”中一样)的可能重复项这同样适用于除了任何宏__LINE__(尽管这是一个常见的情况。
西罗桑蒂利郝海东冠状病六四事件法轮功

Answers:


176

问题是,当您进行宏替换时,预处理器将仅在#未对字符串化运算符和令牌粘贴运算符##都应用的情况下以递归方式扩展宏。因此,您必须使用一些额外的间接层,可以将令牌粘贴操作符与递归扩展的参数一起使用:

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


3
恐怕这不适用于GNU cpp。TOKENPASTE使用LINE作为文字。TOKENPASTE(Unique_,LINE)扩展为Unique___LINE__
DD。

3
@DD:天哪,现在修复。它需要2层间接,而不是1
亚当罗森菲尔德

__COUNTER__宏没有在GCC对我的工作; 尽管该产品__LINE__确实按广告宣传工作。
泰勒

2
根据msdn.microsoft.com/en-us/library/b0084kay(v=vs.80).aspx的说明,任何尝试COUNTER的人都可以获得一些额外的信息,它是Microsoft专有的宏。
Elva 2012年

3
为什么需要2级间接的任何解释?我只用了一个,没有#和##尝试过,并且在VS2017上没有扩展它。显然,海湾合作委员会也是如此。但是,如果您添加了第二级间接,则它确实会扩展。魔法?
·霍尔斯默

-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,它允许“代码中的编译器品牌黑客行为被标记为黑客”,并且在不影响标准的情况下也可以正常工作:默认值是无用的馄饨破损,就像在使用相同名称(命名空间破坏)时更改函数的功能一样,...恶意软件在我看来

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.