2020年10月17日 星期六

C\C++ X-Macro 初步心得

第一次看到X-macro這個技術時,是在[C/C++] enum to string 的方法實作 (X Macro)這篇文章中,
當時看得「霧霧煞煞」(台語:一頭霧水之意),但由於是滿有趣的技巧,
所以就先將其存在evernote中了。
昨天為了寫個功能,想做類似的事
所以特別打開evernote中的記事來研究一下,
終於看懂這個東西了,
所以在這邊記錄一下我對X-Macro的理解!

第一次看到X-Macro時,總是無法將三個小巨集(Macro)連起來,
看懂後發現是我想的太複雜了

先來個例子:
一般而言,將enum轉成字串,可能會用下面的方式
#include <iostream>

enum eOption
{
option1,
option2,
option3
};

char* OptStr[] = {"option1", "option2", "option3" };

const char* enum2str(eOption idx)
{
return OptStr[idx];
}

int main(int argc, char* argv[])
{

printf("%s\n", enum2str(eOption::option1));
printf("%s\n", enum2str(eOption::option2));
printf("%s\n", enum2str(eOption::option3));

system("pause");
return 1;
}

若用X Macro寫,則是
// Here using X Macro
#include <iostream>

// define TABLE
#define TABLE \
SUBSTITUTED(option1, "option1") \
SUBSTITUTED(option2, "option2") \
SUBSTITUTED(option3, "option3")

// using TABLE to generate enum
#define SUBSTITUTED(a, b) a,
enum eOption
{
TABLE
};
#undef SUBSTITUTED

// using TABLE to generate string array
#define SUBSTITUTED(a, b) b,
char* OptStr[] = {TABLE};
#undef SUBSTITUTED

const char* enum2str(eOption idx)
{
return OptStr[idx];
}

int main(int argc, char* argv[])
{

printf("%s\n", enum2str(eOption::option1));
printf("%s\n", enum2str(eOption::option2));
printf("%s\n", enum2str(eOption::option3));

system("pause");
return 1;
}


看起來複雜,但其實很簡單
這裡分兩個部分:
定義用的巨集

使用時的巨集
以及

定義的巨集比較沒問題,
就只是很簡單的定義 
「 『SUBSTITUTED(option1, "option1") SUBSTITUTED(option2, "option2") SUBSTITUTED(option3, "option3")』就是『TABLE』」,
比較有問題的應該是使用的時候。

其實使用的時候,是定義一個巨集,
將前一個巨集的SUBSTITUTED(option1, "option1")替換掉,
以宣告enum為例,就是用輸入的「a」加上逗號(也就是「a,」)來替換掉SUBSTITUTED;
第二個部分就是使用輸入的「b」加上逗號(也就是「b,」)來替換掉SUBSTITUTED
至於#undef,就是為了將巨集的使用限制在某個小小的區域而已。

其實使用X Macro寫及不使用X Macro是一樣的,
差別只是可以集中修改。
當專案龐大時,若未使用X Macro,
就要一次修改兩份檔案,一直切換頗麻煩的,而且容易出錯;
若使用 X Macro,就只需要在一個檔案中修改,
省去切換的麻煩。

X Macro還可以用來做很多事,
在參考資料[3]中,就用來存函式指標的陣列,
還有趣的!
X Macro在C語言及C++ 11以前的編譯器中,
用處滿廣的,可用來做mapping table,
但C++ 11後,有std::map,
其作用就相對小很多。

不還一點,就是使用X Macro會使可讀性就會稍微降低些,
除非有一定熟悉程度的人來維護,
不然會有點麻煩。

程式碼已上傳git(這裡),
有興趣的可以去看看

雖然研究完,發現跟我想做的事還是有點小差距,
我想做的是執行時期的mapping,
而x macro是編譯時期的mapping(就跟巨集一樣),
看來我還是只能乖乖使用class來實現!


參考資料:
這封郵件來自 Evernote。Evernote 是您專屬的工作空間,免費下載 Evernote

沒有留言:

張貼留言