¿Es posible iterar sobre argumentos en macros variadas?

Me preguntaba si es posible repetir los argumentos pasados ​​a una macro variable en C99 o usando cualquier extensión de GCC.

Por ejemplo, ¿es posible escribir una macro genérica que toma una estructura y sus campos pasan como argumentos e imprime el desplazamiento de cada campo dentro de la estructura?

Algo como esto:

 struct a {
     int a;
     int b;
     int c;
 };

 / * PRN_STRUCT_OFFSETS imprimirá el desplazamiento de cada uno de los campos 
    dentro de la estructura pasó como el primer argumento.
 * /

 int main (int argc, char * argv [])
 {
     PRN_STRUCT_OFFSETS (struct a, a, b, c);

     return 0;
 }

Aquí está mi tarea del día, está basada en macro trucos y hoy particularmente aprendí sobre __VA_NARG__ inventado por Laurent Deniau . De todos modos, el siguiente código de ejemplo funciona hasta en 8 campos en aras de la claridad. Simplemente extienda el código duplicando si necesita más (esto es porque el preprocesador no presenta recursividad, ya que lee el archivo solo una vez).

 #include  #include  struct a { int a; int b; int c; }; struct b { int a; int b; int c; int d; }; #define STRINGIZE(arg) STRINGIZE1(arg) #define STRINGIZE1(arg) STRINGIZE2(arg) #define STRINGIZE2(arg) #arg #define CONCATENATE(arg1, arg2) CONCATENATE1(arg1, arg2) #define CONCATENATE1(arg1, arg2) CONCATENATE2(arg1, arg2) #define CONCATENATE2(arg1, arg2) arg1##arg2 /* PRN_STRUCT_OFFSETS will print offset of each of the fields within structure passed as the first argument. */ #define PRN_STRUCT_OFFSETS_1(structure, field, ...) printf(STRINGIZE(structure)":"STRINGIZE(field)"-%d\n", offsetof(structure, field)); #define PRN_STRUCT_OFFSETS_2(structure, field, ...)\ printf(STRINGIZE(structure)":"STRINGIZE(field)"-%d\n", offsetof(structure, field));\ PRN_STRUCT_OFFSETS_1(structure, __VA_ARGS__) #define PRN_STRUCT_OFFSETS_3(structure, field, ...)\ printf(STRINGIZE(structure)":"STRINGIZE(field)"-%d\n", offsetof(structure, field));\ PRN_STRUCT_OFFSETS_2(structure, __VA_ARGS__) #define PRN_STRUCT_OFFSETS_4(structure, field, ...)\ printf(STRINGIZE(structure)":"STRINGIZE(field)"-%d\n", offsetof(structure, field));\ PRN_STRUCT_OFFSETS_3(structure, __VA_ARGS__) #define PRN_STRUCT_OFFSETS_5(structure, field, ...)\ printf(STRINGIZE(structure)":"STRINGIZE(field)"-%d\n", offsetof(structure, field));\ PRN_STRUCT_OFFSETS_4(structure, __VA_ARGS__) #define PRN_STRUCT_OFFSETS_6(structure, field, ...)\ printf(STRINGIZE(structure)":"STRINGIZE(field)"-%d\n", offsetof(structure, field));\ PRN_STRUCT_OFFSETS_5(structure, __VA_ARGS__) #define PRN_STRUCT_OFFSETS_7(structure, field, ...)\ printf(STRINGIZE(structure)":"STRINGIZE(field)"-%d\n", offsetof(structure, field));\ PRN_STRUCT_OFFSETS_6(structure, __VA_ARGS__) #define PRN_STRUCT_OFFSETS_8(structure, field, ...)\ printf(STRINGIZE(structure)":"STRINGIZE(field)"-%d\n", offsetof(structure, field));\ PRN_STRUCT_OFFSETS_7(structure, __VA_ARGS__) #define PRN_STRUCT_OFFSETS_NARG(...) PRN_STRUCT_OFFSETS_NARG_(__VA_ARGS__, PRN_STRUCT_OFFSETS_RSEQ_N()) #define PRN_STRUCT_OFFSETS_NARG_(...) PRN_STRUCT_OFFSETS_ARG_N(__VA_ARGS__) #define PRN_STRUCT_OFFSETS_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N #define PRN_STRUCT_OFFSETS_RSEQ_N() 8, 7, 6, 5, 4, 3, 2, 1, 0 #define PRN_STRUCT_OFFSETS_(N, structure, field, ...) CONCATENATE(PRN_STRUCT_OFFSETS_, N)(structure, field, __VA_ARGS__) #define PRN_STRUCT_OFFSETS(structure, field, ...) PRN_STRUCT_OFFSETS_(PRN_STRUCT_OFFSETS_NARG(field, __VA_ARGS__), structure, field, __VA_ARGS__) int main(int argc, char *argv[]) { PRN_STRUCT_OFFSETS(struct a, a, b, c); printf("\n"); PRN_STRUCT_OFFSETS(struct b, a, b, c, d); return 0; } 

que imprime:

 struct a:a-0 struct a:b-4 struct a:c-8 struct b:a-0 struct b:b-4 struct b:c-8 struct b:d-12 

EDITAR: Aquí hay una versión ligeramente diferente que intenta ser más genérica. La FOR_EACH(what, ...) aplica what a cada otro argumento en la lista de argumentos de variables.

Entonces, solo tienes que definir una macro que tome un único argumento como este:

 #define DO_STUFF(x) foo(x) 

que se aplicará a cada argumento en la lista. Entonces, para su ejemplo típico, necesita hackear un poco, pero sigue siendo conciso:

 #define PRN_STRUCT_OFFSETS_(structure, field) printf(STRINGIZE(structure)":"STRINGIZE(field)" - offset = %d\n", offsetof(structure, field)); #define PRN_STRUCT_OFFSETS(field) PRN_STRUCT_OFFSETS_(struct a, field) 

Y lo aplicas así:

 FOR_EACH(PRN_STRUCT_OFFSETS, a, b, c); 

Finalmente, un progtwig de muestra completo:

 #include  #include  struct a { int a; int b; int c; }; #define STRINGIZE(arg) STRINGIZE1(arg) #define STRINGIZE1(arg) STRINGIZE2(arg) #define STRINGIZE2(arg) #arg #define CONCATENATE(arg1, arg2) CONCATENATE1(arg1, arg2) #define CONCATENATE1(arg1, arg2) CONCATENATE2(arg1, arg2) #define CONCATENATE2(arg1, arg2) arg1##arg2 #define FOR_EACH_1(what, x, ...) what(x) #define FOR_EACH_2(what, x, ...)\ what(x);\ FOR_EACH_1(what, __VA_ARGS__); #define FOR_EACH_3(what, x, ...)\ what(x);\ FOR_EACH_2(what, __VA_ARGS__); #define FOR_EACH_4(what, x, ...)\ what(x);\ FOR_EACH_3(what, __VA_ARGS__); #define FOR_EACH_5(what, x, ...)\ what(x);\ FOR_EACH_4(what, __VA_ARGS__); #define FOR_EACH_6(what, x, ...)\ what(x);\ FOR_EACH_5(what, __VA_ARGS__); #define FOR_EACH_7(what, x, ...)\ what(x);\ FOR_EACH_6(what, __VA_ARGS__); #define FOR_EACH_8(what, x, ...)\ what(x);\ FOR_EACH_7(what, __VA_ARGS__); #define FOR_EACH_NARG(...) FOR_EACH_NARG_(__VA_ARGS__, FOR_EACH_RSEQ_N()) #define FOR_EACH_NARG_(...) FOR_EACH_ARG_N(__VA_ARGS__) #define FOR_EACH_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N #define FOR_EACH_RSEQ_N() 8, 7, 6, 5, 4, 3, 2, 1, 0 #define FOR_EACH_(N, what, x, ...) CONCATENATE(FOR_EACH_, N)(what, x, __VA_ARGS__) #define FOR_EACH(what, x, ...) FOR_EACH_(FOR_EACH_NARG(x, __VA_ARGS__), what, x, __VA_ARGS__) #define PRN_STRUCT_OFFSETS_(structure, field) printf(STRINGIZE(structure)":"STRINGIZE(field)" - offset = %d\n", offsetof(structure, field)); #define PRN_STRUCT_OFFSETS(field) PRN_STRUCT_OFFSETS_(struct a, field) int main(int argc, char *argv[]) { FOR_EACH(PRN_STRUCT_OFFSETS, a, b, c); printf("\n"); return 0; } 

A riesgo de obtener una insignia de arqueólogo, creo que hay una mejora menor en la respuesta de Gregory anterior usando la técnica de Sobrecarga de macro en el número de argumentos

Con foo.h:

 // Make a FOREACH macro #define FE_1(WHAT, X) WHAT(X) #define FE_2(WHAT, X, ...) WHAT(X)FE_1(WHAT, __VA_ARGS__) #define FE_3(WHAT, X, ...) WHAT(X)FE_2(WHAT, __VA_ARGS__) #define FE_4(WHAT, X, ...) WHAT(X)FE_3(WHAT, __VA_ARGS__) #define FE_5(WHAT, X, ...) WHAT(X)FE_4(WHAT, __VA_ARGS__) //... repeat as needed #define GET_MACRO(_1,_2,_3,_4,_5,NAME,...) NAME #define FOR_EACH(action,...) \ GET_MACRO(__VA_ARGS__,FE_5,FE_4,FE_3,FE_2,FE_1)(action,__VA_ARGS__) // Example // Some actions #define QUALIFIER(X) X:: #define OPEN_NS(X) namespace X { #define CLOSE_NS(X) } // Helper function #define QUALIFIED(NAME,...) FOR_EACH(QUALIFIER,__VA_ARGS__)NAME // Emit some code QUALIFIED(MyFoo,Outer,Next,Inner) foo(); FOR_EACH(OPEN_NS,Outer,Next,Inner) class Foo; FOR_EACH(CLOSE_NS,Outer,Next,Inner) 

cpp foo.h genera:

 Outer::Next::Inner::MyFoo foo(); namespace Outer {namespace Next {namespace Inner { class Foo; }}} 

Si su estructura se describe con X-Macros , entonces es posible escribir una función, o una macro para iterar sobre todos los campos de la estructura e imprimir su desplazamiento.

 #include  // offsetof macro //--- first describe the structure, the fields, their types #define X_FIELDS \ X(int, field1) \ X(int, field2) \ X(char, field3) \ X(char *, field4) //--- define the structure, the X macro will be expanded once per field typedef struct { #define X(type, name) type name; X_FIELDS #undef X } mystruct; //--- "iterate" over all fields of the structure and print out their offset void print_offset(mystruct *aStruct) { #define X(type, name) printf("offset of %s is %d\n", #name, offsetof(mystruct, name)); X_FIELDS #undef X } //--- demonstrate int main(int ac, char**av) { mystruct a = { 0, 1, 'a', "hello"}; print_offset(&a); return 0; } 

La solución de Gregory Pakosz funcionó de maravilla. Pero tuve dos pequeños problemas con esto:

  1. Comstackndo con la opción pedante recibí la advertencia: “ISO99 requiere que se usen argumentos de descanso”. Esto es causado por los argumentos variados en la primera macro FOR_EACH_1. Quitarlos y cambiar la llamada a FOR_EACH_1 en FOR_EACH_2 eliminó esta advertencia.

     #define FOR_EACH_1(what, x) #define FOR_EACH_2(what, x, ...)\ what(x); \ FOR_EACH_1(what); 
  2. Como lo usé de una manera muy genérica, a veces tuve que llamar a la macro de repetición con solo 1 argumento. (Sé que no tiene sentido repetir un artículo 1 vez;)). Afortunadamente, la solución a este problema fue bastante simple. Solo eliminando el parámetro x de la macro FOR_EACH.

     #define FOR_EACH(what, ...) FOR_EACH_(FOR_EACH_NARG(__VA_ARGS__), what, __VA_ARGS__) 

Aquí la lista completa con los dos cambios:

 #define CONCATENATE(arg1, arg2) CONCATENATE1(arg1, arg2) #define CONCATENATE1(arg1, arg2) CONCATENATE2(arg1, arg2) #define CONCATENATE2(arg1, arg2) arg1##arg2 #define FOR_EACH_1(what, x) \ what(x) #define FOR_EACH_2(what, x, ...) \ what(x); \ FOR_EACH_1(what, __VA_ARGS__); #define FOR_EACH_3(what, x, ...) \ what(x); \ FOR_EACH_2(what, __VA_ARGS__); #define FOR_EACH_4(what, x, ...) \ what(x); \ FOR_EACH_3(what, __VA_ARGS__); #define FOR_EACH_5(what, x, ...) \ what(x); \ FOR_EACH_4(what, __VA_ARGS__); #define FOR_EACH_6(what, x, ...) \ what(x); \ FOR_EACH_5(what, __VA_ARGS__); #define FOR_EACH_7(what, x, ...) \ what(x); \ FOR_EACH_6(what, __VA_ARGS__); #define FOR_EACH_8(what, x, ...) \ what(x); \ FOR_EACH_7(what, __VA_ARGS__); #define FOR_EACH_NARG(...) FOR_EACH_NARG_(__VA_ARGS__, FOR_EACH_RSEQ_N()) #define FOR_EACH_NARG_(...) FOR_EACH_ARG_N(__VA_ARGS__) #define FOR_EACH_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N #define FOR_EACH_RSEQ_N() 8, 7, 6, 5, 4, 3, 2, 1, 0 #define FOR_EACH_(N, what, ...) CONCATENATE(FOR_EACH_, N)(what, __VA_ARGS__) #define FOR_EACH(what, ...) FOR_EACH_(FOR_EACH_NARG(__VA_ARGS__), what, __VA_ARGS__) 

