Domanda

Mi chiedevo se è possibile iterare su argomenti passati a una macro variadic in C99 o con qualsiasi estensione GCC?

Per esempio è possibile scrivere una macro generica che richiede una struttura e dei suoi campi passati come argomenti e stampe di offset di ciascun campo all'interno della struttura?

Qualcosa di simile a questo:

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

/* PRN_STRUCT_OFFSETS will print offset of each of the fields 
   within structure passed as the first argument.
*/

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

    return 0;
}
È stato utile?

Soluzione

Ecco i compiti della giornata, si basa su trucchi macro e oggi ho imparato a conoscere in particolare __VA_NARG__ inventato da Laurent Deniau . In ogni caso, il seguente codice di esempio funziona fino a 8 campi per motivi di chiarezza. Basta estendere il codice di duplicando se avete bisogno di più (questo è perché il preprocessore non dispone di ricorsione, come si legge il file solo una volta).

#include <stdio.h>
#include <stddef.h>

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

che stampa:

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:.. Ecco una versione leggermente diversa che cerca di essere più generico La macro FOR_EACH(what, ...) applica what ad ogni altro argomento nella lista di argomenti variabili

Quindi, non resta che definire una macro che prende un singolo argomento come questo:

#define DO_STUFF(x) foo(x)

, che sta per essere applicato ad ogni argomento nella lista. Quindi, per il vostro tipico esempio è necessario incidere un po ', ma resta ancora 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 lo si applica in questo modo:

FOR_EACH(PRN_STRUCT_OFFSETS, a, b, c);

Infine, un programma di esempio completo:

#include <stdio.h>
#include <stddef.h>

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

Altri suggerimenti

Con il rischio di guadagnare un distintivo archeologo, penso che ci sia un minore miglioramento alla risposta di Gregorio sopra utilizzando la tecnica da sovraccarico Macro sul numero di argomenti

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

Se la struttura viene descritta con X-Macro , allora è possibile scrivere una funzione , o una macro per iterare su tutti i campi della struttura e stampare il loro offset.

#include <stddef.h>   // 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 soluzione di Gregorio Pakosz funzionava. Ma ho avuto due piccoli problemi con esso:

  1. La compilazione con l'opzione saccente ho ricevuto l'avvertimento: "ISO99 richiede argomenti di riposo da utilizzare". Questo è causato dagli argomenti variad nella prima macro FOR_EACH_1. L'eliminazione di questi e cambiando la chiamata a FOR_EACH_1 in FOR_EACH_2 rimosso questo avvertimento.

    #define FOR_EACH_1(what, x) 
    #define FOR_EACH_2(what, x, ...)\
        what(x);                    \
        FOR_EACH_1(what);
    
  2. Dato che ho usato in un modo molto generico, a volte dovuto chiamare la macro di ripetizione con solo 1 argomento. (Lo so che non ha senso ripetere una voce 1 volte;)). Per fortuna la soluzione a questo problema è molto semplice. Basta rimuovere il parametro x dalla macro for_each.

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

Ecco l'elenco completo con le due modifiche:

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

Forse utilizzare i varargs come un inizializzatore array e iterare countof (array)? cioè sizeof (array) / sizeof (array [0]). La matrice potrebbe potenzialmente essere un array anonimo C99.

Non riesco a pensare ad un altro modo per scorrere i var-args di una macro, dal momento che non so come fare qualsiasi cosa per il testo di ogni elemento var-arg. La parte var-arg potrebbe anche essere un unico argomento che ha le virgole in esso, per tutto quello che puoi fare per essa con CPP, per quanto ne so.

Ma ecco la mia idea per iterare su var-args:

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

Questa è la mia soluzione per questo
godere

#include <stdlib.h>
#include <stdio.h>

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


int main(int argc, char* argv[])
{
    ITERATE_OVER_VARADICT_MACROS("Test of iterate over arguments in variadic macros", 10,12, 34);
    return 0;
}

Questo è il meglio che posso pensare, con lo standard C:

#include <stddef.h>
#include <stdio.h>

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

Sto aggiungendo questo come un'altra risposta. Ecco una prova a farlo con C ++ 0x, compilato con g ++ 4.5.0

#include <iostream>
using namespace std;

template<typename L>
inline void for_each(L l)
{
}

template<typename L, typename P, typename... Q>
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;
}

Il programma stampa

  

123

Tuttavia, con questo approccio, tutti i parametri si passa l'espressione lambda bisogno di avere lo stesso tipo, int nell'esempio di cui sopra. Tuttavia, lambda consentono di catturare variabili come:

int main()
{
  int offset = 10;

  for_each([offset] (int x) { cout << offset + x << endl; }, 1, 2, 3);

  return 0;
}

che stampa:

11
12
13

Se si prendono di mira Objective-C ... guarda il KSVarArgs su Github

  

KSVarArgs è un insieme di macro progettati per rendere fare con argomenti variabili più facili in Objective-C. Tutte le macro presuppongono che l'elenco varargs contiene solo gli oggetti Objective-C o strutture object-simile (assegnabili a digitare id). La macro ksva_iterate_list base () itera sui argomenti variabili, invocando un blocco per ogni argomento, finché non incontra nullo di terminazione. Le altre macro sono per comodità durante la conversione in collezioni comuni.

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

esempio di utilizzo:

[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
]; 
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top