这可以做到,但有两个警告:
鉴于上述情况,您可以使用RESOLVE
我想出的这个漂亮的宏。这是一个单行版本(它可以变得更加防弹/便携):
#define RESOLVE(token, default_token, ...) default_token
在这里它适用于您的示例代码:
#include <stdio.h>
#define BLANK
#define RESOLVE(token, default_token, ...) default_token
#define QUOTE(str) #str
#define QUOTE0(str) QUOTE(str) // need the intermediate QUOTE0 function here or else you get output like `RESOLVE(0, HelloWorld, , )` instead of `HelloWorld`
#define EXPAND_AND_QUOTE(str, ...) QUOTE0(RESOLVE(str, BLANK, __VA_ARGS__)) // the BLANK token is optional here, can also be a literal blank
#define MACRO 0, HelloWorld
int main() {
printf("%s\n", EXPAND_AND_QUOTE(MACRO));
printf("%s\n", QUOTE(MACRO));
#undef MACRO
printf("%s\n", EXPAND_AND_QUOTE(MACRO));
printf("%s\n", QUOTE(MACRO));
}
当我编译和测试它的输出确实是
HelloWorld
MACRO
MACRO
RESOLVE
工作原理
基本思想是,虽然预处理器不能区分已定义和未定义的标记(因为它不区分未定义的标记和常规文本),但它可以区分单个标记和元组,至少在类函数宏的参数列表中. __VA_ARGS__
然后允许宏在此区别的基础上产生不同的行为。
假设您有两个已定义的标记
#define BAR bar
#define BAZ baz0, baz1
和一个未定义的令牌FOO
。当您将这些传递给 时RESOLVE
,它们有效地扩展到
RESOLVE(FOO, BLANK) -> RESOLVE(token=, default_token=BLANK, __VA_ARGS__=) -> BLANK
RESOLVE(BAR, BLANK) -> RESOLVE(token=bar, default_token=BLANK, __VA_ARGS__=) -> BLANK
RESOLVE(BAZ, BLANK) -> RESOLVE(token=baz0, default_token=baz1, __VA_ARGS__=BLANK) -> baz1
在 的情况下BAZ
,BLANK
被存储在 中的第二个值从命名参数列表的末尾(并进入省略号)推开,BAZ
然后返回。