É possível iterar sobre argumentos em macros variadicas?

Eu queria saber se é possível iterar sobre argumentos passados ​​para uma macro variadic em C99 ou usando qualquer extensão GCC?

Por exemplo, é possível escrever uma macro genérica que usa uma estrutura e seus campos como argumentos e imprime o deslocamento de cada campo dentro da estrutura?

Algo assim:

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

 / * PRN_STRUCT_OFFSETS imprimirá o deslocamento de cada um dos campos 
    dentro da estrutura passada como o primeiro argumento.
 * /

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

     return 0;
 }

    Aqui está o meu trabalho de casa do dia, é baseado em truques de macro e hoje eu particularmente aprendi sobre __VA_NARG__ inventado por Laurent Deniau . De qualquer forma, o código de amostra a seguir funciona até 8 campos para fins de clareza. Apenas estenda o código duplicando se você precisar de mais (isso ocorre porque o pré-processador não possui recursion, já que ele lê o arquivo apenas uma 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 

    EDIT: aqui é uma versão ligeiramente diferente que tenta ser mais genérica. A FOR_EACH(what, ...) aplica o what a todos os outros argumentos na lista de argumentos variables.

    Então, você só precisa definir uma macro que aceita um único argumento como este:

     #define DO_STUFF(x) foo(x) 

    que será aplicado a todos os argumentos da lista. Então, para o seu exemplo típico, você precisa hackar um pouco, mas ainda assim é 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) 

    E você aplica assim:

     FOR_EACH(PRN_STRUCT_OFFSETS, a, b, c); 

    Finalmente, um programa de amostra 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; } 

    Correndo o risco de ganhar um distintivo de arqueólogo, eu acho que há uma pequena melhora na resposta de Gregory acima usando a técnica de Sobrecarga de Macro no Número de Argumentos

    Com 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 gera:

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

    Se sua estrutura é descrita com X-Macros , então é possível escrever uma function ou uma macro para iterar sobre todos os campos da estrutura e imprimir seu deslocamento.

     #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; } 

    A solução de Gregory Pakosz funcionou muito bem. Mas eu tive dois pequenos problemas com isso:

    1. Compilando com a opção pedante, recebi o aviso: “O ISO99 requer que os argumentos de descanso sejam usados”. Isso é causado pelos argumentos variad na primeira macro FOR_EACH_1. Remover esses e alterar a chamada para FOR_EACH_1 em FOR_EACH_2 removeu esse aviso.

       #define FOR_EACH_1(what, x) #define FOR_EACH_2(what, x, ...)\ what(x); \ FOR_EACH_1(what); 
    2. Desde que eu usei de uma forma muito genérica, às vezes tive que chamar a macro de repetição com apenas 1 argumento. (Eu sei que não faz sentido repetir um item 1 vezes;)). Felizmente a solução para esse problema foi bem simples. Apenas removendo o parâmetro x da macro FOR_EACH.

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

    Aqui a lista completa com as duas mudanças:

     #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__) 

    Talvez use o varargs como um inicializador de matriz e iterar sobre countof (matriz)? ou seja, sizeof (array) / sizeof (array [0]). A matriz pode ser potencialmente uma matriz anônima C99.

    Não consigo pensar em outra maneira de iterar sobre os var-args de uma macro, já que não sei como fazer nada com o texto de cada elemento var-arg. A parte var-arg pode muito bem ser um único argumento que tenha vírgulas, para tudo o que você pode fazer com CPP, AFAIK.

    Mas aqui está a minha ideia 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 é a minha solução para isso
    apreciar

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

    Este é o melhor que posso pensar, com o padrão C:

     #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; } 

    Estou adicionando isso como outra resposta. Aqui está uma tentativa de fazê-lo com C ++ 0x, compilado com 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; } 

    O programa imprime

    123

    No entanto, com essa abordagem, todos os parâmetros que você passa para a expressão lambda precisam ter o mesmo tipo, int no exemplo acima. No entanto, os lambdas permitem que você capture 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 

    Se você está alvejando Objective-C … confira o AWSOME KSVarArgs no Github

    O KSVarArgs é um conjunto de macros projetado para tornar mais fácil lidar com argumentos variables ​​em Objective-C. Todas as macros assumem que a lista varargs contém apenas objects objective-c ou estruturas semelhantes a objects (designáveis ​​para digitar id). A macro base ksva_iterate_list () itera sobre os argumentos da variável, invocando um bloco para cada argumento, até encontrar um nulo de terminação. As outras macros são, por conveniência, ao converter para collections comuns.

     /*! @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); }]; }]; } 

    exemplo de uso:

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