Макросы X — это идиоматическое использование макросов языка программирования для генерации списочных структур данных или кода. Они наиболее полезны, когда по крайней мере некоторые из списков не могут быть составлены индексированием, например, во время компиляции . Они обеспечивают надежное обслуживание параллельных списков, соответствующие элементы которых должны быть объявлены или выполнены в том же порядке.
Примерами таких списков являются, в частности, инициализация массивов совместно с декларациями констант перечисления и прототипов функций, генерация последовательностей операторов и переключателей и т. д.
Использование макросов X восходит к 1960-м годам. [1] Они остаются полезными в современных языках программирования C и C++ , но остаются относительно неизвестными. [2] [3]
Приложение X-макроса состоит из двух частей:
Список определяется макросом или заголовочным файлом (называемым LIST
), который сам по себе не генерирует никакого кода, а просто состоит из последовательности вызовов макроса (классически называемого " X
") с данными элементов. Каждому расширению LIST
предшествует определение X
с синтаксисом для элемента списка. Вызов LIST
расширяется X
для каждого элемента в списке.
В этом примере определяется список переменных и автоматически генерируются их объявления и функция для их вывода.
Сначала определение списка. Записи списка могут содержать несколько аргументов, но здесь используется только имя переменной.
#define СПИСОК_ПЕРЕМЕННЫХ \ X(значение1) \ X(значение2) \ X(значение3)
Затем мы расширяем этот список, чтобы сгенерировать объявления переменных:
#define X(имя) int имя; СПИСОК_ПЕРЕМЕННЫХ #undef X
Аналогичным образом мы можем сгенерировать функцию, которая выводит переменные и их значения:
void print_variables ( void ) { #define X(имя) printf("%s = %d\n", #имя, имя); СПИСОК_ПЕРЕМЕННЫХ #undef X }
При запуске через препроцессор C генерируется следующий код. Для удобства чтения добавлены переносы строк и отступы, хотя они фактически не генерируются препроцессором:
int значение1 ; int значение2 ; int значение3 ; void print_variables ( void ) { printf ( "%s = %d \n " , "значение1" , значение1 ); printf ( "%s = %d \n " , "значение2" , значение2 ); printf ( "%s = %d \n " , "значение3" , значение3 ); }
Целью этого примера является улучшение читаемости использования макроса X путем:
#define FOR_LIST_OF_VARIABLES(DO) \ DO(id1, name1) \ DO(id2, name2) \ DO(id3, name3) \
Как и выше, выполните этот список для генерации объявлений переменных:
#define DEFINE_NAME_VAR(id, name, ...) int name; ДЛЯ_СПИСКА_ПЕРЕМЕННЫХ ( DEFINE_NAME_VAR )
или объявить перечисление:
#define DEFINE_ENUMERATION(id, name, ...) name = id, enum my_id_list_type { FOR_LIST_OF_VARIABLES ( DEFINE_ENUMERATION ) }
Аналогичным образом мы можем сгенерировать функцию, которая выводит переменные и их имена:
void print_variables ( void ) { #define PRINT_NAME_AND_VALUE(id, name, ...) printf("%s = %d\n", #name, name); ДЛЯ_СПИСКА_ПЕРЕМЕННЫХ ( PRINT_NAME_AND_VALUE ) }