2010-12-28 15 views
9

¿Hay alguna forma en C estándar o con extensiones GNU para agregar cosas a una definición de macro? P.E., dada una macro definida como
#define List foo bar
puedo añadir bas para que List se expande como si hubiera definí
#define List foo bar bas?¿Puedo agregar a una macro de preprocesador?

Tenía la esperanza de que pudiera hacer algo como esto:

#define List foo bar bas 

#define List_ Expand(List) 
#undef List 
#define List Expand(List_) quux 

pero no puedo encontrar la manera de definir la macro Expand() lo que debes de hacer lo que quiera.

motivación: estoy jugando con discriminado/tagged sindicatos a lo largo de estas líneas:

struct quux_foo { int x; }; 
struct quux_bar { char *s; }; 
struct quux_bas { void *p; }; 

enum quux_type {quux_foo, quux_bar, quux_bas}; 

struct quux { 
    enum quux_type type; 
    union { 
     struct quux_foo foo; 
     struct quux_bar bar; 
     struct quux_bas bas; 
    } t; 
}; 

Calculo que este es un buen lugar para el X-macro. Si defino una macro
#define quux_table X(foo) X(bar) X(bas)
la estructura de enumeración & se puede definir de este modo, y nunca perder la sincronización:

#define X(t) quux_ ## t, 
enum quux_type {quux_table}; 
#undef X 

#define X(t) struct quux_ ## t t; 
struct quux { 
    enum quux_type type; 
    union {quux_table} t; 
}; 
#undef X 

Por supuesto, las estructuras quux_* pueden perder la sincronización, así que me gustaría a hacer algo como esto, solamente legalmente:

struct quux_foo { int x; }; 
#define quux_table quux_table X(foo) 

struct quux_bar { char *s; }; 
#define quux_table quux_table X(bar) 

struct quux_bas { void *p; }; 
#define quux_table quux_table X(bas) 

(Bueno, lo que realmente quiero ser capaz de hacer es algo así como
member_struct(quux, foo) { int x; };
pero soy muy consciente de que las macros no pueden ser (re) definido dentro de las macros.)

De todos modos, eso es mi ejemplo motivador. ¿Hay alguna manera de lograr esto?

Los ejemplos de Boost.Preprocessor están bien, si puede mostrarme cómo hacer que la técnica X-macro funcione con esa biblioteca.

+0

Sí, esto es definitivamente factible, si no insistes en la redefinición Algo doloroso si usas el preprocesador en bruto. Algo menos doloroso con Boost.Preprocessor. Por desgracia, mi frío me impide pensar lo suficiente como para producir un método. Con suerte, alguien más lo hará. – swestrup

Respuesta

5

Efectivamente, no.

Las macros se evalúan con pereza. Cuando #define List_ Expand(List), su lista de reemplazo es la secuencia de cuatro tokens Expand, (, List y ). No hay ninguna forma de expandir una macro en una lista de reemplazo.

Todas las macro sustituciones tienen lugar cuando se invoca una macro.

Recomiendo mirar usando la biblioteca Boost.Preprocessor para la generación automática de código. Es un poco de trabajo, pero puedes lograrlo usando fairly impressive things. Debe ser totalmente compatible con C.

+0

Gracias por la explicación. –

2

No estoy seguro de si esto ayuda, pero puede hacer vari arg macros. El Sr. Conrad del proyecto x264 ama el abuso de preprocesadores. Si suenan como si pudieran ayudarlo, puede encontrar más información Here

+1

C99 Las macros variadas son agradables (y las he usado), pero esto no es realmente relevante para mi pregunta. (No directamente, al menos, aunque puedo imaginar que la solución real podría _utilizar macros variadas en alguna parte.) –

2

¡Hay una manera!

Usando la nueva palabra clave _Pragma esto se puede lograr en gcc (aunque no con msvc)

Si usted hace estallar una macro dentro de su propia definición que retrasará su expansión hasta que la macro se expanda por primera vez. Esto le permite hacer que su expansión anterior sea parte de su propia definición. Sin embargo, desde que se extrae durante su expansión, sólo puede ser usado una vez

Aquí hay un código de ejemplo para verlo en acción

#define pushfoo _Pragma("push_macro(\"foo\")") //for convenience 
#define popfoo _Pragma("pop_macro(\"foo\")") 

#define foo 1 

pushfoo       //push the old value 
#undef foo      //so you don't get a warning on the next line 
#define foo popfoo foo , 2  //append to the previous value of foo 

pushfoo 
#undef foo 
#define foo popfoo foo , 3 

pushfoo 
#undef foo 
#define foo popfoo foo , 4 


foo //this whole list will expand to something like popfoo foo popfoo foo popfoo foo , 4 
    //which will in turn expand to 1 , 2 , 3 , 4 

foo //the second time this will expand to just 1 

Esta opción debe realizar la generación automática de código un poco justo más fácil, aunque desafortunadamente solo en gcc (tal vez clang, no lo he probado)

Para ser sincero, no hay ninguna razón por la que pueda encontrar el motivo por el cual esto debe funcionar, probablemente sea un comportamiento indefinido el que funcione. Supongo que la razón es que después de hacer estallar foo, la macro actual que se está expandiendo ya no está asociada con el nombre foo, que permite expandir el símbolo foo, pero esa es solo mi conjetura

Cuestiones relacionadas