如何使用C预处理器将连接字符串转换为wide-char?

时间:2010-02-03 14:04:01

标签: c++ visual-c++ c-preprocessor wchar-t stringification

我正在开发一个项目,我通过连接(数字等)形成许多常量字符串。

例如,我有一个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相同的结果?我宁愿不用全宽标记创建第二个字符串,因为我必须为每个标记维护两个宏,这不是很方便并且可能导致错误。另外,我也需要每个字符串的窄版本,所以不幸的是,使用全宽字符串也不是一种选择。

3 个答案:

答案 0 :(得分:11)

根据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_

答案 1 :(得分:1)

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

L"wide_a" L"wide_b"

所以你可以定义

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

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

答案 2 :(得分:1)

只需使用空的宽字符串文字即可:

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