7

我正在做一个项目,我有许多由连接(数字等)形成的常量字符串。

例如,我有一个LOCATION宏,它可以格式化__FILE____LINE__转换成一个字符串,当打印消息或错误时,我可以使用它来了解我在代码中的位置:

#define _STR(x)    # x
#define STR(x)     _STR(x)
#define LOCATION __FILE__ "(" STR(__LINE__) ")"

所以,这将格式化一个像“file.cpp(42)”这样的位置。问题是当我尝试将结果转换为宽字符串时:

#define _WIDEN(x)  L ## x
#define WIDEN(x)   _WIDEN(x)
#define WLOCATION  WIDEN(LOCATION)

这适用于 GCC,并导致 L"file.cpp(42)" 被插入到我的代码中。但是,在使用 MSVC++(使用 Visual C++ 2008 Express)尝试此操作时,出现错误:

error: Concatenating wide "file.cpp" with narrow "("

我知道L前缀只会添加到我的表达式中的第一个术语。我也试过这个:

#define _WIDEN(x) L ## #x

哪个“有效”,但给出的字符串L"\"file.cpp\" \"(\" \"42\" \")\""显然不是很方便(也不是我想要的),特别是考虑到这个宏与其他宏相比很简单。

所以,我的问题是:我怎样才能让它应用于 MSVC++ 中的整个表达式,这样我才能得到与 GCC 相同的结果?我宁愿不创建具有全范围标记的第二个字符串,因为我必须为每个宏维护两个宏,这不是很方便并且可能导致错误。另外,我还需要每个字符串的窄版本,所以不幸的是,使用全宽字符串也不是一种选择。

4

3 回答 3

12

根据 C 标准(又名“ISO-9899:1999”又名“C99”),Visual C 是错误的,而 gcc 是正确的。该标准规定,第 6.4.5/4 节:

在翻译阶段 6 中,由任何相邻字符序列和宽字符串文字标记指定的多字节字符序列连接成单个多字节字符序列。如果任何标记是宽字符串文字标记,则生成的多字节字符序列将被视为宽字符串文字;否则,它被视为字符串文字。

所以你可以投诉。可以说,C 标准的先前版本(又名“C89”又名“C90”又名“ANSI C”)没有强制将宽字符串与非宽字符串合并。虽然 C99 现在已经有十多年的历史了,但微软似乎对使其 C 编译器符合标准没有兴趣。一些用户报告说可以通过编译 C 代码来访问某些“C99”特性,就像它是 C++ 代码一样,因为 C++ 包含这些特性——对于 C++,微软做出了努力。但这似乎并没有扩展到预处理器。

在 C89 方言中,我认为你正在寻找的东西是不可能的(实际上我很确定,因为我已经编写了自己的预处理器,所以我想我知道我在说什么)。但是您可以添加一个额外的参数并传播它:

#define W(x)          W_(x)
#define W_(x)         L ## x
#define N(x)          x
#define STR(x, t)     STR_(x, t)
#define STR_(x, t)    t(#x)

#define LOCATION_(t)  t(__FILE__) t("(") STR(__LINE__, t) t(")")
#define LOCATION      LOCATION_(N)
#define WLOCATION     LOCATION_(W)

它应该适用于 gcc 和 Visual C(至少,它适用于我,使用 Visual C 2005)。

旁注:您不应定义名称以下划线开头的宏。这些名称是保留的,因此使用它们可能会与系统头文件或未来版本的编译器中使用的某些名称发生冲突。而不是_WIDEN,使用WIDEN_.

于 2010-02-03T15:08:01.813 回答
1

要连接两个宽文字字符串,您可以使用

L"wide_a" L"wide_b"

所以你可以定义

#define WLOCATION WIDEN(__FILE__) L"(" WIDEN(STR(__LINE__)) L")"

(注:未在 MSVC++ 上测试)

于 2010-02-03T14:09:28.920 回答
1

只需使用空的宽字符串文字就可以了:

 #define WLOCATION L"" __FILE__ "(" STR(__LINE__) ")"
于 2014-03-02T22:24:14.053 回答