3.6 Макросы с переменным числом аргументов
Макрос может быть объявлен для приема переменного числа аргументов, так же, как и функция. Синтаксис определения макроса аналогичен синтаксису функции. Вот пример:
#define eprintf(…) fprintf (stderr, __VA_ARGS__)
Этот вид макроса называется макросом с переменным числом аргументов. Когда макрос вызывается, все токены в списке его аргументов после последнего именованного аргумента (в этом макросе их нет), включая запятые, становятся переменным аргументом. Эта последовательность токенов заменяет идентификатор __VA_ARGS__ в теле макроса везде, где он встречается. Таким образом, у нас есть такое расширение:
eprintf ("%s:%d: ", input_file, lineno)
→ fprintf (stderr, "%s:%d: ", input_file, lineno)
Переменный аргумент полностью макрорасширяется перед его вставкой в макрорасширение, так же как и обычный аргумент. Вы можете использовать операторы ‘#’ и ‘##’, чтобы строчить переменный аргумент или склеивать его ведущий или заключительный токен с другим токеном. (Но см. ниже важный частный случай для ‘##’.)
Если ваш макрос сложный, вам может понадобиться более описательное имя для переменного аргумента, чем __VA_ARGS__. CPP разрешает это как расширение. Вы можете написать имя аргумента непосредственно перед ‘…’; это имя используется для переменного аргумента. Макрос eprintf выше можно записать
#define eprintf(args…) fprintf (stderr, args)
используя это расширение. Вы не можете использовать __VA_ARGS__ и это расширение в одном макросе.
Вы можете иметь именованные аргументы, а также переменные аргументы в макросе с переменным числом аргументов. Мы могли бы определить eprintf так:
#define eprintf(format, …) fprintf (stderr, format, __VA_ARGS__)
Эта формулировка выглядит более описательно, но, к сожалению, она менее гибкая: теперь вы должны предоставить по меньшей мере один аргумент после строки формата. В стандартном C вы не можете опустить запятую, разделяющую именованный аргумент и переменные аргументы. Кроме того, если вы оставите переменный аргумент пустым, вы получите синтаксическую ошибку, потому что после строки формата будет лишняя запятая.
eprintf("success!\n", );
→ fprintf(stderr, "success!\n", );
GNU CPP имеет пару расширений, которые решают эту проблему. Во-первых, вам разрешается полностью опустить переменный аргумент:
eprintf ("success!\n")
→ fprintf(stderr, "success!\n", );
Во-вторых, оператор склейки токенов ‘##’ имеет специальное значение, когда он расположен между запятой и переменным аргументом. Если вы напишете
#define eprintf(format, …) fprintf (stderr, format, ##__VA_ARGS__)
и переменный аргумент опущен при использовании макроса eprintf, то запятая перед ‘##’ будет удалена. Это не происходит, если вы передаете пустой аргумент, а также не происходит, если токен перед ‘##’ является чем-то иным, чем запятая.
eprintf ("success!\n")
→ fprintf(stderr, "success!\n");
Вышеизложенное объяснение является неоднозначным в случае, когда единственным параметром макроса является параметр переменных аргументов, так как бессмысленно пытаться различить, является ли отсутствие аргументов вообще пустым аргументом или отсутствующим аргументом. CPP сохраняет запятую при соответствию конкретному стандарту C. В противном случае запятая отбрасывается как расширение стандарта.
Стандарт C предписывает, что единственное место, где может появиться идентификатор __VA_ARGS__ , находится в списке замен макроса с переменным числом аргументов. Его нельзя использовать в качестве имени макроса, имени аргумента макроса или внутри другого типа макроса. Он также может быть запрещен в открытом тексте; стандарт неоднозначен. Мы рекомендуем избегать его использования, кроме как для его определенной цели.
Макросы с переменным числом аргументов стали стандартной частью языка C с C99. GNU CPP ранее поддерживал их с именованным переменным аргументом (‘args…’, а не ‘…’ и __VA_ARGS__), что по-прежнему поддерживается для обратной совместимости.
Далее: Предопределенные макросы, Предыдущий: Конкатенация, Наверх: Макросы [Содержание][Индекс]
© Free Software Foundation
Licensed under the GNU Free Documentation License, Version 1.3.
https://gcc.gnu.org/onlinedocs/gcc-7.5.0/cpp/Variadic-Macros.html