巨集不僅是簡單的文字替換,## (Token Pasting) 與 # (Stringification) 兩大預處理運算子,能在編譯期構建識別碼、生成字串字面值,是 ATL、MFC、Linux Kernel 等大型專案廣泛使用的元編程利器。
## 連接符功能:將兩個 Token 合併為單一 Token。限制:不能出現在巨集參數列表的首尾。
#define PASTER(n) printf("token" #n " = %d", token##n)
int token9 = 9;
PASTER(9);
展開過程:
#n → "9" (字串化)token##n → token9 (連接)printf("token" "9" " = %d", token9); → 輸出 token9 = 9# 字串化運算子將巨集參數轉為字串字面值,保留原始拼寫。常用於診斷輸出、序列化標籤生成。
IMPLEMENTS_INTERFACE#define IMPLEMENTS_INTERFACE(Itf) \
{ &IID_##Itf, ENTRY_IS_OFFSET, BASE_OFFSET(_ITCls, Itf) },
使用 IMPLEMENTS_INTERFACE(ICat) 時,IID_##Itf 展開為 IID_ICat,自動對應介面 GUID。這避免了手寫重複代碼,並確保介面名稱與 GUID 變數命名一致性。
do { ... } while(0)#define wait_event(wq, condition) \
do { \
if (condition) break; \
__wait_event(wq, condition); \
} while (0)
為何用 do { } while(0)?
if (...) wait_event(...); else ... 而不改變控制流。break 提前退出,模擬函數返回行為。# + ## 協同#define DECLARE_EVENT_HANDLER(EventName) \
void On##EventName(); \
static const char* Get##EventName##Name() { return #EventName; }
// 使用
DECLARE_EVENT_HANDLER(Click)
// 展開為:
// void OnClick();
// static const char* GetClickName() { return "Click"; }
## 不能產生關鍵字、非法標識符。# 與 ## 會阻止參數的進一步展開,若需先展開再連接,需雙層巨集:#define CAT(a,b) a##b#define EXPAND_CAT(a,b) CAT(a,b)constexpr/模板元編程替代。掌握 ## 與 #,能讓你讀懂 ATL/MFC/Linux 核心巨集庫,也能編寫極具表達力的編譯期代碼生成器。但在現代 C++ 中,請優先考慮 constexpr、std::string_view、模板別名等類型安全方案,僅在跨平台 C 相容、極致效能、或舊代碼維護場景下使用預處理器黑魔法。