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

发布于 2024-08-20 02:54:12 字数 1016 浏览 7 评论 0原文

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

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

I am working on a project where I have many constant strings formed by concatenation (numbers, etc.).

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__) ")"

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)

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 "("

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

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.

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

好久不见√ 2024-08-27 02:54:13

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

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:

In translation phase 6, the multibyte character sequences specified by any sequence of adjacent character and wide string literal tokens are concatenated into a single multibyte character sequence. If any of the tokens are wide string literal tokens, the resulting multibyte character sequence is treated as a wide string literal; otherwise, it is treated as a character string literal.

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.

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)

which should work on both gcc and Visual C (at least, it works for me, using Visual C 2005).

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_.

在梵高的星空下 2024-08-27 02:54:13

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

L"wide_a" L"wide_b"

So you can Define

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

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

To concatenate two wide literal strings you could use

L"wide_a" L"wide_b"

So you could define

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

(Note: not tested on MSVC++)

何时共饮酒 2024-08-27 02:54:13

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

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

Just using an empty wide string literal should work:

 #define WLOCATION L"" __FILE__ "(" STR(__LINE__) ")"
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文