我可以在字符串化之前强制扩展未定义的宏吗?

时间:2013-10-25 23:17:33

标签: c++ c macros

免责声明:我已经查看了与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;传递给引用函数。

5 个答案:

答案 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)产生的情况下,在辅助宏的扩展链中的某个地方,两个令牌SomethingMACROSomethingMACRO,而EXPAND_AND_QUOTE(FOO)生成SomethingFOOSomethingMACRO。这是可能的,但那么我们如何处理这两个令牌呢?我们可以对两者进行字符串化,然后在运行时将它们与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)

这个可以完成,有两点需要注意:

  • 您的编译器必须支持可变参数宏(它们是C99标准的一部分)
  • 在定义宏令牌时,您必须添加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的情况下,BLANKBAZ中存储的第二个值从命名参数列表的末尾(并进入省略号)中推出返回。