19 Preprocessing directives [cpp]

19.3 Macro replacement [cpp.replace]

Два списка замены идентичны тогда и только тогда, когда токены предварительной обработки в обоих имеют одинаковый номер, порядок, орфографию и разделение пробелов, где все разделения пробелов считаются идентичными.

Идентификатор, в настоящее время определенный как объектно-подобный макрос (см. Ниже), может быть переопределен другой #define директивой предварительной обработки при условии, что второе определение является объектным определением макроса и два списка замены идентичны, в противном случае программа имеет неправильный формат. Аналогичным образом, идентификатор, который в настоящее время определяется как макрос, подобный функции (см. Ниже), может быть переопределен другой #define директивой предварительной обработки при условии, что второе определение является определением макроса, подобным функции, которое имеет тот же номер и написание параметров, и два списка замены идентичны, иначе программа будет некорректной.

Между идентификатором и списком замен в определении объектно-подобного макроса должен быть пробел.

Если identifier-listв определении макроса не заканчивается многоточие, количество аргументов (включая те аргументы, которые не содержат токенов предварительной обработки) в вызове макроса, подобного функции, должно равняться количеству параметров в определении макроса. В противном случае в вызове должно быть больше аргументов, чем параметров в определении макроса (исключая...). Должен существовать ) токен предварительной обработки, который завершает вызов.

Идентификатор__VA_­ARGS__ должен встречаться только в replacement-list функциональном макросе, в параметрах которого используется многоточие.

Идентификатор параметра в макросе, подобном функции, должен быть однозначно объявлен в пределах его области действия.

Идентификатор, следующий сразу за define символом, называется macro name. Для имен макросов существует одно пространство имен. Любые символы пробела, предшествующие или следующие за списком замены токенов предварительной обработки, не считаются частью списка замены ни для одной из форм макроса.

Если # токен предварительной обработки, за которым следует идентификатор, лексически встречается в точке, в которой может начаться директива предварительной обработки, идентификатор не подлежит замене макроса.

Директива предварительной обработки формы

# define identifier replacement-list new-line

определяет, object-like macro что заставляет каждый последующий экземпляр имени макроса148 заменяться списком замены токенов предварительной обработки, которые составляют оставшуюся часть директивы.149 Затем список замены повторно просматривается для поиска дополнительных имен макросов, как указано ниже.

Директива предварительной обработки формы

# define identifier lparen identifier-listopt ) replacement-list new-line
# define identifier lparen ... ) replacement-list new-line
# define identifier lparen identifier-list , ... ) replacement-list new-line

определяетfunction-like macro с параметрами, использование которых аналогично синтаксически вызову функции. Параметры задаются необязательным списком идентификаторов, область действия которого простирается от их объявления в списке идентификаторов до символа новой строки, который завершает #define директиву предварительной обработки. Каждый последующий экземпляр функционально-подобного имени макроса, за которым следует a ( в качестве следующего токена предварительной обработки, представляет последовательность токенов предварительной обработки, которая заменяется списком замены в определении (вызов макроса). Замененная последовательность токенов предварительной обработки завершается совпадающим ) токеном предварительной обработки, пропуская промежуточные согласованные пары левых и правых скобок предварительной обработки токенов. В последовательности токенов предварительной обработки, составляющих вызов макроса, подобного функции, новая строка считается нормальным символом пробела.

Последовательность токенов предварительной обработки, ограниченная внешними совпадающими круглыми скобками, формирует список аргументов для макроса, подобного функции. Отдельные аргументы в списке разделяются запятыми токенами предварительной обработки, но запятые токены предварительной обработки между соответствующими внутренними скобками не разделяют аргументы. Если в списке аргументов есть последовательности токенов предварительной обработки, которые в противном случае действовали бы как директивы предварительной обработки,150 поведение не определено.

Если есть ,... непосредственно предшествующий) в функции-как макроопределение, то задние аргументах, включая любую разделяющую запятую предварительной обработки маркеры, объединяется для формирования одного товара:variable arguments. Количество аргументов, объединенных таким образом, таково, что после слияния количество аргументов на один больше, чем количество параметров в определении макроса (исключая ...).

Поскольку к моменту замены макроса все символьные литералы и строковые литералы являются токенами предварительной обработки, а не последовательностями, которые могут содержать подпоследовательности, подобные идентификаторам (см.[lex.phases]Этапы трансляции), они никогда не сканируются на предмет имен макросов или параметров.

alternative token Не является идентификатором, даже если его написание полностью состоит из букв и символов подчеркивания. Поэтому невозможно определить макрос, имя которого совпадает с именем альтернативного токена.

A conditionally-supported-directive- это директива предварительной обработки независимо от того, поддерживает ли ее реализация.

19.3.1 Argument substitution [cpp.subst]

После того, как аргументы для вызова макроса, подобного функции, были идентифицированы, происходит подстановка аргументов. Параметр в списке замены, если не предшествуют # или ## предварительной обработкой маркера или сопровождаемое ## предварительная обработка маркера (см ниже), заменяются на соответствующий аргумент после того, как все содержащиеся в нем макросы были расширены. Перед заменой токены предварительной обработки каждого аргумента полностью заменяются макросом, как если бы они сформировали остальную часть файла предварительной обработки; другие токены предварительной обработки недоступны.

