Поиск…


Вступление

X-макросы - это метод на основе препроцессора для минимизации повторяющегося кода и поддержания соответствия данных / кода. Множественные отличия макросов, основанные на общем наборе данных, поддерживаются путем представления всей группы расширений с помощью одного главного макроса с заменяющим текстом этого макроса, состоящим из последовательности расширений внутреннего макроса, по одному для каждой базы данных. Внутренний макрос традиционно называется X() , отсюда и название техники.

замечания

Предполагается, что пользователь основного макроса в стиле X-макроса предоставит свое собственное определение для внутреннего макроса X() и в пределах его возможностей для расширения основного макроса. Таким образом, внутренние макро-ссылки мастера расширяются в соответствии с определением пользователя X() . Таким образом, количество повторяющегося кода шаблона в исходном файле может быть уменьшено (появляется только один раз, в заменяющем тексте X() ), как это предпочитают сторонники философии «Не повторяйте себя» (DRY).

Кроме того, переопределяя X() и расширяя мастер-макрос один или несколько дополнительных раз, макросы X могут облегчить сохранение соответствующих данных и кода - одно расширение макроса объявляет данные (например, как элементы массива или элементы перечисления), и другие расширения производят соответствующий код.

Хотя имя «X-macro» происходит от традиционного имени внутреннего макроса, этот метод не зависит от этого конкретного имени. Любое допустимое имя макроса можно использовать на своем месте.

Критики включают

  • исходные файлы, которые полагаются на макросы X, труднее читать;
  • как и все макросы, макросы X строго текстовые - они по своей сути не обеспечивают безопасность любого типа; а также
  • X для генерации кода . По сравнению с альтернативами, основанными на вызывающих функциях, макросы X эффективно делают код более крупным.

Хорошее объяснение макросов X можно найти в статье Рэнди Майерса [X-Macros] у доктора Доббса ( http://www.drdobbs.com/the-new-cx-macros/184401387) .

Тривиальное использование X-макросов для printfs

/* define a list of preprocessor tokens on which to call X */
#define X_123 X(1) X(2) X(3)

/* define X to use */
#define X(val) printf("X(%d) made this print\n", val);
X_123
#undef X
/* good practice to undef X to facilitate reuse later on */

В этом примере препроцессор генерирует следующий код:

printf("X(%d) made this print\n", 1);
printf("X(%d) made this print\n", 2);
printf("X(%d) made this print\n", 3);

Значение и идентификатор Enum

/* declare items of the enum */
#define FOREACH \
      X(item1) \
      X(item2) \
      X(item3) \
/* end of list */

/* define the enum values */
#define X(id) MyEnum_ ## id,
enum MyEnum { FOREACH };
#undef X

/* convert an enum value to its identifier */
const char * enum2string(int enumValue)
{
    const char* stringValue = NULL;
#define X(id) if (enumValue == MyEnum_ ## id) stringValue = #id;
    FOREACH
#undef X
    return stringValue;
}

Затем вы можете использовать перечисленное значение в своем коде и легко распечатать его идентификатор, используя:

printf("%s\n", enum2string(MyEnum_item2));

Расширение: укажите макрос X как аргумент

Метод X-macro можно обобщить, указав имя макроса «X» как аргумент главного макроса. Это дает преимущества, позволяющие избежать конфликтов имен макросов и разрешить использование макроса общего назначения в качестве макроса «X».

Как всегда с макросами X, главный макрос представляет список элементов, значение которых специфично для этого макроса. В этом варианте такой макрос может быть определен следующим образом:

/* declare list of items */
#define ITEM_LIST(X) \
      X(item1) \
      X(item2) \
      X(item3) \
/* end of list */

Затем можно сгенерировать код для печати имен элементов следующим образом:

/* define macro to apply */
#define PRINTSTRING(value) printf( #value "\n");

/* apply macro to the list of items */
ITEM_LIST(PRINTSTRING)

Это расширяется до этого кода:

printf( "item1" "\n"); printf( "item2" "\n"); printf( "item3" "\n");

В отличие от стандартных макросов X, где «X» - это встроенная характеристика главного макроса, с этим стилем может быть ненужным или даже нежелательным после этого определять неопределенный макрос, используемый в качестве аргумента ( PRINTSTRING в этом примере).

Генерация кода

X-Macros можно использовать для генерации кода, написав повторяющийся код: перебирать список для выполнения некоторых задач или объявлять набор констант, объектов или функций.

Здесь мы используем X-макросы для объявления перечисления, содержащего 4 команды, и карту их имен в виде строк

Затем мы можем напечатать строковые значения перечисления.

/* All our commands */
#define COMMANDS(OP) OP(Open) OP(Close) OP(Save) OP(Quit)

/* generate the enum Commands: {cmdOpen, cmdClose, cmdSave, cmdQuit, }; */
#define ENUM_NAME(name) cmd##name,
enum Commands {
  COMMANDS(ENUM_NAME)
};
#undef ENUM_NAME

/* generate the string table */
#define COMMAND_OP(name) #name,
const char* const commandNames[] = {
  COMMANDS(COMMAND_OP)
};
#undef COMMAND_OP

/* the following prints "Quit\n": */
printf("%s\n", commandNames[cmdQuit]());

Аналогично, мы можем создать таблицу переходов для вызова функций по значению перечисления.

Для этого требуется, чтобы все функции имели одну и ту же подпись. Если они не принимают аргументов и возвращают int, мы помещаем это в заголовок с определением перечисления:

/* declare all functions as extern */
#define EXTERN_FUNC(name) extern int doCmd##name(void);
COMMANDS(EXTERN_FUNC)
#undef EXTERN_FUNC

/* declare the function pointer type and the jump table  */
typedef int (*CommandFunc)(void);
extern CommandFunc commandJumpTable[];

Все перечисленные ниже могут быть в разных единицах компиляции, если указанная выше часть включена в заголовок:

/* generate the jump table */
#define FUNC_NAME(name) doCmd##name,
CommandFunc commandJumpTable[] = {
  COMMANDS(FUNC_NAME)
};
#undef FUNC_NAME

/* call the save command like this: */
int result = commandJumpTable[cmdSave]();

/* somewhere else, we need the implementations of the commands */
int doCmdOpen(void) {/* code performing open command */}
int doCmdClose(void) {/* code performing close command */}
int doCmdSave(void) {/* code performing save command */}
int doCmdQuit(void) {/* code performing quit command */}

Примером этого метода, используемого в реальном коде, является диспетчеризация графических процессоров в Chromium .



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow