Spec-Zone.ru › GCC 5 CPP

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");

Вышеизложенное объяснение неоднозначно в случае, когда единственным параметром макроса является параметр переменных аргументов, так как бессмысленно пытаться отличить, является ли отсутствие аргументов пустым аргументом или отсутствующим аргументом. В этом случае стандарт C99 чётко указывает, что запятая должна остаться, однако существующее расширение GCC использовалось для проглатывания запятой. Поэтому CPP сохраняет запятую при соответствии определённому стандарту C и опускает её в противном случае.

C99 предписывает, что идентификатор __VA_ARGS__ может появляться только в списке замены макроса с переменным числом аргументов. Его нельзя использовать в качестве имени макроса, имени аргумента макроса или внутри макроса другого типа. Он также может быть запрещён в открытом тексте; стандарт неоднозначен. Мы рекомендуем избегать его использования, кроме его определённого назначения.

Макросы с переменным числом аргументов являются новой функцией в C99. GNU CPP поддерживает их уже давно, но только с именованным переменным аргументом (‘args…’, а не ‘…’ и __VA_ARGS__). Если вы обеспокоены переносимостью на предыдущие версии GCC, вы должны использовать только именованные переменные аргументы. С другой стороны, если вы обеспокоены переносимостью на другие соответствующие реализации C99, вы должны использовать только __VA_ARGS__.

Предыдущие версии CPP реализовывали расширение удаления запятой гораздо более широко. Мы ограничили его в этом выпуске, чтобы свести к минимуму различия со стандартом C99. Чтобы получить тот же эффект как в этой, так и в предыдущих версиях GCC, токен, предшествующий специальному ‘##’, должен быть запятой, и между этой запятой и тем, что непосредственно перед ней, должно быть пробельное пространство:

#define eprintf(format, args…) fprintf (stderr, format , ##args)

См. Различия с предыдущими версиями, для подробностей.

Далее: Предопределённые макросы, Предыдущий: Конкатенация, Вверх: Макросы [Оглавление][Индекс]

© Free Software Foundation
Licensed under the GNU Free Documentation License, Version 1.3.
https://gcc.gnu.org/onlinedocs/gcc-5.5.0/cpp/Variadic-Macros.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API