Replacing text macros
Препроцессор поддерживает текстовую макроподстановку. Также поддерживается функциональная текстовая макроподстановка.
Содержание |
Синтаксис
#define
идентификатор список-замены
(необязательно)
|
(1) | ||||||||
#define
идентификатор
(
параметры
)
список-замены
(необязательно)
|
(2) | ||||||||
#define
идентификатор
(
параметры
, ...)
список-замены
(необязательно)
|
(3) | (начиная с C++11) | |||||||
#define
идентификатор
(...)
список-замены
(необязательно)
|
(4) | (начиная с C++11) | |||||||
#undef
идентификатор
|
(5) | ||||||||
Объяснение
#define директивы
Директива #define определяет идентификатор как макрос, то есть предписывает компилятору заменять большинство последовательных вхождений идентификатора на список-замены , который будет дополнительно обработан. Исключения возникают из правил сканирования и замены . Если идентификатор уже определён как макрос любого типа, программа является некорректной, если определения не идентичны.
Макросы, подобные объектам
Объектоподобные макросы заменяют каждое вхождение определённого identifier на replacement-list . Версия (1) директивы #define работает именно таким образом.
Макросы, подобные функциям
Функциональные макросы заменяют каждое вхождение определенного идентификатора на список-замены , дополнительно принимая ряд аргументов, которые затем заменяют соответствующие вхождения любых параметров в списке-замены .
Синтаксис вызова макроса, подобного функции, аналогичен синтаксису вызова функции: каждое вхождение имени макроса, за которым следует ( в качестве следующего токена препроцессора, вводит последовательность токенов, которая заменяется replacement-list . Последовательность завершается соответствующим токеном ) , пропуская промежуточные парные левые и правые скобки.
Для версии (2) количество аргументов должно совпадать с количеством параметров в определении макроса. Для версий (3,4) количество аргументов должно быть не меньше количества параметров (
не
(начиная с C++20)
учитывая
...
). В противном случае программа является некорректной. Если идентификатор не находится в функциональной нотации, т.е. не имеет скобок после себя, он вообще не заменяется.
Версия (2) директивы #define определяет простой функциональный макрос.
Версия (3) директивы
#define
определяет функционально-подобный макрос с переменным количеством аргументов. Дополнительные аргументы (называемые
переменными аргументами
) могут быть доступны с использованием идентификатора
__VA_ARGS__
, который затем заменяется на аргументы, переданные вместе с идентификатором для замены.
Директива
#define
версии (4) определяет функционально-подобный макрос с переменным количеством аргументов, но без обычных аргументов. Аргументы (называемые
переменными аргументами
) могут быть доступны только с помощью идентификатора
__VA_ARGS__
, который затем заменяется на аргументы, переданные вместе с идентификатором для замены.
|
Для версий (3,4),
replacement-list
может содержать последовательность токенов
#define F(...) f(0 __VA_OPT__(,) __VA_ARGS__) F(a, b, c) // replaced by f(0, a, b, c) F() // replaced by f(0) #define G(X, ...) f(0, X __VA_OPT__(,) __VA_ARGS__) G(a, b, c) // replaced by f(0, a, b, c) G(a, ) // replaced by f(0, a) G(a) // replaced by f(0, a) #define SDEF(sname, ...) S sname __VA_OPT__(= { __VA_ARGS__ }) SDEF(foo); // replaced by S foo; SDEF(bar, 1, 2); // replaced by S bar = { 1, 2 }; |
(since C++20) |
Примечание: если аргумент макроса, подобного функции, содержит запятые, не защищённые парными скобками (чаще всего встречается в списках аргументов шаблонов, как в assert ( std:: is_same_v < int , int > ) ; или BOOST_FOREACH ( std:: pair < int , int > p, m ) ), запятая интерпретируется как разделитель аргументов макроса, что приводит к ошибке компиляции из-за несоответствия количества аргументов.
Сканирование и Замена
- Сканирование отслеживает макросы, которые были заменены. Если сканирование находит текст, соответствующий такому макросу, оно помечает его как «игнорируемый» (все последующие сканирования будут его игнорировать). Это предотвращает рекурсию.
- Если сканирование обнаруживает функциональный макрос, аргументы сканируются перед помещением в replacement-list . За исключением случаев, когда операторы # и ## принимают аргумент без сканирования.
- После замены макроса результирующий текст сканируется.
Обратите внимание, что возможно определить псевдорекурсивный макрос:
#define EMPTY #define SCAN(x) x #define EXAMPLE_() EXAMPLE #define EXAMPLE(n) EXAMPLE_ EMPTY()(n-1) (n) EXAMPLE(5) SCAN(EXAMPLE(5))
Вывод:
EXAMPLE_ ()(5 -1) (5) EXAMPLE_ ()(5 -1 -1) (5 -1) (5)
Зарезервированные имена макросов
Единица трансляции, которая включает заголовок стандартной библиотеки , не должна #define или #undef имена, объявленные в любом заголовке стандартной библиотеки .
Трансляционная единица, использующая любую часть стандартной библиотеки, не должна #define или #undef имена, лексически идентичные:
|
(начиная с C++11) |
В противном случае поведение не определено.
# и ## операторы
В макросах, подобных функциям, оператор # перед идентификатором в replacement-list выполняет замену параметров и заключает результат в кавычки, фактически создавая строковый литерал. Кроме того, препроцессор добавляет обратные слеши для экранирования кавычек, окружающих встроенные строковые литералы (если они есть), и удваивает обратные слеши внутри строки при необходимости. Все начальные и конечные пробелы удаляются, а любая последовательность пробелов в середине текста (но не внутри встроенных строковых литералов) сокращается до одного пробела. Эта операция называется "стрингификацией". Если результат стрингификации не является корректным строковым литералом, поведение не определено.
|
Когда
#
появляется перед
#define showlist(...) puts(#__VA_ARGS__) showlist(); // expands to puts("") showlist(1, "x", int); // expands to puts("1, \"x\", int") |
(since C++11) |
Оператор
##
между любыми двумя последовательными идентификаторами в
replacement-list
выполняет замену параметров на два идентификатора (которые сначала не макроподставляются), а затем объединяет результат. Эта операция называется "конкатенацией" или "склейкой токенов". Только токены, которые вместе образуют допустимый токен, могут быть склеены: идентификаторы, образующие более длинный идентификатор, цифры, образующие число, или операторы
+
и
=
, образующие
+=
. Комментарий не может быть создан склейкой
/
и
*
, поскольку комментарии удаляются из текста до начала макроподстановки. Если результат конкатенации не является допустимым токеном, поведение не определено.
Примечание: некоторые компиляторы предоставляют расширение, позволяющее
##
появляться после запятой и перед
__VA_ARGS__
, в этом случае
##
ничего не делает при наличии переменных аргументов, но удаляет запятую при отсутствии переменных аргументов: это позволяет определять макросы, такие как
fprintf
(
stderr
, format,
##__VA_ARGS__)
.
Этого также можно достичь стандартным способом с использованием
__VA_OPT__
, например
fprintf
(
stderr
, format __VA_OPT__
(
,
)
__VA_ARGS__
)
.
(since C++20)
#undef директива
Директива #undef отменяет определение идентификатора , то есть аннулирует предыдущее определение идентификатора , заданное директивой #define . Если с идентификатором не связан макрос, данная директива игнорируется.
Предопределенные макросы
Следующие макросы предопределены в каждой единице трансляции:
|
__cplusplus
|
обозначает версию стандарта C++, которая используется, раскрывается в значение
|
|
__STDC_HOSTED__
(C++11)
|
раскрывается в целочисленную константу
1
если реализация является hosted (работает под ОС),
0
если freestanding (работает без ОС)
(макроконстанта) |
|
__FILE__
|
раскрывается в имя текущего файла, как строковый литерал, может быть изменено директивой
#line
(макроконстанта) |
|
__LINE__
|
раскрывается в номер строки текущей
физической исходной строки
, целочисленная константа, может быть изменено директивой
#line
(макроконстанта) |
|
__DATE__
|
раскрывается в дату трансляции, строковый литерал вида
"Mmm dd yyyy"
. Первый символ
"dd"
является пробелом, если день месяца меньше 10. Название месяца такое же, как генерируется
std::
asctime
(
)
(макроконстанта) |
|
__TIME__
|
раскрывается во время трансляции, строковый литерал вида
"hh:mm:ss"
(макроконстанта) |
|
__STDCPP_DEFAULT_NEW_ALIGNMENT__
(C++17)
|
раскрывается в
std::size_t
литерал, значение которого является выравниванием, гарантированным вызовом alignment-unaware
operator new
(большие выравнивания будут переданы в alignment-aware перегрузку, такую как
operator new
(
std::
size_t
,
std::
align_val_t
)
)
(макроконстанта) |
|
__STDCPP_BFLOAT16_T__
__STDCPP_FLOAT16_T__
__STDCPP_FLOAT32_T__
__STDCPP_FLOAT64_T__
__STDCPP_FLOAT128_T__
(C++23)
|
раскрывается в
1
тогда и только тогда, когда реализация поддерживает соответствующий
расширенный тип с плавающей точкой
(макроконстанта) |
|
__STDC_EMBED_NOT_FOUND__
__STDC_EMBED_FOUND__
__STDC_EMBED_EMPTY__
(C++26)
|
раскрывается в
0
,
1
и
2
соответственно
(макроконстанта) |
Следующие дополнительные имена макросов могут быть предопределены реализациями:
|
__STDC__
|
определяемая реализацией величина, если присутствует, обычно используется для указания соответствия стандарту C
(макроконстанта) |
||||
|
__STDC_VERSION__
(C++11)
|
определяемая реализацией величина, если присутствует
(макроконстанта) |
||||
|
__STDC_ISO_10646__
(C++11)
|
(макроконстанта) |
||||
|
__STDC_MB_MIGHT_NEQ_WC__
(C++11)
|
раскрывается в
1
если
'x'
==
L
'x'
может быть ложным для элемента
x
базового набора символов, например, в системах на базе EBCDIC, которые используют Unicode для
wchar_t
(макроконстанта) |
||||
|
__STDCPP_THREADS__
(C++11)
|
раскрывается в
1
если программа может иметь более одного потока выполнения
(макроконстанта) |
|
__STDCPP_STRICT_POINTER_SAFETY__
(C++11)
(удалено в C++23)
|
раскрывается в
1
если реализация имеет строгую
std::pointer_safety
(макроконстанта) |
Значения этих макросов (за исключением
__FILE__
и
__LINE__
) остаются постоянными на протяжении всей единицы трансляции. Попытки переопределить или отменить определение этих макросов приводят к неопределённому поведению.
Макросы для проверки языковых возможностейСтандарт определяет набор препроцессорных макросов, соответствующих возможностям языка C++, введённым в C++11 или позднее. Они предназначены для простого и переносимого способа обнаружения наличия указанных возможностей. См. Тестирование возможностей для подробностей. |
(начиная с C++20) |
ПримечанияПредопределенная переменная __func__ не является предопределенным макросом, но обычно используется вместе с __FILE__ и __LINE__ , например, в assert . |
(since C++11) |
Пример
#include <iostream> // Создать фабрику функций и использовать её #define FUNCTION(name, a) int fun_##name() { return a; } FUNCTION(abcd, 12) FUNCTION(fff, 2) FUNCTION(qqq, 23) #undef FUNCTION #define FUNCTION 34 #define OUTPUT(a) std::cout << "output: " #a << '\n' // Использование макроса в определении последующего макроса #define WORD "Hello " #define OUTER(...) WORD #__VA_ARGS__ int main() { std::cout << "abcd: " << fun_abcd() << '\n'; std::cout << "fff: " << fun_fff() << '\n'; std::cout << "qqq: " << fun_qqq() << '\n'; std::cout << FUNCTION << '\n'; OUTPUT(million); //обратите внимание на отсутствие кавычек std::cout << OUTER(World) << '\n'; std::cout << OUTER(WORD World) << '\n'; }
Вывод:
abcd: 12 fff: 2 qqq: 23 34 output: million Hello World Hello WORD World
Отчеты о дефектах
Следующие отчеты об изменениях в поведении, являющиеся дефектными, были применены ретроактивно к ранее опубликованным стандартам C++.
| DR | Применяется к | Поведение как опубликовано | Корректное поведение |
|---|---|---|---|
| CWG 2908 | C++98 |
было неясно, расширяется ли
__LINE__
до текущего
номера физической строки или текущего номера логической строки |
расширяется до текущего
номера физической строки |
| LWG 294 | C++98 |
единица трансляции, включающая заголовок стандартной библиотеки, могла содержать
макросы, определяющие имена, объявленные в других заголовках стандартной библиотеки |
запрещено |
| P2621R2 | C++23 |
универсальные символьные имена не разрешались
формировать с помощью конкатенации токенов |
разрешено |
Смотрите также
|
C++ documentation
для
Macro Symbol Index
|
|
|
C documentation
для
Replacing text macros
|