Buscar..


Introducción

Las macros X son una técnica basada en preprocesador para minimizar el código repetitivo y mantener las correspondencias de datos / código. Se admiten múltiples expansiones de macros distintas basadas en un conjunto común de datos al representar todo el grupo de expansiones a través de una sola macro maestra, con el texto de reemplazo de esa macro que consiste en una secuencia de expansiones de una macro interna, una para cada dato. La macro interna se denomina tradicionalmente X() , de ahí el nombre de la técnica.

Observaciones

Se espera que el usuario de una macro maestra de estilo X-macro proporcione su propia definición para la macro interna X() , y dentro de su alcance para expandir la macro maestra. Las referencias macro internas del maestro se expanden de acuerdo con la definición de X() del usuario. De esta manera, la cantidad de código repetitivo repetitivo en el archivo de origen se puede reducir (aparece solo una vez, en el texto de reemplazo de X() ), como lo favorecen los adherentes a la filosofía de "No repetirse" (DRY).

Además, al redefinir X() y expandir la macro maestra una o más veces, las macros X pueden facilitar el mantenimiento de los datos y el código correspondientes; una expansión de la macro declara los datos (como elementos de la matriz o miembros de la enumeración, por ejemplo), y Las otras expansiones producen el código correspondiente.

Aunque el nombre de "X-macro" proviene del nombre tradicional de la macro interna, la técnica no depende de ese nombre en particular. Cualquier nombre de macro válido puede ser usado en su lugar.

Las criticas incluyen

  • Los archivos de origen que se basan en macros X son más difíciles de leer;
  • como todas las macros, las macros X son estrictamente textuales, no proporcionan ningún tipo de seguridad; y
  • Las macros X proporcionan la generación de código . En comparación con las alternativas basadas en funciones de llamada, las macros X hacen que el código sea más grande.

Una buena explicación de las macros de X se puede encontrar en el artículo de Randy Meyers [X-Macros] en Dr. Dobbs ( http://www.drdobbs.com/the-new-cx-macros/184401387) .

Uso trivial de X-macros para 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 */

Este ejemplo dará como resultado que el preprocesador genere el siguiente código:

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 valor e identificador

/* 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;
}

A continuación, puede usar el valor enumerado en su código e imprimir fácilmente su identificador utilizando:

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

Extensión: Da la macro X como argumento

El enfoque de la X-macro se puede generalizar un poco al hacer que el nombre de la macro "X" sea un argumento de la macro maestra. Esto tiene las ventajas de ayudar a evitar colisiones de nombres de macro y de permitir el uso de una macro de propósito general como la macro "X".

Como siempre con las macros X, la macro maestra representa una lista de elementos cuyo significado es específico de esa macro. En esta variación, tal macro podría definirse así:

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

Uno podría entonces generar un código para imprimir los nombres de los artículos de esta manera:

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

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

Eso se expande a este código:

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

A diferencia de las macros X estándar, donde el nombre "X" es una característica incorporada de la macro maestra, con este estilo puede ser innecesario o incluso no deseable volver a definir la macro utilizada como argumento ( PRINTSTRING en este ejemplo).

Codigo de GENERACION

Las macros X se pueden usar para la generación de código, al escribir código repetitivo: iterar sobre una lista para realizar algunas tareas o declarar un conjunto de constantes, objetos o funciones.

Aquí usamos X-macros para declarar una enumeración que contiene 4 comandos y un mapa de sus nombres como cadenas

Luego podemos imprimir los valores de cadena de la enumeración.

/* 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]());

De manera similar, podemos generar una tabla de salto para llamar a funciones mediante el valor enum.

Esto requiere que todas las funciones tengan la misma firma. Si no toman argumentos y devuelven un int, pondríamos esto en un encabezado con la definición de enumeración:

/* 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[];

Todo lo siguiente puede estar en diferentes unidades de compilación, asumiendo que la parte anterior se incluye como encabezado:

/* 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 */}

Un ejemplo de esta técnica utilizada en código real es el envío de comandos de GPU en Chromium .



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow