简体   繁体   中英

Does gcc support multi-line raw string literals in macro definition?

I want to put a constant json string in a header file, so that it can be used and also viewed by other sources. I want to use raw string literals in C++11, because it looks clear and pretty. However, i have tried gcc 4.8.5/gcc 4.9.2 to compile the following code using gcc -std=c++11 test.cpp :

#include <cstdio>

/* works, but looks ugly */
#define STR_a \
"{ \n\
    \"AAA\": \"a\", \n\
    \"BBB\": \"b\" \n\
}"

/* works with VS2017, not works with gcc */
#define STR_b \
R"({
    "AAA": "a",
    "BBB": "b"
})";

/* works, but must use 'extern const'/'static' in header files */
const char *STR_var = 1 + R"(
{
    "AAA": "a",
    "BBB": "b"
})";

int main()
{
    const char *s = STR_b;
    printf("%s\n", s);
    return 0;
}

However, i get compile errors:

test.cpp:16:1: error: unterminated raw string
 R"({ 
 ^
test.cpp:19:3: warning: missing terminating " character
 })";
   ^
test.cpp:19:1: error: missing terminating " character
 })";
 ^
test.cpp:29:2: error: stray ‘R’ in program

If i add backslashs, gcc works:

#define STR_b \
R"({ \
    "AAA": "a", \
    "BBB": "b" \
})";

But it shows wrong string:

{ \
    "AAA": "a", \
    "BBB": "b" \
}

Is it an implementation-defined feature? Does higher versions of gcc support this feature?


Edit:

I downloaded and compiled gcc 7.3.1 source, then tried again for my test code; however, gcc7.3.1 reports the same errors like gcc 4.X. I give up, and decide to continue to use static const char * . The answer of @lyang is also good, it opens my minds.

Latest update 4/4/2019

I recently heard about Cog and think that it could be a better solution than m4. It uses python for preprocessing, making the code more readible, as shown below (easily support quote inside string):

#include <cstdio>

/*[[[cog
import cog, re

def escape_raw(s):
    s = re.sub(r'\\', r'\\\\', s) # escape backslash first
    s = re.sub(r'"', r'\"', s) # escape quotes
    return re.sub(r'\n', r'\\n\n', s) # escape newline last

def cog_define(name, val):
    cog.outl("#define {name} {val}".format(name=name, val=val))

STR_test = r"""{
    "AAA": "a",
    "BBB": "b",
    "contain \" quote": "c"
}"""

cog_define("STR_test", escape_raw(STR_test))

]]]*/
//[[[end]]]

int main()
{
    const char *s = STR_test;
    printf("%s\n", s);
    return 0;
}

output:

#include <cstdio>

/*[[[cog
import cog, re

def escape_raw(s):
    s = re.sub(r'\\', r'\\\\', s) # escape backslash first
    s = re.sub(r'"', r'\"', s) # escape quotes
    return re.sub(r'\n', r'\\n\n', s) # escape newline last

def cog_define(name, val):
    cog.outl("#define {name} {val}".format(name=name, val=val))

STR_test = r"""{
    "AAA": "a",
    "BBB": "b",
    "contain \" quote": "c"
}"""

cog_define("STR_test", escape_raw(STR_test))

]]]*/
#define STR_test {\n
    \"AAA\": \"a\",\n
    \"BBB\": \"b\",\n
    \"contain \\\" quote\": \"c\"\n
}
//[[[end]]]

int main()
{
    const char *s = STR_test;
    printf("%s\n", s);
    return 0;
}

==============================================================

Have you tried GNU's m4 ?

This is kind of hacky, but the idea is to preprocess a nicer looking version to the ugly version (not using raw string).

Your code will be like this:

#include <cstdio>

m4_changecom(`/*', `*/')
m4_define(`ESCAPE_RAW', `"m4_patsubst(`m4_patsubst($1, `"', `\\"')', `
', `\\n\\
')'") /* substitute newline and double quote with escaped version */

#define STR_test ESCAPE_RAW(`{
    "AAA": "a",
    "BBB": "b"
}')

int main()
{
    const char *s = STR_test;
    printf("%s\n", s);
    return 0;
}

The ugly ESCAPE_RAW macro only needs to define once, and all following "raw string"s can use ESCAPE_RAW to generate ugly versions that gcc recognizes.

To preprocess with m4, use command m4 -P test.cpp , where -P forces the m4_ prefix on the define statements. The command produces this:

#include <cstdio>




#define STR_test "{\n\
    \"AAA\": \"a\",\n\
    \"BBB\": \"b\"\n\
}"

int main()
{
    const char *s = STR_test;
    printf("%s\n", s);
    return 0;
}

Maybe name your m4 file with .m4 extension, and use m4 to generate the ugly looking header files.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM