为啥“#define WC(p) L#p”在 GCC 和 Clang 中不起作用?

Posted

技术标签:

【中文标题】为啥“#define WC(p) L#p”在 GCC 和 Clang 中不起作用?【英文标题】:Why does "#define WC(p) L#p" not work in GCC and Clang?为什么“#define WC(p) L#p”在 GCC 和 Clang 中不起作用? 【发布时间】:2014-07-31 20:29:09 【问题描述】:

演示程序:

#include <iostream>
using namespace std;

#define WC(p) L#p

int main()

    wcout << WC(XXX);

它在 Visual C++ 上运行良好,但在 Linux 上的 Clang 和 GCC 上都无法编译。错误(clang 3.4,Linux):

file.cpp:8:11:错误:使用未声明的标识符“L” file.cpp:4:15:注意:从宏 'WC' 扩展

为什么会失败?如何使用 C 定义创建宽字符字符串文字?就我而言,这种能力将大大减少代码重复。

【问题讨论】:

MSVC 预处理器是非标准的:它适用于字符流,而不是应有的标记流。经典示例:#define N(x) -x,然后N(N(1)); 在 MSVC 中产生错误,因为它扩展为 --1(减量),而不是 - - 1(两个一元否定)。 GCC 和 clang 是正确的:只是将 L 令牌和 "xyz" 令牌放在一起不应产生单个 L"xyz" 令牌;为此,您需要一个令牌粘贴 ## 运算符。 【参考方案1】:

预处理器对一系列标记进行操作。

在宏WC(p)的定义中,替换列表中有三个标记:L#p。当然,# 是一个运算符,它将以下标记“字符串化”。重要的是L 本身就是一个令牌。对WC(XXX) 进行宏替换的结果是两个 标记:L"XXX"

另一方面,宽字符串文字是一个单个标记。不管宏替换列表的拼写如何,两个单独的标记,其中一个是L,另一个是窄字符串文字,not 是否构成宽字符串文字。和L "XXX"一样,加一个空格。

您需要将两个标记粘贴到一个标记中才能获得宽字符串文字。这提出了以下解决方案:

#define WC(p) L ## #p

编辑:但是,正如 cmets 中所指出的,这可能也不起作用,因为该语言不保证 # 将在 ## 之前被评估。我们必须先强制​​预处理器将p 转换为字符串,然后粘贴标记:

#define CONCAT(x, y) x ## y
#define WC(p) CONCAT(L, #p)

现在WC(XXX) 将首先进行宏替换以产生CONCAT(L, "XXX")。然后进一步的宏替换产生单个令牌L"XXX"

【讨论】:

严格来说,我认为这仍然无效。 ### 的执行顺序未指定,因此允许预处理器尝试连接 L#,并发出错误消息,因为 L# 不是有效令牌。需要另一个级别的宏间接:#define WC(p) CONCAT(L, #p)#define CONCAT(x, y) x ## y @hvd 我已经纠正了。你应该写一个答案,所以我可以删除我的。 我评论了你的答案,因为它的要点是正确的,而且你的解释几乎肯定比我的更清楚,所以请随时将其编辑到你的答案中。

以上是关于为啥“#define WC(p) L#p”在 GCC 和 Clang 中不起作用?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 FxCop 将 GC.KeepAlive() 标记为违规?

为啥 windbg> !EEHeap -gc 显示的托管堆比 VMMAP.exe 小得多?

为啥 System.Timers.Timer 能在 GC 中存活,而 System.Threading.Timer 不能?

JVM堆内存很富足时,为啥经常连续发生两次full GC

Net Core已经开源好几年了, 为啥不像JVM那样很多人研究和调优其GC算法?

为啥堆在java中发生变化