【问题标题】:Stringify Macro with Unicode String Literal使用 Unicode 字符串文字对宏进行字符串化
【发布时间】:2017-01-17 02:32:05
【问题描述】:

我正在使用这个预处理器宏来“字符串化”并轻松地从定义解析函数返回:

#define STRINGIFY_RETURN(x) case x:     return #x ""

它在 MBSC 环境中使用普通字符串文字就像一个魅力。示例:

#define MY_DEFINE_1 1
#define MY_DEFINE_2 2
#define MY_DEFINE_3 3

const char* GetMyDefineNameA(unsigned int value)
{
    switch(value)
    {
        STRINGIFY_RETURN(MY_DEFINE_1);
        STRINGIFY_RETURN(MY_DEFINE_2);
        STRINGIFY_RETURN(MY_DEFINE_3);
        default:    return "Unknown";
    }
}

但是我不得不越来越多地切换到 Unicode 兼容性,所以我不得不重写这个函数来返回 Unicode 字符串,这些字符串需要在字符串文字前面加上 L 前缀。所以我尝试了:

#define STRINGIFY_RETURN_WIDE(x)    case x:     return #x L""

const wchar_t* GetMyDefineNameW(unsigned int value)
{
    switch(value)
    {
        STRINGIFY_RETURN_WIDE(MY_DEFINE_1);
        STRINGIFY_RETURN_WIDE(MY_DEFINE_2);
        STRINGIFY_RETURN_WIDE(MY_DEFINE_3);
        default:    return L"Unknown";
    }
}

但这给了我错误:

错误 C2308:连接不匹配的字符串

错误 C2440: 'return' : 无法从 'const char [12]' 转换为 'const wchar_t *

我也试过了:

#define STRINGIFY_RETURN_WIDE(x)    case x:     return L #x ""
#define STRINGIFY_RETURN_WIDE(x)    case x:     return #x "" L

但无论如何,我无法让它工作。我对此一无所知,似乎无法找到解决方案。

如果有人能展示执行此宏的正确方法,以便它解析为 Unicode 字符串文字,我将不胜感激。

更新:

#define STRINGIFY_RETURN_WIDE(x)    case x:     return L#x ""

不会抛出 C2440 错误,但它仍然给我 C2308。

更新 2:

我正在使用 Microsoft Visual Studio 2013

【问题讨论】:

  • 您应该打印出预处理文件以查看编译器生成的确切内容,然后从那里开始工作。
  • 你不只是想要:#define STRINGIFY_RETURN_WIDE(x) case x: return L#x ""
  • @TimBeaudet 我刚试过,它也给了我 C2308,但 C2440 错误消失了:/
  • 这适用于 clang++:#define STRINGIFY_RETURN_WIDE(x) case x: return L""#x
  • 连接空字符串有什么好处?我认为return L#x 应该足够了。

标签: c++ c macros c-preprocessor stringification


【解决方案1】:

您有两个主要选择:

#define STRINGIFY_RETURN_WIDE(x) case x: return L#x L""

这将连接两个L"…" 字符串。另一种更简单的解决方案是不连接空字符串:

#define STRINGIFY_RETURN_WIDE(x) case x: return L#x

尚不清楚附加空字符串有什么好处。


正如Robert Prévostcomment 中指出的那样,这不适用于 G++ 和 Clang++, 虽然它似乎适用于 Vinzenz 与他的编译器(Microsoft Visual Studio 2013)。

问题在于预处理器将其输入标记化,而宽字符串文字L"..." 都是一个标记,但上面的宏试图生成标记L 和“...”`,从而导致问题:

xx11.cpp:5:49: error: ‘L’ was not declared in this scope
 #define STRINGIFY_RETURN_WIDE(x) case x: return L#x
                                                 ^
xx11.cpp:11:9: note: in expansion of macro ‘STRINGIFY_RETURN_WIDE’
         STRINGIFY_RETURN_WIDE(MY_DEFINE_1);

有一个解决方法:

#define MY_DEFINE_1 1
#define MY_DEFINE_2 2
#define MY_DEFINE_3 3

#define LSTR(x) L ## x
#define STRINGIFY_RETURN_WIDE(x) case x: return LSTR(#x)

const wchar_t* GetMyDefineNameW(unsigned int value)
{
    switch(value)
    {
        STRINGIFY_RETURN_WIDE(MY_DEFINE_1);
        STRINGIFY_RETURN_WIDE(MY_DEFINE_2);
        STRINGIFY_RETURN_WIDE(MY_DEFINE_3);
        default:    return L"Unknown";
    }
}

在带有 GCC 6.2.0 的 Mac OS X 10.11.6 上检查。

【讨论】:

  • 再次感谢,这有效。请问是否有任何可能的原因可以在该宏中连接一个空字符串?我在寻找这个的时候已经看过好几次了。留下空文字编译,我很高兴,这只是我的好奇心。
  • 这对 clang++ 不起作用。只是为了完整起见,定义两个宏就可以了。 #define _T(x) L##x #define STRINGIFY_RETURN_WIDE(x) case x: return _T(#x)。
  • @RobertPrévost:这很有趣;问题与标记化以及如何将 L 视为或不视为字符串的一部分有关。我怀疑clang++ 正在对事物采取更严格正确的竞争,但这意味着如果您需要使用 stringify # 运算符和字符串类型前缀,那么您将被双宏技术所困扰大纲。感谢您指出这一点。
  • 我想不出连接空字符串的充分理由。即使您提供了一个空参数,我相信它也会被字符串化。我怀疑这是很久以前某个古董编译器的黑魔法,原因已经消失在时间的迷雾中。
【解决方案2】:

http://en.cppreference.com/w/cpp/preprocessor/replace

表明:

#define showlist(...) puts(#__VA_ARGS__)
showlist();            // expands to puts("")
showlist(1, "x", int); // expands to puts("1, \"x\", int")

由于扩展包含引号,我认为简单地 return L#x 将是您想要的宽字符结果。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-04-16
    • 1970-01-01
    • 1970-01-01
    • 2013-06-21
    • 2011-11-30
    • 2012-01-13
    • 1970-01-01
    • 2013-11-28
    相关资源
    最近更新 更多