2013-02-13 5 views
7

Mi riferisco a un esempio di codice dal progetto opensource tig. Che è un ottimo strumento!Qual è la necessità di definire un Enum/Struct tramite macro?

di file: tig.c

Ho difficoltà a trovare una ragione per la definizione del richiesta enumerazione come segue:

enum request { 
#define REQ_GROUP(help) 
#define REQ_(req, help) REQ_##req 

     /* Offset all requests to avoid conflicts with ncurses getch values. */ 
     REQ_UNKNOWN = KEY_MAX + 1, 
     REQ_OFFSET, 
     REQ_INFO, 

     /* Internal requests. */ 
     REQ_JUMP_COMMIT, 

#undef REQ_GROUP 
#undef REQ_ 
}; 

anche strutture come pure ..

static const struct request_info req_info[] = { 
#define REQ_GROUP(help) { 0, NULL, 0, (help) }, 
#define REQ_(req, help) { REQ_##req, (#req), STRING_SIZE(#req), (help) } 
     REQ_INFO 
#undef REQ_GROUP 
#undef REQ_ 
}; 

come si può vedere REQ_GROUP è stato # definito più volte creando confusione .. almeno per me. Beh, so che potrebbe esserci una buona ragione per fare come tale. Qual è il vero motivo per nascondere la definizione enum/struct nel codice usando le macro?

+2

Per capire cosa sta accadendo, spesso trovo utile compilare una sorgente con le opzioni gcc '-dNI -E' che risulta nell'origine preelaborata, ma mostra le definizioni di macro, senza che le macro vengano espanse e con affermazioni '# include' intatte. –

+1

Le risposte qui sotto hanno.Un nome generico per questa strategia è "X macros", wikipedia ha una pagina a riguardo: http://en.wikipedia.org/wiki/X_Macro così come Dr Dobbs: http://www.drdobbs.com/cpp/the -x-macro/228700289 – Vicky

+0

@Vicky drdobbs link era una chicca! Grazie! – ashishsony

risposta

7

Di solito è inattivo quando si deve utilizzare un trattamento diverso sulla stessa origine dati.

Per esempio, si potrebbe fare:

#define MY_LIST X(Elem1) X(Elem2) X(Elem3) 

E poi:

enum MyEnum { 
# define X(e) e, 

    MY_LIST 

    Last 

# undef X 
}; 

Nel qual caso, MY_LIST si espande utilizzando la definizione corrente di X.

Adesso, nello stesso file, posso anche usare MY_LIST per creare un metodo

char const* to_string(MyEnum e) { 
    switch(e) { 
#  define X(e) case e: return #e; 

     MY_LIST 

     case Last: return "Last"; 

#  undef X 
    } 
    return 0; 
} // to_string 

to_string questo modo, l'insieme dei valori del enum è sempre e solo scritto una volta, e automaticamente sia l'enum e un certo numero di metodi che si occupano di esso sono mantenuti sincronizzati con questo set.

1

È per evitare la duplicazione dell'elenco di richieste. Quella lista ha bisogno solo di mantenere in un posto, la definizione di REQ_INFO e l'enumerazione e le strutture dati vengono generate automaticamente da tale elenco con le opportune definizioni di REQ_GROUP e REQ_.

Senza tali macro, l'enumerazione e la struttura dati dovrebbero essere mantenute separatamente, avendo cura di mantenerle coerenti tra loro, comportando più lavoro e più possibilità di errore.

1

ha dimenticato definisce importanti nello stesso file:

#define REQ_INFO \ 
REQ_GROUP("View switching") \ 
VIEW_INFO(VIEW_REQ), \ 
\ 
REQ_GROUP("View manipulation") \ 
REQ_(ENTER, "Enter current line and scroll"), \ 
REQ_(NEXT, "Move to next"), \ 
REQ_(PREVIOUS, "Move to previous"), \ 
< output omitted as it is too long > 

Così, per esempio, la struttura che avete dimostrato espande in:

static const struct request_info req_info[] = { 
    { 0, NULL, 0, "View switching" }, 
    < VIEW_INFO also expands to some long structure that was ommited here > 
    { 0, NULL, 0, "View manipulation" }, 
    { REQ_ENTER, ENTER, STRING_SIZE("ENTER"), "Enter current line and scroll"}, 
    { REQ_NEXT, NEXT, STRING_SIZE("NEXT"), "Move to next"} 
    < and so on > 
}; 

Come altre risposte menzionati, è fatto per lo più per mantenere sincronizzate più strutture/enumeratori.

Problemi correlati