Пространства имён
Варианты
Действия

Замена текстовых макросов

Материал из cppreference.com
 
 
Язык С++
Общие темы
Управление потоком
Операторы условного выполнения
Операторы итерации (циклы)
Операторы перехода
Функции
Объявление функции
Выражение лямбда-функции
Спецификатор inline
Спецификации динамических исключений(до C++17*)
Спецификатор noexcept(C++11)
Исключения
Пространства имён
Типы
Спецификаторы
decltype(C++11)
auto(C++11)
alignas(C++11)
Спецификаторы длительности хранения
Инициализация
Выражения
Альтернативные представления
Литералы
Логические - Целочисленные - С плавающей запятой
Символьные - Строковые - nullptr(C++11)
Определяемые пользователем(C++11)
Утилиты
Атрибуты(C++11)
Types
Объявление typedef
Объявление псевдонима типа(C++11)
Casts
Неявные преобразования - Явные преобразования
static_cast - dynamic_cast
const_cast - reinterpret_cast
Выделение памяти
Классы
Свойства функции класса
Специальные функции-элементы
Шаблоны
Разное
 
 

Препроцессор поддерживает замену текстового макроса. Также поддерживается замена текстового макроса подобного функции.

Содержание

[править]Синтаксис

#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) список-замен может содержать последовательность токенов __VA_OPT__ (контент), которая заменяется на контент, если __VA_ARGS__ не пусто, и в противном случае ничего не расширяется.

#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)

В противном случае поведение не определено.

[править]Операторы # и ##

В макросах, подобных функциям, оператор # перед идентификатором в списке-замен запускает идентификатор через замену параметра и заключает результат в кавычки, фактически создавая строковый литерал. Кроме того, препроцессор добавляет обратную косую черту, чтобы избежать кавычек, окружающих встроенные строковые литералы, если они есть, и при необходимости удваивает обратную косую черту внутри строки. Все начальные и конечные пробелы удаляются, а любая последовательность пробелов в середине текста (но не внутри встроенных строковых литералов) сжимается до одного пробела. Эта операция называется "стрингификацией". Если результат преобразования в строку не является допустимым строковым литералом, поведение не определено.

Когда # появляется перед __VA_ARGS__, весь развёрнутый __VA_ARGS__ заключается в кавычки:

#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++, расширяется до значения
  • 199711L(до C++11),
  • 201103L(C++11),
  • 201402L(C++14),
  • 201703L(C++17),
  • 202002L(C++20) или
  • 202302L(C++23)
    (макроконстанта)
__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__, определённая как статический массив символов, содержащий имя функции в формате, определяемом реализацией. Это не макрос препроцессора, но он используется вместе с __FILE__ и __LINE__, например макросом assert.

(начиная с 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 по Замена текстовых макросов
close