Поддерживает ли gcc многострочные необработанные строковые литералы в определении макроса? - PullRequest
1 голос
/ 12 марта 2019

Я хочу поместить постоянную строку 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 сообщает о тех же ошибках, что и gcc 4.X. Я сдаюсь и решаю продолжать использовать static const char *. Ответ @lyang также хорош, он открывает мои умы.

1 Ответ

0 голосов
/ 12 марта 2019

Последнее обновление 4/4/2019

Я недавно слышал о 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;
}

выход: * +1010 *

#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 принудительно устанавливает префикс m4_ в операторах define. Команда производит это:

#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 для создания уродливых заголовочных файлов.

...