Идентификатор,__VA_­ARGS__ который встречается в списке замены, должен рассматриваться, как если бы он был параметром, а переменные аргументы должны формировать токены предварительной обработки, используемые для его замены.

19.3.2 The # operator [cpp.stringize]

За каждым # токеном предварительной обработки в списке замены для функционально-подобного макроса должен следовать параметр в качестве следующего токена предварительной обработки в списке замены.

Acharacter string literal - это string-literalбез префикса. Если в списке замены параметру непосредственно предшествует # токен предварительной обработки, оба они заменяются токеном предварительной обработки литерала из односимвольной строки, который содержит написание последовательности токенов предварительной обработки для соответствующего аргумента. Каждое появление пробела между токенами предварительной обработки аргумента становится одним пробелом в литерале строки символов. Пробел перед первым токеном предварительной обработки и после последнего токена предварительной обработки, составляющего аргумент, удаляется. В противном случае, первоначальное написание каждого Preprocessing лексемы аргумента сохраняется в строке литеры, для специальной обработки , за исключением производства написания строк и символьных литералы: а \ символ вставляется перед каждым " и \ характером литеры или строкового литерала (включая" символы- разделители ). Если полученная замена не является допустимым символьным строковым литералом, поведение не определено. Литерал символьной строки, соответствующий пустому аргументу, равен"". Порядок оценки # и ## операторов не определен.

19.3.3 The ## operator [cpp.concat]

## Предварительная обработка маркера не должны происходить в начале или в конце списка замены для любой формы макроопределения.

Если в списке замены макроса, подобного функции, параметру непосредственно предшествует или следует ## токен предварительной обработки, параметр заменяется последовательностью токенов предварительной обработки соответствующего аргумента; однако, если аргумент не содержит токенов предварительной обработки, вместо этого параметр заменяется токеном предварительной обработки метки места размещения.151

Как для объектно-подобных, так и для функционально-подобных вызовов макросов, прежде чем список замены будет повторно исследован на предмет дополнительных имен макросов для замены, каждый экземпляр ## токена предварительной обработки в списке замены (не из аргумента) удаляется, а предыдущий токен предварительной обработки объединяется со следующим токеном предварительной обработки. Жетоны предварительной обработки меток-меток обрабатываются особым образом: объединение двух меток приводит к одному токену предварительной обработки метки-метки, а объединение метки-метки с токеном предварительной обработки без метки приводит к получению маркера предварительной обработки без метки. Если результат не является допустимым токеном предварительной обработки, поведение не определено. Полученный токен доступен для дальнейшей замены макроса. Порядок оценки ## операторов не указан.

