Разрешены ли пустые макросы стандартом языка C++?
Согласно N3092 (окончательный проект комитета С++ 11), в 16.3: Замена макроса, объектоподобный макрос определяется как:
# define identifier replacement-list new-line
и подобный функции макрос определяется как:
# define identifier lparen identifier-list_opt ) replacement-list new-line
# define identifier lparen ... ) replacement-list new-line
# define identifier lparen identifier-list , ... ) replacement-list new-line
У всех есть список замены (так называемое значение макроса), и это определяется как:
replacement-list:
pp-tokensopt
и pp-токены определяются как:
pp-tokens:
preprocessing-token
pp-tokens preprocessing-token
и токен предварительной обработки является, например, идентификатором.
Насколько я прочитал, макрос с пустым значением четко определен.
Тем не менее, 16.3-3 говорит:
Между идентификатором и списком замещения в определении объектоподобного макроса должен быть пробел.
и обратите внимание, что список замен сам по себе не является обязательным (хотя он может иметь пустое значение).
Поэтому я верю:
//well-formed (function-like macro with value)
#define f(x) (x)<NEWLINE>
//well-formed (function-like macro without value)
#define f(x)<NEWLINE>
//well-formed (function-like macro without value)
#define f(x) <NEWLINE>
//well-formed (object-like macro with value)
#define f hello<NEWLINE>
//**ill-formed** (object-like macro without value)
#define f<NEWLINE>
//well-formed (object-like macro without value)
#define f <NEWLINE>
и, таким образом, например, так называемая "защита включения" должна иметь вид:
#ifndef is_xyz_included<NEWLINE>
#define is_xyz_included <NEWLINE> // NOTE THE SPACE BEFORE <NEWLINE>
#endif<NEWLINE>
Моя интерпретация неверна?
Ответы
Ответ 1
Я считаю, что ваша интерпретация верна. Следовательно, действительный включающий охранник должен иметь форму, которую вы описываете, с пробелом, который не является новой строкой. Не перевод строки из-за
[CPP]
4 Единственными пробельными символами, которые должны появляться между токенами предварительной обработки в директиве предварительной обработки (сразу после введения # токена предварительной обработки непосредственно перед завершающим символом новой строки), являются пробел и горизонтальная табуляция (включая пробелы, которые заменили комментарии или возможно, другие символы пробела в фазе перевода 3).
что препятствует тому, чтобы новая строка была пробелом, о котором говорилось в абзаце, который вы упомянули.
Но учитывая, насколько распространена эта идиома с включенными защитными элементами, настолько, что сам стандарт содержит такой пример, это, вероятно, дефект формулировки, основанный на предположении, что список замены не должен быть пустым, чтобы макрос мог иметь какой-либо использовать.
Стоит упомянуть, что это было ошибкой формулировки, разделяемой со стандартом C, насколько мне удалось выяснить формулировку.
Ответ 2
Моя интерпретация неверна?
Я считаю, что ваша интерпретация верна. GCC и Clang испускают диагностику для плохо сформированного макроопределения.
//**ill-formed** (object-like macro without value)
#define f<NEWLINE>
GCC:
#1 with x86-64 gcc 9.1
<source>:2:9: warning: ISO C++11 requires whitespace after the macro name
2 | #define f<NEWLINE>
Clang:
<source>:2:10: warning: ISO C99 requires whitespace after the macro name [-Wc99-extensions]
#define f<NEWLINE>
Посмотреть демо
Ответ 3
Стандарт гласит:
Между идентификатором и списком замещения в определении объектоподобного макроса должен быть пробел.
Таким образом, для:
#define foo
просто после новой строки после foo
нам нужно проверить требования стандарта.
Для того, чтобы между идентификатором и списком замещения был пробел, после foo
должен быть пробел. Есть; символ новой строки считается пробелом, и в стандарте не указано, что это должен быть какой-то определенный тип пробела (например, обычный пробел или табуляция). Поскольку после foo
есть пробел, это означает требование пробел между идентификатором и списком замены уже выполнен. Так что пока все действительно.
Затем нам нужно проверить, есть ли список замены. Нет, так как символ новой строки отмечает конец определения макроса. Таким образом, у нас есть пустой список замены. Действителен до сих пор.
Определение макроса закончено, и все до этого момента является действительным. Таким образом, макроопределение не является плохо сформированным.
Тот факт, что символ новой строки здесь служит двум целям (пробел между идентификатором и списком замещения, а также терминатор определения макроса), не делает макрос неправильным.
Ответ 4
Моя интерпретация неверна?
Да. Ниже приведена совершенно корректная программа C++. Пробела после A
в строке нет.
#define A
int main() {}
Из вашего поста replacement_list
pp-tokens
список состоит из необязательных pp-tokens
, что делает пустой список replacement_list
вполне допустимым для использования, когда #define
-ing объектно-подобный макрос.
Стандарт даже включает в себя пример пустого макроса, который не имеет пробела после макроса. См. Https://timsong-cpp.github.io/cppwp/n3337/cpp#6.
#define EMPTY
EMPTY # include <file.h>
Обратите внимание, что вторая строка недопустима, но первая строка верна.