为啥“#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 不能?