[ Example: В следующем фрагменте:

#define hash_hash # ## #
#define mkstr(a) # a
#define in_between(a) mkstr(a)
#define join(c, d) in_between(c hash_hash d)
char p[] = join(x, y);          // equivalent to char p[] = "x ## y";

Расширение производит на разных этапах:

join(x, y)
in_between(x hash_hash y)
in_between(x ## y)
mkstr(x ## y)
"x ## y"

Другими словами, при раскрытииhash_­hash создается новый токен, состоящий из двух смежных острых знаков, но этот новый токен не является ## оператором. ]end example

Жетоны предварительной обработки меток не отображаются в синтаксисе, поскольку они являются временными объектами, которые существуют только на этапе перевода 4.

19.3.4 Rescanning and further replacement [cpp.rescan]

После того, как все параметры в списке замены были заменены и# и## обработка имеет место, все placemarker предобработки лексем удаляется. Затем результирующая последовательность токенов предварительной обработки повторно сканируется вместе со всеми последующими токенами предварительной обработки исходного файла для замены других имен макросов.

Если имя заменяемого макроса обнаружено во время этого сканирования списка замены (не включая остальные токены предварительной обработки исходного файла), он не заменяется. Более того, если какие-либо вложенные замены встречаются с именем заменяемого макроса, оно не заменяется. Эти незамещенные токены предварительной обработки имени макроса больше не доступны для дальнейшей замены, даже если они позже (повторно) проверяются в контекстах, в которых в противном случае токен предварительной обработки имени макроса был бы заменен.

Полученная в результате полностью замененная макросами последовательность токенов предварительной обработки не обрабатывается как директива предварительной обработки, даже если она похожа на нее, но все выражения унарных операторов прагмы в ней затем обрабатываются, как указано[cpp.pragma.op] ниже.

19.3.5 Scope of macro definitions [cpp.scope]

Определение макроса длится (независимо от структуры блока) до тех пор, пока#undef не встретится соответствующая директива или (если не встретится ни одна) до конца единицы трансляции. Макроопределения не имеют значения после фазы перевода 4.

Директива предварительной обработки формы

# undef identifier new-line

заставляет указанный идентификатор больше не определяться как имя макроса. Он игнорируется, если указанный идентификатор в настоящее время не определен как имя макроса.

[ Example: Самым простым способом использования этой возможности является определение «константы манифеста», как в

#define TABSIZE 100
int table[TABSIZE];

end example]

[ Example: Следующее определяет макрос, подобный функции, значение которого является максимальным из его аргументов. Он имеет преимущества работы с любыми совместимыми типами аргументов и генерации встроенного кода без накладных расходов на вызов функции. У него есть недостатки, заключающиеся в том, что он оценивает один или другой его аргумент второй раз (включая побочные эффекты) и генерирует больше кода, чем функция, если вызывается несколько раз. Он также не может получить свой адрес, поскольку у него его нет.

#define max(a, b) ((a) > (b) ? (a) : (b))

Скобки обеспечивают правильную привязку аргументов и результирующего выражения. ]end example

[ Example: Чтобы проиллюстрировать правила переопределения и пересмотра, последовательность

#define x       3
#define f(a)    f(x * (a))
#undef  x
#define x       2
#define g       f
#define z       z[0]
#define h       g(~
#define m(a)    a(w)
#define w       0,1
#define t(a)    a
#define p()     int
#define q(x)    x
#define r(x,y)  x ## y
#define str(x)  # x

f(y+1) + f(f(z)) % t(t(g)(0) + t)(1);
g(x+(3,4)-w) | h 5) & m
    (f)^m(m);
p() i[q()] = { q(1), r(2,3), r(4,), r(,5), r(,) };
char c[2][6] = { str(hello), str() };

приводит к

f(2 * (y+1)) + f(2 * (f(2 * (z[0])))) % f(2 * (0)) + t(1);
f(2 * (2+(3,4)-0,1)) | f(2 * (~ 5)) & f(2 * (0,1))^m(0,1);
int i[] = { 1, 23, 4, 5, };
char c[2][6] = { "hello", "" };

end example]

[ Example: Чтобы проиллюстрировать правила создания символьных строковых литералов и конкатенации токенов, последовательность

#define str(s)      # s
#define xstr(s)     str(s)
#define debug(s, t) printf("x" # s "= %d, x" # t "= %s", \
               x ## s, x ## t)
#define INCFILE(n)  vers ## n
#define glue(a, b)  a ## b
#define xglue(a, b) glue(a, b)
#define HIGHLOW     "hello"
#define LOW         LOW ", world"

debug(1, 2);
fputs(str(strncmp("abc\0d", "abc", '\4')        // this goes away
    == 0) str(: @\n), s);
#include xstr(INCFILE(2).h)
glue(HIGH, LOW);
xglue(HIGH, LOW)

приводит к

printf("x" "1" "= %d, x" "2" "= %s", x1, x2);
fputs("strncmp(\"abc\\0d\", \"abc\", '\\4') == 0" ": @\n", s);
#include "vers2.h"      (after macro replacement, before file access)
"hello";
"hello" ", world"

или после конкатенации символьных строковых литералов,

printf("x1= %d, x2= %s", x1, x2);
fputs("strncmp(\"abc\\0d\", \"abc\", '\\4') == 0: @\n", s);
#include "vers2.h"      (after macro replacement, before file access)
"hello";
"hello, world"

Пространство вокруг # и ## лексемы в определении макроса не является обязательным. ]end example

[ Example: Чтобы проиллюстрировать правила для маркеров предварительной обработки меток, последовательность

#define t(x,y,z) x ## y ## z
int j[] = { t(1,2,3), t(,4,5), t(6,,7), t(8,9,),
  t(10,,), t(,11,), t(,,12), t(,,) };

приводит к

int j[] = { 123, 45, 67, 89,
  10, 11, 12, };

end example]

[ Example: Чтобы продемонстрировать правила переопределения, допустима следующая последовательность.

#define OBJ_LIKE      (1-1)
#define OBJ_LIKE      /* white space */ (1-1) /* other */
#define FUNC_LIKE(a)   ( a )
#define FUNC_LIKE( a )(     /* note the white space */ \
                a /* other stuff on this line
                  */ )

Но следующие переопределения недействительны:

#define OBJ_LIKE    (0)         // different token sequence
#define OBJ_LIKE    (1 - 1)     // different white space
#define FUNC_LIKE(b) ( a )      // different parameter usage
#define FUNC_LIKE(b) ( b )      // different parameter spelling

end example]

[ Example: Наконец, чтобы показать возможности макроса списка переменных аргументов:

#define debug(...) fprintf(stderr, __VA_­ARGS__)
#define showlist(...) puts(#__VA_­ARGS__)
#define report(test, ...) ((test) ? puts(#test) : printf(__VA_­ARGS__))
debug("Flag");
debug("X = %d\n", x);
showlist(The first, second, and third items.);
report(x>y, "x is %d but y is %d", x, y);

приводит к

fprintf(stderr, "Flag");
fprintf(stderr, "X = %d\n", x);
puts("The first, second, and third items.");
((x>y) ? puts("x>y") : printf("x is %d but y is %d", x, y));

end example]