Замена текстовых макросов
Препроцессор поддерживает замену текстовых макросов. Также поддерживается замена текстовых макросов, подобных функциям.
Синтаксис
#define identifier replacement-list (необязательно) | (1) | |
#define identifier (parameters ) replacement-list (необязательно) | (2) | |
#define identifier (parameters , ...) replacement-list (необязательно) | (3) | (начиная с C++11) |
#define identifier (...) replacement-list (необязательно) | (4) | (начиная с C++11) |
#undef identifier | (5) |
Пояснение
Директивы #define
Директивы #define определяют identifier как макрос, то есть указывают компилятору заменять большинство последующих вхождений identifier на replacement-list, который затем будет обработан дополнительно. Исключения определяются правилами сканирования и замены.
Если identifier уже определён как макрос другой директивой #defined, программа некорректна, если только определения не идентичны.
Если identifier является именем предопределённого макроса (см. ниже) или идентификатором defined, поведение не определено(до C++26)программа некорректна(начиная с C++26).
Макросы, подобные объектам
Макросы, подобные объектам, заменяют каждое вхождение определённого identifier на replacement-list. Версия (1) директивы #define работает именно так.
Макросы, подобные функциям
Макросы, подобные функциям, заменяют каждое вхождение определённого identifier на replacement-list, дополнительно принимая несколько аргументов, разделённых запятыми; эти аргументы затем заменяют соответствующие вхождения любых parameters в replacement-list. Если аргументы содержат директиву препроцессора, поведение не определено(до C++26)программа некорректна(начиная с C++26).
Синтаксис вызова макроса, подобного функции, похож на синтаксис вызова функции: каждое вхождение имени макроса, за которым следующим токеном препроцессора следует (, вводит последовательность токенов препроцессора, заменяемую на replacement-list. Последовательность завершается соответствующим токеном препроцессора ); при этом пропускаются промежуточные пары соответствующих левых и правых круглых скобок.
Для версии (2) число аргументов должно совпадать с числом параметров в определении макроса. Для версий (3,4) число аргументов не должно быть меньше числа параметров (не(начиная с C++20) считая ...). В противном случае программа некорректна. Если идентификатор не используется в функциональной нотации, то есть после него нет круглых скобок, он вообще не заменяется.
Версия (2) директивы #define определяет простой макрос, подобный функции.
Версия (3) директивы #define определяет макрос, подобный функции, с переменным числом аргументов. Доступ к дополнительным аргументам (называемым переменными аргументами) осуществляется с помощью идентификатора __VA_ARGS__, который затем заменяется аргументами, указанными вместе с заменяемым идентификатором.
Версия (4) директивы #define определяет макрос, подобный функции, с переменным числом аргументов, но без обычных аргументов. Доступ к аргументам (называемым переменными аргументами) осуществляется только с помощью идентификатора __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 };
| (начиная с 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, заменяет параметр в этом идентификаторе и заключает результат в кавычки, фактически создавая строковый литерал без префикса.
Назовём стрингифицируемым аргументом последовательность токенов препроцессора, полученную после расширения идентификатора. Если стрингифицируемый аргумент пуст, результатом является пустой строковый литерал. В противном случае:
- Удаляются пробельные символы перед первым токеном препроцессора и после последнего токена препроцессора, входящих в стрингифицируемый аргумент.
- Каждая последовательность пробельных символов между токенами препроцессора стрингифицируемого аргумента становится одним символом пробела в строковом литерале.
- Исходное написание каждого токена препроцессора в стрингифицируемом аргументе сохраняется в строковом литерале, за исключением обработки имён заголовков, символьных литералов и строковых литералов:
- Перед каждым существующим символом
"и символом\вставляется символ обратной косой черты\. - Каждый символ новой строки заменяется двухсимвольной последовательностью
\n.
- Перед каждым существующим символом
Если результат не является допустимым строковым литералом, поведение не определено(до C++26)программа некорректна(начиная с C++26).
| Если перед #define showlist(...) puts(#__VA_ARGS__)
showlist(); // expands to puts("")
showlist(1, "x", int); // expands to puts("1, \"x\", int")
| (начиная с C++11) |
Оператор ##, расположенный между любыми двумя соседними идентификаторами в replacement-list, заменяет параметры в обоих идентификаторах (которые предварительно не раскрываются как макросы), а затем объединяет результат.
Объединение может привести к образованию новых токенов препроцессора: созданию более длинного идентификатора из коротких идентификаторов, образованию числа из цифр или образованию оператора += из операторов + и =. Комментарий нельзя образовать объединением / и *, поскольку комментарии удаляются из текста до подстановки макросов.
Если результат объединения не является допустимым токеном препроцессора, поведение не определено(до C++26)программа некорректна(начиная с C++26).
Директива #undef
Директива #undef отменяет определение identifier, то есть отменяет предыдущее определение identifier директивой #define. Если с идентификатором не связан макрос, директива игнорируется.
Если identifier является именем предопределённого макроса (см. ниже) или идентификатором defined, поведение не определено(до C++26)программа некорректна(начиная с C++26).
Предопределённые макросы
В каждой единице трансляции предопределены следующие имена макросов:
|
__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, значение которого равно выравниванию, гарантируемому вызовом operator new, не учитывающим выравнивание (для больших значений выравнивания будет использоваться перегрузка, учитывающая выравнивание, например operator new(std::size_t, std::align_val_t)). Выравнивание, более строгое, чем требуемое этим макросом, называется расширенным выравниванием для new. (константа макроса) |
|
__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) |
Примечания
Некоторые компиляторы поддерживают расширение, позволяющее ставить ## после запятой и перед __VA_ARGS__; в этом случае ## ничего не делает, если переменные аргументы присутствуют, но удаляет запятую, если они отсутствуют. Это позволяет определять такие макросы, как fprintf (stderr, format, ##__VA_ARGS__). Аналогичного результата можно добиться стандартным способом, используя __VA_OPT__, например fprintf (stderr, format __VA_OPT__(, ) __VA_ARGS__).(начиная с C++20)
| Предопределённая переменная | (начиная с C++11) |
Пример
#include <iostream>
// Make function factory and use it
#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'
// Using a macro in the definition of a later macro
#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); //note the lack of quotes
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++ о указателе символов макросов | |
| Документация по C о замене текстовых макросов |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/preprocessor/replace