免责声明:我已经查看了与Stringification of a macro value类似的问题的答案。
考虑以下测试程序:
#include <stdio.h>
#define QUOTE(str) #str
#define EXPAND_AND_QUOTE(str) QUOTE(str)
#define MACRO 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
MACRO
所需的输出是
HelloWorld
MACRO
MACRO
有没有办法重新定义我的宏(即不是MACRO的元宏)来获得所需的输出?
特别是,我希望第三个printf
应该等同于:
printf("%s\n", QUOTE());
也就是说,我希望将未定义的宏扩展为&#34;虚无&#34;然后有那个&#34;虚无&#34;传递给引用函数。
答案 0 :(得分:3)
这在标准C中是不可能的。
在宏替换期间,令牌可能会发生四件事。它可以保持不变,可以用替换值替换,它可以与另一个标记连接,并且可以进行字符串化。
如果MACRO
未更改,则无法区分这两种情况:
#undef MACRO
printf("%s\n", EXPAND_AND_QUOTE(MACRO));
#define FOO MACRO
printf("%s\n", EXPAND_AND_QUOTE(FOO));
在后者中替换FOO
后,我们有两种情况:在一种情况下,我们有MACRO
因为它没有变化。另一方面,我们有MACRO
,因为它已取代FOO
。在那之后,行为必须相同。但问题要求需要不同的行为,前者为""
,后者为"MACRO"
。由于相同的行为不能产生不同的结果,因此这不起作用。 (但更多关于以下内容。)
第二种可能性,即用另一个值替换,不会发生,因为MACRO
未定义。
第三种可能性仅产生一些新的令牌,例如FOOMACRO
。然而,虽然我们可以选择第一部分,但我们不能选择后一部分,因此我们无法知道会产生什么标记,并且根据是否定义MACRO
,我们可能无法使用它。
第四种可能性与第三种可能性相同,我们生成"MACRO"
并且不能使用它。
我对此做的唯一模糊的希望是生成两个令牌,一个是让令牌被替换而另一个不被取代的结果。例如,我们可以产生一种情况,在上面给定MACRO
的非定义和FOO
的定义,EXPAND_AND_QUOTE(MACRO)
产生的情况下,在辅助宏的扩展链中的某个地方,两个令牌SomethingMACRO
和SomethingMACRO
,而EXPAND_AND_QUOTE(FOO)
生成SomethingFOO
和SomethingMACRO
。这是可能的,但那么我们如何处理这两个令牌呢?我们可以对两者进行字符串化,然后在运行时将它们与strcmp
进行比较。但是,我不认为在编译时可以做任何事情。此外,它无法区分FOO
被定义为#define FOO FOO
的情况;如果我们成功地比较上述令牌,那么会生成""
,但要求会生成"FOO"
。
答案 1 :(得分:2)
试试这个:
#undef MACRO
#define MACRO
可替换地:
#undef MACRO
#ifndef MACRO
# undef EXPAND_AND_QUOTE
# define EXPAND_AND_QUOTE(str) ""
#endif
答案 2 :(得分:1)
而不是#undef MACRO
尝试:
#undef MACRO
#define MACRO \0
\ 0确保可以传递MACRO(因为某些编译器咳嗽 VC ++ 咳嗽无法解析MACRO)但仍然是一个空字符串。 \ 0是ASCII字符串结尾的转义字符,实质上是将MACRO设置为空字符串。
答案 3 :(得分:1)
标题中的问题没有任何意义 - 未定义的宏如何扩展?它未定义,因此无法扩展。获得您想要的输出的解决方案是添加
#define MACRO
在#undef之后 - 也就是说,将MACRO
定义为一个扩展为空的宏。
答案 4 :(得分:-1)
这个可以完成,有两点需要注意:
在定义宏令牌时,您必须添加0,
,即
#define MACRO 0, HelloWorld
鉴于上述情况,您可以使用我提出的这个漂亮的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
中存储的第二个值从命名参数列表的末尾(并进入省略号)中推出返回。