Replacing text macros
Препроцессор поддерживает текстовую макроподстановку. Также поддерживается функциональная текстовая макроподстановка.
Содержание
Синтаксис
Объяснение
#define директивыДиректива #define определяет идентификатор как макрос, то есть предписывает компилятору заменять большинство последовательных вхождений идентификатора на список-замены , который будет дополнительно обработан. Исключения возникают из правил сканирования и замены . Если идентификатор уже определён как макрос любого типа, программа является некорректной, если определения не идентичны. Макросы, подобные объектамОбъектоподобные макросы заменяют каждое вхождение определённого identifier на replacement-list . Версия (1) директивы #define работает именно таким образом. Макросы, подобные функциямФункциональные макросы заменяют каждое вхождение определенного идентификатора на список-замены , дополнительно принимая ряд аргументов, которые затем заменяют соответствующие вхождения любых параметров в списке-замены . Синтаксис вызова макроса, подобного функции, аналогичен синтаксису вызова функции: каждое вхождение имени макроса, за которым следует ( в качестве следующего токена препроцессора, вводит последовательность токенов, которая заменяется replacement-list . Последовательность завершается соответствующим токеном ) , пропуская промежуточные парные левые и правые скобки.
Для версии (2) количество аргументов должно совпадать с количеством параметров в определении макроса. Для версий (3,4) количество аргументов должно быть не меньше количества параметров (
не
(начиная с C++20)
учитывая
Версия (2) директивы #define определяет простой функциональный макрос.
Версия (3) директивы
#define
определяет функционально-подобный макрос с переменным количеством аргументов. Дополнительные аргументы (называемые
переменными аргументами
) могут быть доступны с использованием идентификатора
Директива
#define
версии (4) определяет функционально-подобный макрос с переменным количеством аргументов, но без обычных аргументов. Аргументы (называемые
переменными аргументами
) могут быть доступны только с помощью идентификатора
Примечание: если аргумент макроса, подобного функции, содержит запятые, не защищённые парными скобками (чаще всего встречается в списках аргументов шаблонов, как в 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 имена, лексически идентичные:
В противном случае поведение не определено.
# и ## операторыВ макросах, подобных функциям, оператор # перед идентификатором в replacement-list выполняет замену параметров и заключает результат в кавычки, фактически создавая строковый литерал. Кроме того, препроцессор добавляет обратные слеши для экранирования кавычек, окружающих встроенные строковые литералы (если они есть), и удваивает обратные слеши внутри строки при необходимости. Все начальные и конечные пробелы удаляются, а любая последовательность пробелов в середине текста (но не внутри встроенных строковых литералов) сокращается до одного пробела. Эта операция называется "стрингификацией". Если результат стрингификации не является корректным строковым литералом, поведение не определено.
Оператор
##
между любыми двумя последовательными идентификаторами в
replacement-list
выполняет замену параметров на два идентификатора (которые сначала не макроподставляются), а затем объединяет результат. Эта операция называется "конкатенацией" или "склейкой токенов". Только токены, которые вместе образуют допустимый токен, могут быть склеены: идентификаторы, образующие более длинный идентификатор, цифры, образующие число, или операторы
Примечание: некоторые компиляторы предоставляют расширение, позволяющее
##
появляться после запятой и перед
#undef директиваДиректива #undef отменяет определение идентификатора , то есть аннулирует предыдущее определение идентификатора , заданное директивой #define . Если с идентификатором не связан макрос, данная директива игнорируется. Предопределённые макросыСледующие имена макросов предопределены в каждой единице трансляции:
Следующие дополнительные имена макросов могут быть предопределены реализациями:
Значения этих макросов (кроме
Пример
Запустить этот код
#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++.
Смотрите также
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||