¿Tal vez use los varargs como un inicializador de matriz e itere sobre countof (array)? es decir, sizeof (array) / sizeof (array [0]). La matriz podría ser una matriz anónima C99.

No puedo pensar en otra forma de iterar sobre los var-args de una macro, ya que no sé cómo hacer nada con el texto de cada elemento var-arg. La parte var-arg podría ser un único argumento que tiene comas, por todo lo que puede hacer con CPP, AFAIK.

Pero esta es mi idea para iterar sobre var-args:

 #define countof(a) ( sizeof(a)/sizeof((a)[0]) ) #define MACRO(fd, format, ...) do { int ar_[] = { __VA_ARGS__ }; \ for(int i=0; i 

Esta es mi solución para esto
disfrutar

 #include  #include  #define ITERATE_OVER_VARADICT_MACROS( str, ...)\ do{\ int i, _arr_[] = {__VA_ARGS__};\ fprintf(stderr,"msg =%s\n", str); \ for(i=0; i 

Esto es lo mejor que puedo pensar, con C estándar:

 #include  #include  // prints a single offset #define PRN_STRUCT_OFFSET(x, a) printf("&" #x "." #a " = %d\n", offsetof(x, a)); // prints a struct with one member #define PRN_STRUCT_OFFSETS_1(x, a) PRN_STRUCT_OFFSET(x, a) // prints a struct with two members #define PRN_STRUCT_OFFSETS_2(x, a, b) \ PRN_STRUCT_OFFSET(x, a) \ PRN_STRUCT_OFFSET(x, b) // and so on until some N. // Boost.Preprocessor might help here, I'm not sure struct some_struct { int a; void* c; }; int main(void) { PRN_STRUCT_OFFSETS_2(struct some_struct, a, c); return 0; } 

Estoy agregando esto como otra respuesta. Aquí hay un bash de hacerlo con C ++ 0x, comstackdo con g ++ 4.5.0

 #include  using namespace std; template inline void for_each(L l) { } template inline void for_each(L l, P arg, Q... args) { l(arg); for_each(l, args...); } int main() { for_each([] (int x) { cout << x; }, 1, 2, 3); return 0; } 

El progtwig imprime

123

Sin embargo, con este enfoque, todos los parámetros que pasa a la expresión lambda deben tener el mismo tipo, int en el ejemplo anterior. Sin embargo, lambdas te permite capturar variables como:

 int main() { int offset = 10; for_each([offset] (int x) { cout << offset + x << endl; }, 1, 2, 3); return 0; } 

que imprime:

 11 12 13 

Si te diriges a Objective-C … echa un vistazo a los KSVarArgs INCREÍBLES en Github

KSVarArgs es un conjunto de macros diseñado para facilitar el tratamiento de argumentos variables en Objective-C. Todas las macros asumen que la lista varargs contiene solo objetos object-c o estructuras similares a objetos (asignables a type id). La macro de base ksva_iterate_list () itera sobre los argumentos variables, invocando un bloque para cada argumento, hasta que encuentra un nil de terminación. Las otras macros son por conveniencia cuando se convierten a colecciones comunes.

 /*! @param firstNote NSString that is the only known arg */ - (void) observeWithBlocks:(NSString*)firstNote,...{ /*! ksva_list_to_nsarray puts varargs into new array, `namesAndBlocks` */ ksva_list_to_nsarray(firstNote, namesAndBlocks); /// Split the array into Names and Blocks NSArray *names = [namesAndBlocks subArrayWithMembersOfKind:NSString.class], *justBlocks = [namesAndBlocks arrayByRemovingObjectsFromArray:names]; [names eachWithIndex:^(id obj, NSInteger idx) { [self observeName:obj usingBlock:^(NSNotification *n) { ((void(^)())justBlocks[idx])(n); }]; }]; } 

uso de ejemplo:

 [NSNotificationCenter.defaultCenter observeWithBlocks: NSViewFrameDidChangeNotification, /// first, named arg ^(NSNotification *m){ [self respondToFrameChange]; }, // vararg NSTextViewDidChangeSelectionNotification, // vararg ^(NSNotification *z){ [z.infoDict[@"textView"] save]; }, // vararg nil // must nil-terminate ];