gcc在宏定义中支持多行原始字符串吗?

sxissh06  于 2023-02-23  发布在  其他
关注(0)|答案(2)|浏览(133)

我想把一个常量json字符串放在一个header文件中,这样它就可以被其他源代码使用和查看。我想在C++11中使用原始字符串常量,因为它看起来很清晰和漂亮。然而,我尝试过gcc 4.8.5/gcc 4.9.2使用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;
}

但是,我得到编译错误:

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

如果我加上反斜杠,gcc工作:

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

但它显示错误的字符串:

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

它是一个实现定义的特性吗?gcc的更高版本支持这个特性吗?
编辑:
我下载并编译了gcc 7. 3. 1源代码,然后再次尝试我的测试代码;但是,gcc7.3.1报告了与gcc4.X相同的错误。我放弃了,并决定继续使用static const char *。@lyang的答案也不错,它打开了我的思路。

waxmsbnn

waxmsbnn1#

最新更新日期:2019年4月4日

我最近听说了Cog,认为它可能是一个比m4更好的解决方案,它使用python进行预处理,使代码更易读,如下所示(轻松支持字符串中的引号):

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

输出:

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

==============================================================
你试过GNU的m4吗?
这有点笨拙,但其思想是将一个好看的版本预处理为难看的版本(不使用原始字符串)。
您的代码如下所示:

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

丑陋的ESCAPE_RAW宏只需要定义一次,所有后面的“原始字符串“都可以使用ESCAPE_RAW生成gcc可以识别的丑陋版本。
要使用m4进行预处理,请使用命令m4 -P test.cpp,其中-P在define语句中强制使用m4_前缀。

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

也许您的m4文件命名为.m4扩展名,并使用m4生成难看的头文件。

7ivaypg9

7ivaypg92#

此问题由this bug report解决,它似乎在2022年6月实施了修复。截至本文撰写之时,该修复计划在GCC 13中发布,并且已在 Backbone.js 中提供。
我怀疑这里发生的事情是[cpp.pre]/1中关于如何处理预处理器指令的以下规则和附带注解:
序列中的最后一个标记是序列中紧跟包含换行符的空格的第一个标记。[ * 注意:* 换行符结束预处理指令,即使它出现在类似函数的宏的调用中。- end note ]
我猜GCC在预处理指令中检测换行符时过于激进,导致了您所遇到的行为。当您添加反斜杠时,规范中的字母表示各行很早就被拼接成一行,但是当词法分析原始字符串文字标记时,这种拼接会在原始字符串文字中恢复。这似乎避免了换行符检测。但是当然会产生一个与您在这个过程中想要的不同的原始字符串。

相关问题