2013-02-13 10 views
7

Mam na myśli przykład kodu z projektu opensource tig. Co jest świetnym narzędziem!Jaka jest potrzeba zdefiniowania Enum/Struct przez makra?

file: tig.c

walczę by znaleźć powód do definiowania żądania wyliczanie następująco:

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

nawet struktur jak dobrze ..

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

jak widać REQ_GROUP został #zdefiniowany wiele razy tworząc zamieszanie .. co najmniej dla mnie. Cóż, wiem, że może istnieć dobry powód do zrobienia jako taki. Jaki jest faktyczny powód, aby ukryć definicję enum/struct w kodzie za pomocą makr?

+2

Aby zrozumieć, co się dzieje, często uważam, że warto skompilować źródło z opcjami gcc '-dNI -E', co powoduje, że wstępnie przetworzone źródło, ale pokazujące definicje makr, bez powiększania makr i Instrukcje '# include' są nienaruszone. –

+1

Poniższe odpowiedzi mają to.Ogólna nazwa tej strategii to "X macros", wikipedia ma stronę o tym: http://en.wikipedia.org/wiki/X_Macro jak robi Dr Dobbs: http://www.drdobbs.com/cpp/the -x-macro/228700289 – Vicky

+0

@Vicky drdobbs link was a gem! dzięki! – ashishsony

Odpowiedz

7

Zwykle zmniejsza się, gdy należy zastosować inne leczenie dla tego samego źródła danych.

Na przykład można zrobić:

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

, a następnie:

enum MyEnum { 
# define X(e) e, 

    MY_LIST 

    Last 

# undef X 
}; 

W takim przypadku, MY_LIST jest rozszerzany za pomocą definicji Xprąd.

Teraz w tym samym pliku, mogę również użyć MY_LIST aby utworzyć to_string metoda

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 

ten sposób zbiór wartości wyliczenia jest tylko kiedykolwiek napisany raz i automatycznie zarówno wyliczenia i wiele metod, które sobie z tym radzą, jest zsynchronizowanych z tym zestawem.

1

Ma to na celu uniknięcie powielania listy żądań. Ta lista wymaga tylko zachowania w jednym miejscu, definicji REQ_INFO, a struktura wyliczeniowa i danych jest automatycznie generowana z tej listy odpowiednimi definicjami REQ_GROUP i REQ_.

Bez tych makr wyliczenie i struktura danych musiałyby być utrzymywane osobno, przy zachowaniu spójności między nimi, co wymagałoby więcej pracy i większej ilości błędów.

1

możliwość pominięcia ważnych określa w tym samym pliku:

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

Tak więc, na przykład, struktura zostały pokazane rozszerza się na:

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

Jak inne odpowiedzi wspomniano, że odbywa się to głównie aby zsynchronizować wiele struktur/modułów wyliczających.

Powiązane problemy