Замена текстовых макросов
Препроцессор поддерживает замену текстового макроса. Также поддерживается замена текстового макроса подобного функции.
Содержание |
[править]Синтаксис
#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)// заменяется на f(0, a, b, c) F()// заменяется на f(0) #define G(X, ...) f(0, X __VA_OPT__(,) __VA_ARGS__) G(a, b, c)// заменяется на f(0, a, b, c) G(a, )// заменяется на f(0, a) G(a)// заменяется на f(0, a) #define SDEF(sname, ...) S sname __VA_OPT__(= { __VA_ARGS__ }) SDEF(foo);// заменяется на S foo; SDEF(bar, 1, 2);// заменяется на 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();// расширяется до puts("") showlist(1, "x", int);// расширяется до 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__ | расширяется до даты трансляции, символьный строковый литерал в форме "Ммм дд гггг". Первый символ "дд" пробел, если день месяца меньше 10. Название месяца как будто сгенерировано std::asctime() (макроконстанта) |
__TIME__ | расширяется до времени трансляции, символьный строковый литерал в форме "чч:мм:сс" (макроконстанта) |
__STDCPP_DEFAULT_NEW_ALIGNMENT__ (начиная с C++17) | расширяется до литерала std::size_t, значение которого является выравниванием, гарантируемым вызовом неосведомлёного о выравнивании оператора 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) | расширяется до целочисленной константы в форме ггггммL , если wchar_t использует Юникод, дата указывает последнюю версию поддерживаемого Юникода (макроконстанта) |
__STDC_MB_MIGHT_NEQ_WC__ (C++11) | расширяется до 1, если 'x'== L'x' может быть ложным для элемента базового набора символов, например, в системах на основе EBCDIC, которые используют Юникод для wchar_t. (макроконстанта) |
__STDCPP_THREADS__ (начиная с C++11) | расширяется до 1, если программа может иметь более одного потока выполнения (макроконстанта) |
__STDCPP_STRICT_POINTER_SAFETY__ (начиная с C++11)(удалено в C++23) | расширяется до 1, если в реализации есть строгие std::pointer_safety (макроконстанта) |
Значения этих макросов (кроме __FILE__
и __LINE__
) остаются постоянными во всей единице трансляции. Попытки переопределить или отменить определение этих макросов приводят к неопределённому поведению.
Примечание: в области видимости каждого тела функции есть специальная локальная предопределённая переменная с именем __func__, определённая как статический массив символов, содержащий имя функции в формате, определяемом реализацией. Это не макрос препроцессора, но он используется вместе с | (начиная с C++11) |
Макросы тестирования особенностей языкаСтандарт определяет набор макросов препроцессора, соответствующих особенностям языка C++, представленным в C++11 или новее. Они предназначены как простой и портируемый способ обнаружения наличия указанных особенностей. Смотрите тестирование функциональности для подробностей. | (начиная с C++20) |
[править]Пример
#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 << "вывод: " #a << '\n' // Использование макроса в определении более позднего макроса#define WORD "Привет "#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(миллион);//обратите внимание на отсутствие кавычек std::cout<< OUTER(Мир)<<'\n';std::cout<< OUTER(WORD Мир)<<'\n';}
Вывод:
abcd: 12 fff: 2 qqq: 23 34 вывод: миллион Привет Мир Привет WORD Мир
[править]Отчёты о дефектах
Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:
Номер | Применён | Поведение в стандарте | Корректное поведение |
---|---|---|---|
LWG 294 | C++98 | единица трансляции, включающая заголовок стандартной библиотеки, может содержать макросы, определяющие имена, объявленные в заголовках других стандартных библиотек | запрещено |
WG не указан | C++23 | универсальные имена символов не могли образовываться путём конкатенации токенов | позволено |
[править]Смотрите также
Документация C++ по Указатель Макро Символов | |
Документация C по Замена текстовых макросов |