Stringify Macro with Unicode String Literal
Asked Answered
L

2

10

I'm using this preprocessor macro to "stringify" and return easily from a definition resolving function:

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

It works like a charm in MBSC environment with normal string literals. Example:

#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";
    }
}

However I had to switch to Unicode compatibility more and more and so I had to rewrite this function to return Unicode strings which require the prefixing with L in front of the string literals. So I tried:

#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";
    }
}

But that gives me the errors:

error C2308: concatenating mismatched strings

error C2440: 'return' : cannot convert from 'const char [12]' to 'const wchar_t *

I also tried:

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

but no matter what, I can't get it to work. I'm clueless about this and can't seem to find a solution.

I'd be really grateful if someone could show the correct way to do this macro so that it resolves to a Unicode string literal.

Update:

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

does not throw the C2440 error, but it still gives me the C2308.

Update 2:

I'm using Microsoft Visual Studio 2013

Lahomalahore answered 9/9, 2016 at 2:54 Comment(7)
You should print out the preprocessed file to see exactly what the compiler produced, and then work from there.Ladylike
Would you not just want: #define STRINGIFY_RETURN_WIDE(x) case x: return L#x ""Wood
@TimBeaudet I just tried, it gives me C2308 as well but the C2440 error is gone :/Lahomalahore
This works with clang++: #define STRINGIFY_RETURN_WIDE(x) case x: return L""#xBirkle
What's the benefit of concatenating the empty string anyway? return L#x should be sufficient, I think.Abbate
@RobertPrévost just tried that, gives me C2308 as well. I'm using Visual Studio 2013Lahomalahore
@JonathanLeffler That worked! I copied that piece of code off an example and I don't know that much about preprocessor stuff to be honest. Didn't know I can remove the "" from it!Lahomalahore
A
8

You have two main options:

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

This concatenates two L"…" strings. The alternative, and simpler, solution is to not concatenate the empty string:

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

It is not clear that there's any benefit to appending an empty string.


As Robert Prévost noted in a comment, this does not work with G++ and Clang++, though it seems to work for Vinzenz with his compiler (Microsoft Visual Studio 2013).

The problem is that the preprocessor tokenizes its input, and a wide string literal L"..." is all one token, but the macro above tries to generate tokens L and "..."`, leading to problems:

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);

There is a workaround:

#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";
    }
}

Checked on Mac OS X 10.11.6 with GCC 6.2.0.

Abbate answered 9/9, 2016 at 3:11 Comment(4)
Thanks again, that worked. May I ask though if there is any possible reason why one would concatenate an empty string in that macro? I've seen it several times while searching for this. Leaving the empty literals out compiles and I'm happy, it's just my curiosity.Lahomalahore
This didn't work for clang++. Just for completeness, defining two macros does. #define _T(x) L##x #define STRINGIFY_RETURN_WIDE(x) case x: return _T(#x).Birkle
@RobertPrévost: That's intriguing; the problem is related to tokenization and how the L is, or is not, treated as part of the string. I suspect that clang++ is taking a more strictly correct vie on things, but it means that if you need to use the stringify # operator and a string-type prefix, then you're stuck with the dual-macro technique you outline. Thanks for pointing that out.Abbate
I can think of no good reason for concatenating an empty string. Even if you provided an empty argument, I believe it would be stringified. I suspect it was black magic for some antique compiler once upon a long time ago and the reason is lost in the mists of time.Abbate
W
2

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

Shows that:

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

Since the expansion includes the quotes, I think simply return L#x would be your desired result for wide characters.

Wood answered 9/9, 2016 at 3:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.