如何使用 C 预处理器将连接的字符串转换为宽字符?
我正在做一个项目,其中有许多由连接(数字等)组成的常量字符串.
I am working on a project where I have many constant strings formed by concatenation (numbers, etc.).
例如,我有一个 LOCATION
宏,它可以将 __FILE__
和 __LINE__
格式化成一个字符串,我可以用它来知道我所在的位置打印消息或错误时的代码:
For example, I have a LOCATION
macro that formats __FILE__
and __LINE__
into a string that I can use to know where I am in the code, when printing messages or errors:
#define _STR(x) # x
#define STR(x) _STR(x)
#define LOCATION __FILE__ "(" STR(__LINE__) ")"
因此,这将格式化一个类似于file.cpp(42)"的位置.问题是当我尝试将结果转换为宽字符串时:
So, this would format a location like "file.cpp(42)". The problem is when I try to convert the result to a wide-string:
#define _WIDEN(x) L ## x
#define WIDEN(x) _WIDEN(x)
#define WLOCATION WIDEN(LOCATION)
这与 GCC 配合得很好,并导致 L"file.cpp(42)" 被插入到我的代码中.但是,在使用 MSVC++(使用 Visual C++ 2008 Express)尝试此操作时,出现错误:
This works just fine with GCC, and results in L"file.cpp(42)" being inserted in my code. However, when trying this with MSVC++ (using Visual C++ 2008 Express), I get an error:
error: Concatenating wide "file.cpp" with narrow "("
我知道 L
前缀只会添加到我的表达式中的第一个术语.我也试过这个:
I understand that the L
prefix gets added only to the first term in my expression. I've also tried this:
#define _WIDEN(x) L ## #x
哪个有效",但是给出了字符串 L""file.cpp" "(" "42" ")""
这显然不是很方便(而不是我要找的),特别是考虑到这个宏与其他宏相比很简单.
Which "works", but gives the string L""file.cpp" "(" "42" ")""
which is obviously not very convenient (and not what I am looking for), especially considering that this macro is simple compared to other macros.
所以,我的问题是:如何才能将其应用于 MSVC++ 中的整个表达式,以便获得与 GCC 相同的结果?我宁愿不创建具有全范围标记的第二个字符串,因为我将不得不为每个宏维护两个宏,这不是很方便并且可能导致错误.另外,我还需要每个字符串的窄版本,因此不幸的是,使用全宽字符串也不是一种选择.
So, my question is: how can I get it to apply to the entire expression in MSVC++, so I can get the same result I am getting with GCC? I would rather not create a second string with all-wide tokens, because I would then have to maintain two macros for each one, which is not very convenient and can lead to bugs. Plus, I need the narrow version of each string as well, so using all-wide strings is not an option either, unfortunately.
推荐答案
根据 C 标准(又名ISO-9899:1999"又名C99"),Visual C 是错误的,gcc 是正确的.该标准规定,第 6.4.5/4 节:
According to the C standard (aka "ISO-9899:1999" aka "C99"), Visual C is wrong and gcc is correct. That standard states, section 6.4.5/4:
在翻译阶段 6 中,由任何相邻字符序列和宽字符串文字标记指定的多字节字符序列连接成单个多字节字符序列.如果任何标记是宽字符串文字标记,则生成的多字节字符序列将被视为宽字符串文字;否则,它被视为字符串文字.
所以你可以提出投诉.可以说,以前版本的 C 标准(又名C89"又名C90"又名ANSI C")并没有强制要求将宽字符串与非宽字符串合并.虽然 C99 现在已经有十多年的历史了,但微软似乎没有兴趣让它的 C 编译器符合标准.一些用户报告说,可以通过像编译 C++ 代码一样编译 C 代码来访问某些C99"功能,因为 C++ 包含这些功能――而对于 C++,Microsoft 做出了努力.但这似乎并没有扩展到预处理器.
So you could file a complaint. Arguably, the previous version of the C standard (aka "C89" aka "C90" aka "ANSI C") did not mandate merging of wide strings with non-wide strings. Although C99 is now more than ten years old, it seems that Microsoft has no interest in making its C compiler conforming. Some users have reported being able to access some "C99" features by compiling C code as if it was C++ code, because C++ includes these features -- and for C++, Microsoft made an effort. But this does not seem to extend to the preprocessor.
在C89方言中,我认为您要寻找的东西是不可能的(实际上我很确定,而且由于我已经编写了自己的预处理器,因此我想我知道我在说什么).但是你可以添加一个额外的参数并传播它:
In the C89 dialect, I think that what you are looking for is not possible (actually I am pretty sure of it, and since I have written my own preprocessor I think I know what I am talking about). But you could add an extra parameter and propagate it:
#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).
which should work on both gcc and Visual C (at least, it works for me, using Visual C 2005).
旁注:您不应该定义名称以下划线开头的宏.这些名称是保留的,因此通过使用它们,您可能会与系统头文件或未来版本的编译器中使用的某些名称发生冲突.使用 WIDEN_
代替 _WIDEN
.
Side note: you should not define macros with a name beginning with an underscore. These names are reserved, so by using them you could clash with some names used in system headers or in future versions of the compiler. Instead of _WIDEN
, use WIDEN_
.
相关文章