2010-05-28 14 views
6

Chcę to wyjaśnić z góry: Wiem, jak działa ta sztuczka, czego chcę, to link do jasnego wyjaśnienia do dzielenia się z innymi.Szukasz dobrego wyjaśnienia idiomu generowania tabeli

Jedna z odpowiedzi na C macro question mówi o makrze "X makro" lub "jeszcze nie zdefiniowane makro". Wiąże się to definiowanie coś takiego:

#define MAGIC_LIST \ 
    X(name_1, default_1) \ 
    X(name_2, default_2) \ 
    ... 

Następnie do tworzenia, powiedzmy, tablicę wartości z wymienionych indeksów zrobić:

typedef enum { 
#define X(name, val) name, 

    MAGIC_LIST 

#undef X 
} NamedDefaults; 

można powtórzyć procedurę z innym #define dla X() aby utworzyć tablica wartości, i może debugowanie ciągów itp.

Chciałbym link do jasnego wyjaśnienia, jak to działa, rozbił się na kogoś, kto jest dobrze znany z C . Nie mam pojęcia, co wszyscy zazwyczaj nazywają tym wzorcem, więc moje próby przeszukiwania sieci nie udały się do tej pory.

(Jeśli istnieje takie wyjaśnienie na SO, to byłoby w porządku ...)

+0

Powiązane: http://stackoverflow.com/questions/6635851/real-world-use-of-x-macros/6636596#6636596 –

Odpowiedz

4

raz pierwszy dowiedział się o makr X w Dr Dobbs Journal (czy był to Urzędowy C użytkownika?), W artykule o C99 przez Randy Meyers.

Chociaż magazyn zniknął, artykuł brzmi: online here.

5

Strona Wikipedii o C preprocesora wspomina go, ale nie jest jasne, znakomicie IMO: http://en.wikipedia.org/wiki/C_preprocessor#X-Macros

napisałem referat o tym mojej grupy; nie krępuj się używać tego, jeśli chcesz.

/* X-macros are a way to use the C pre-processor to provide tuple-like 
* functionality that would not otherwise be easy to implement in C. 
* Any time you find yourself writing a comment that says something 
* like "These values must be kept in sync with the values in typedef enum 
* foo_t", or adding a new item to a list and copying and pasting functions 
* to handle it, then X-macros are probably a better way to implement the 
* behaviour you want. 
*/ 


/* Begin with the main definition of the table of tuples. This can be directly 
* in the header file, or in a separate #included template file. This example 
* is from some hardware revision reporting code. 
*/ 


/* 
* Board versions 
* Upper bound resistor value, hardware version, hardware version string 
*/ 
#define APP_HW_VERSIONS \ 
    X(0, HW_UNKNOWN, UNKNOWN_HW_VER) \ 
    X(8, HW_NO_VERSION, "XDEV") /* Unversioned board (e.g. dev board) */ \ 
    X(24, HW_REVA,  "REVA") \ 
    X(39, HW_REVB,  "REVB") \ 
    X(54, HW_REVD,  "REVD") \ 
    X(71, HW_REVE,  "REVE") \ 
    X(88, HW_REVF,  "REVF") \ 
    X(103,HW_REVG,  "REVG") \ 
    X(118,HW_REVH,  "REVH") \ 
    X(137,HW_REVI,  "REVI") \ 
    X(154,HW_REVJ,  "REVJ") \ 
    /* add new versions above here */ \ 
    X(255,HW_REVX,  "REVX") /* Unknown newer version */ 


/* Now, any time you need to use the contents of this table, you redefine the 
* X(a,b,c) macro to give the behaviour you want. In the hardware revision 
* example, the first thing we need is an enumerated type giving the 
* possible options for the value of the hardware revision. 
*/ 

#define X(a,b,c) b, 
typedef enum { 
APP_HW_VERSIONS 
} app_hardware_version_t; 
#undef X 

/* The next thing we need in this example is some code to extract the 
* hardware revision from the value of the version resistors. 
*/ 
static app_hardware_version_t read_board_version(
    board_aio_id_t identifier, 
    board_aio_val_t value 
    ) 
{ 
    app_hardware_version_t app_hw_version; 

    /* Determine board version based on ADC reading */ 
#define X(a,b,c) if (value < a) {app_hw_version = b;} else 
APP_HW_VERSIONS 
#undef X 
    { 
     app_hw_version = HW_UNKNOWN; 
    } 

    return app_hw_version; 
} 

