Замена текстовых макросов
Предпроцессор поддерживает замену текстовых макросов. Также поддерживается замена макросов с поведением, подобным функциям.
Синтаксис
#define идентификатор список-замены (необязательно) | (1) | |
#define идентификатор (параметры ) список-замены (необязательно) | (2) | |
#define идентификатор (параметры , ...) список-замены (необязательно) | (3) | (с C++11) |
#define идентификатор (...) список-замены (необязательно) | (4) | (с C++11) |
#undef идентификатор | (5) |
Объяснение
Директивы #define
Директивы #define определяют идентификатор как макрос, то есть инструктируют компилятор заменить все последующие вхождения идентификатора на список-замены, который будет дополнительно обработан. Исключение составляют правила сканирования и замены. Если идентификатор уже определен как макрос любого типа, программа считается ошибочной, если определения не идентичны.
Макросы-объекты
Макросы-объекты заменяют каждое вхождение определенного идентификатора на список-замены. Версия (1) директивы #define ведет себя точно так же.
Макросы-функции
Макросы-функции заменяют каждое вхождение определенного идентификатора на список-замены, дополнительно принимая некоторое количество аргументов, которые затем заменяют соответствующие вхождения параметров в списке-замен.
Синтаксис вызова макроса-функции аналогичен синтаксису вызова функции: каждое вхождение имени макроса, за которым следует (, как следующий препроцессорный токен, вводит последовательность токенов, которая заменяется списком-замен. Последовательность завершается соответствующим токеном ), пропуская промежуточные сопоставленные пары левой и правой скобок.
Для версии (2) количество аргументов должно совпадать с количеством параметров в определении макроса. Для версий (3,4) количество аргументов не должно быть меньше количества параметров (не(с C++20) считая ...). В противном случае программа считается ошибочной. Если идентификатор не имеет функциональной нотации, то есть после него нет скобок, он не заменяется вообще.
Версия (2) директивы #define определяет простой макрос-функцию.
Версия (3) директивы #define определяет макрос-функцию с переменным количеством аргументов. Дополнительные аргументы (называемые переменными аргументами) можно получить, используя __VA_ARGS__ идентификатор, который затем заменяется аргументами, предоставленными с идентификатором, подлежащим замене.
Версия (4) директивы #define определяет макрос-функцию с переменным количеством аргументов, но без обычных аргументов. К аргументам (называемым переменными аргументами) можно получить доступ только с помощью __VA_ARGS__ идентификатора, который затем заменяется аргументами, предоставленными с идентификатором, подлежащим замене.
| Для версий (3,4), список-замен может содержать последовательность токенов #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)), запятая интерпретируется как разделитель аргументов макроса, что приводит к ошибке компиляции из-за несоответствия количества аргументов.
Сканирование и замена
- Сканирование отслеживает макросы, которые они заменили. Если сканирование находит текст, соответствующий такому макросу, оно помечает его как "для игнорирования" (все сканирования будут его игнорировать). Это предотвращает рекурсию.
- Если сканирование находит макрос-функцию, аргументы сканируются перед их размещением внутри списка-замен. За исключением операторов # и ##, которые берут аргумент без сканирования.
- После замены макроса, результирующий текст сканируется.
Обратите внимание, что возможно определить псевдорекурсивный макрос:
#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) |
В противном случае поведение неопределено.
Операторы # и ##
В макросах-функциях оператор # перед идентификатором в списке-замен обрабатывает идентификатор с заменой параметров и помещает результат в кавычки, фактически создавая строковый литерал. Кроме того, предпроцессор добавляет обратные слэши для экранирования кавычек, окружающих вложенные строковые литералы, если таковые имеются, и удваивает обратные слэши внутри строки по мере необходимости. Все начальные и конечные пробелы удаляются, а любая последовательность пробелов посередине текста (но не внутри вложенных строковых литералов) сводится к одному пробелу. Эта операция называется "строковой интерпретацией". Если результат строковой интерпретации не является допустимым строковым литералом, поведение неопределено.
| Когда # появляется перед #define showlist(...) puts(#__VA_ARGS__)
showlist(); // expands to puts("")
showlist(1, "x", int); // expands to puts("1, \"x\", int") | (с C++11) |
Оператор ## между любыми двумя последовательными идентификаторами в списке-замен выполняет замену параметров для двух идентификаторов (которые не расширяются макросом предварительно) и затем соединяет результат. Эта операция называется "конкатенацией" или "склеиванием токенов". Могут склеиваться только токены, образующие вместе допустимый токен: идентификаторы, образующие более длинный идентификатор, цифры, образующие число, или операторы + и = образующие +=. Комментарий не может быть создан путем склеивания / и *, потому что комментарии удаляются из текста до того, как рассматривается замена макроса. Если результат конкатенации не является допустимым токеном, поведение неопределено.
Примечание: некоторые компиляторы предлагают расширение, которое позволяет ## появляться после запятой и перед __VA_ARGS__, в этом случае ## ничего не делает при наличии переменных аргументов, но удаляет запятую, когда переменные аргументы отсутствуют: это позволяет определять макросы, такие как fprintf (stderr, format, ##__VA_ARGS__). Это также можно достичь стандартным способом, используя __VA_OPT__, например fprintf (stderr, format __VA_OPT__(, ) __VA_ARGS__).(с C++20)
Директива #undef
Директива #undef аннулирует идентификатор, то есть отменяет предыдущее определение идентификатора директивой #define. Если идентификатор не имеет связанного макроса, директива игнорируется.
Предопределенные макросы
Следующие имена макросов предопределены в каждой единице трансляции:
| __cplusplus | обозначает используемую версию стандарта C++, расширяется до значения
|
| __STDC_HOSTED__
(C++11) | расширяется до целочисленной константы 1 , если реализация размещена (работает под ОС), 0 , если выполняется в автономном режиме (без ОС) (макро-константа) |
| __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)) (макро-константа) |
| __STDCPP_BFLOAT16_T____STDCPP_FLOAT16_T____STDCPP_FLOAT32_T____STDCPP_FLOAT64_T____STDCPP_FLOAT128_T__
(C++23) | расширяется до 1 только в том случае, если реализация поддерживает соответствующий расширенный тип плавающей точки (макро-константа) |
Следующие дополнительные имена макросов могут быть предварительно определены реализациями:
| __STDC__ | значение, определённое реализацией, которое обычно используется для указания соответствия стандарту C (макро-константа) |
||||
| __STDC_VERSION__
(C++11) | значение, определённое реализацией (макро-константа) |
||||
| __STDC_ISO_10646__
(C++11) |
(макро-константа) |
||||
| __STDC_MB_MIGHT_NEQ_WC__
(C++11) | расширяется до 1, если 'x' == L'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++11) |
Макросы для тестирования языковых функцийСтандарт определяет набор макросов препроцессора, соответствующих языковым функциям C++, представленным в C++11 или более поздних версиях. Они предназначены для простого и портативного обнаружения наличия этих функций. См. Тестирование функций для получения подробной информации. | (с C++20) |
Пример
#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 | Применено к | Поведение, как опубликовано | Корректное поведение |
|---|---|---|---|
| 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/w/cpp/preprocessor/replace