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

我想创建一个C宏,根据行号创建一个名字的函数。 我以为我可以做一些事情(真正的功能将有大括号内的声明):

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

我希望能扩展到如下的东西:

 static void Unique_23(void) {} 

这是行不通的。 通过令牌连接,定位宏被字面处理,最终扩展为:

 static void Unique___LINE__(void) {} 

这可能吗?

(是的,有一个真正的原因,我想要做到这一点,不管这看起来多么无用)。

问题是,当你有一个宏的替换,预处理器将只扩大宏的递归,如果没有字符串操作符#和令牌粘贴操作符##应用到它。 所以,你必须使用一些额外的间接层,你可以用递归扩展的参数来使用token-pasting操作符:

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

然后,在扩展UNIQUE期间__LINE__被扩展为行号(因为它不涉及### ),然后在TOKENPASTE扩展期间发生令牌粘贴。

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

GCC不需要“包装”(或实现),除非结果需要“串化”。 海湾合作委员会有功能,但所有可以用简单的C版本1(和一些认为伯克利4.3 C是如此之快,值得学习如何使用)。

** Clang(llvm)对于宏扩展不能正确地使用白色空格 – 它增加了空格(这肯定会将结果作为C标识符进行进一步的预处理)**,clang根本不会执行#或* macro expansion作为C预处理器预计将持续数十年。 主要的例子是编译X11,宏“Concat3”被破坏,结果是现在是MISNAMED C标识符,这当然不能建立。 我开始建造失败是他们的职业。

我认为这里的答案是“打破标准的新C是坏的C”,这些黑客总是选择(clobber命名空间),他们无缘无故地改变默认值,但并不真正“提高C”(除了他们自己的说法:说是为了解释为什么他们摆脱了所有的破坏,没有人让他们负责)。


之前的C预处理器不支持UNIq _()__,因为它们支持#pragma,它允许“编译器品牌hackery在代码中被标记为hackery”,而且在不影响标准的情况下也是如此:默认是无用的馄饨破损,就像使用相同的名称(名称空间破坏)改变一个函数的功能是…在我看来,恶意软件

Interesting Posts