/* Now we have two different places that need to extract the hardware revision 
* as a string: the MMI info screen and the ATI command. 
*/ 

/* in the info screen code: */ 
    switch(ver) 
    { 
#define X(a,b,c) case b: ascii_to_display_string((lcd_char_t *) &app[0], c, HW_VER_STRING_LEN); break; 
    APP_HW_VERSIONS 
#undef X 
    default: 
     ascii_to_display_string((lcd_char_t *) &app[0], UNKNOWN_HW_VER, HW_VER_STRING_LEN); 
     break; 
    } 

/* in the ATI handling code: */ 
    switch(ver) 
    { 
#define X(a,b,c) case b: strncpy(&p_data, (const uint8_t *) c, HW_VER_STRING_LEN); break; 
    APP_HW_VERSIONS 
#undef X 

    default: 
     strncpy_write(&p_data, (const uint8_t *) UNKNOWN_HW_VER, HW_VER_STRING_LEN); 
     break; 
    } 

/* Another common example use case is auto-generation of accessor and mutator 
* functions for a list of storage keys 
*/ 

/* First the tuple table */ 

/* Configuration items: 
    * Storage key ID, name, type, min value, max value 
    */ 
#define CONFIG_ITEMS \ 
    X(1234, DEVICE_ID, uint16_t, 0, 0xFFFF) \ 
    X(1235, NUM_CONNECTIONS, uint8_t, 0, 8) \ 
    X(1236, ENABLE_LOGGING, bool_t, 0, 1) \ 
    X(1237, SECURITY_KEY, uint32_t, 0, 0xFFFFFFFF) 
    /* add new items above here */ 

/* Generate the enumerated type of keys */  
#define X(a,b,c,d,e) CONFIG_ITEM_##b = a, 
typedef enum { 
    CONFIG_ITEMS 
    } config_item_t; 
#undef X 

/* Generate the accessor functions */ 
#define X(a,b,c,d,e) \ 
    int get_config_item_##b(void *p_buf) \ 
    { \ 
     return read_from_key(a, sizeof(c), p_buf); \ 
    } 
CONFIG_ITEMS 
#undef X 

/* Generate the mutator functions */ 
#define X(a,b,c,d,e) \ 
    bool_t set_config_item_##b(void *p_buf) \ 
    { \ 
     c val = * (c*) p_buf; \ 
     if (val < d || val > e) return FALSE; \ 
     return write_to_key(a, sizeof(c), p_buf); \ 
    } 
CONFIG_ITEMS 
#undef X 

/* Or, if you prefer, one big generic accessor function */ 
int get_config_item(config_item_t id, void *p_buf) 
{ 
    switch (id) 
    { 
#define X(a,b,c,d,e) case a: return read_from_key(a, sizeof(c), p_buf); break; 
    CONFIG_ITEMS 
#undef X 
    default: 
     return 0; 
    } 
} 

/* and one big generic mutator function */ 
bool_t set_config_item(config_item_t id, void *p_buf) 
{ 
    switch (id) 
    { 
#define X(a,b,c,d,e) \ 
    case a: \ 
     { \ 
      c val = * (c*) p_buf; \ 
      if (val < d || val > e) return FALSE; \ 
      return write_to_key(a, sizeof(c), p_buf); \ 
     } 

    CONFIG_ITEMS  
#undef X 

    default: 
     return FALSE; 
    } 
} 

/* Finally let's add a logging function to dump all the config items */ 
void log_config_items(void) 
{ 
#define X(a,b,c,d,e) \ 
    { \ 
     c val; \ 
     if (read_from_key(a, sizeof(c), &val) == sizeof(c)) \ 
     { printf("CONFIG_ITEM_##b (##a): 0x%x\n", val); } \ 
     else { printf("CONFIG_ITEM_##b (##a): Failed to read\n"); } \ 
    } 
    CONFIG_ITEMS 
#undef X 
} 


/* Now, when you need to add a new item to your list of config keys, you don't 
* need to update the enumerated type and copy and paste new get and set 
* functions for each new key; you simply update the table of tuples and the 
* pre-processor takes care of the rest. 
*/ 
Powiązane problemy