2010-05-28 24 views
6

Tôi muốn làm rõ điều này: Tôi biết cách hoạt động của mẹo này, những gì tôi muốn là liên kết đến giải thích rõ ràng để chia sẻ với người khác.Tìm kiếm lời giải thích tốt về thành ngữ macro tạo thành bảng

Một trong những câu trả lời cho một cuộc thi C macro question về thành ngữ "macro X" hoặc "chưa được xác định vĩ mô". Điều này bao gồm việc xác định cái gì đó như:

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

Sau đó, để tạo ra, chẳng hạn, một mảng các giá trị với chỉ số tên bạn làm:

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

    MAGIC_LIST 

#undef X 
} NamedDefaults; 

Bạn có thể lặp lại các thủ tục với một khác nhau #define cho X() để tạo ra một mảng giá trị và có thể gỡ lỗi các chuỗi, v.v.

Tôi muốn một liên kết giải thích rõ ràng cách hoạt động của quảng cáo, được quảng cáo tại một người quen thuộc với C . Tôi không biết mọi người thường gọi kiểu này là gì, vì vậy, những nỗ lực của tôi để tìm kiếm trên web cho nó đã thất bại từ trước tới nay.

(Nếu có như vậy một lời giải thích trên SO, mà muốn được tốt ...)

+0

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

Trả lời

4

đầu tiên tôi học được về macro X trong Dr Dobbs Journal (hay là Tạp chí C của người sử dụng?), Trong một bài báo về C99 bởi Randy Meyers.

Mặc dù đã hết mag, bài viết là online here.

5

Trang Wikipedia về tiền xử lý C đề cập đến nó nhưng không rực rỡ rõ ràng IMO: http://en.wikipedia.org/wiki/C_preprocessor#X-Macros

tôi đã viết một bài báo về nó cho nhóm của tôi; cảm thấy tự do để sử dụng điều này nếu bạn muốn.

/* 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. 
*/ 
Các vấn đề liên quan