【搬運】實現一個有意思的TODO巨集
阿新 • • 發佈:2019-01-25
實現一個能產生warning的TODO巨集,用於在程式碼裡做備忘,效果:
下面一步步來實現這個巨集。
Let’s do it
手動讓編譯器報警(報錯)可以用以下幾個方法:
#warning sunnyxx #error sunnyxx |
但我們知道,帶#
的預處理指令是無法被#define
的。好在C99提供了一個_Pragma
運算子可以把部分#pragma
指令字串化:
// 等價於 _Pragma("message \"sunnyxx\"") // 需要注意雙引號的轉義 // 或 _Pragma("message(\"sunnyxx\")") // 需要注意雙引號的轉義 |
利用這個特性,我們就可以將warning定義成巨集
#define SOME_WARNING _Pragma( 報告大王!\ )int main() { SOME_WARNING // [!]報告大王! return 1; } |
接下來,我們讓這個巨集能夠接受入參,並顯示到warning中去,這裡會面臨巨集的基本用法的考驗。
#define STRINGIFY(S) #S |
個人認為不太可能在一個巨集定義中完成這件事,需要用到輔助巨集:STRINGIFY(S)
將入參轉化成字串,省去了_Pragma
中全串加轉義字元的困擾。
這時,一個基本功能的TODO巨集就完成了,下面向其中加入額外的資訊:
// 兩個已有的巨集 #define STRINGIFY(S) #S #define PRAGMA_MESSAGE(MSG) _Pragma(STRINGIFY(message(MSG))) // 延遲1次展開的巨集 #define DEFER_STRINGIFY(S) STRINGIFY(S) |
其中涉及到的知識:
- 兩個常量字串可以拼接成一個整串 “123””456” => “123456”
- 使用到3個預定義巨集,
__COUNTER__
巨集展開次數的計數器,全域性唯一;__FILE__
當前檔案完整目錄字串;__LINE__
在當前檔案第幾行 - 在字串中預定義巨集應延時展開,如果將上面的
DEFER_STRINGIFY
換成STRINGIFY
的話,如__LINE__
就不能被正確展開成行數,而是成了一個常量字串"__LINE__"
- 為了美化,warning message中可以使用
\n
換行
於是,使用FORMATTED_MESSAGE(MSG)
巨集就可以將帶檔案路徑、序號、行數等資訊加入到最終的warning中。
其實到這步已經OK了,為了讓這個巨集更加搶眼,還可以借鑑RAC,把巨集定義成前面加@
的形式:
#define KEYWORDIFY try {} @catch (...) {}
|
將最終的巨集定義前面加上上面的巨集後,使用時就可以加@
字首了(空的try-catch會被編譯器優化,所以沒啥效能損耗)
最終版本
#define STRINGIFY(S) #S #define DEFER_STRINGIFY(S) STRINGIFY(S) #define PRAGMA_MESSAGE(MSG) _Pragma(STRINGIFY(message(MSG))) #define FORMATTED_MESSAGE(MSG) DEFER_STRINGIFY(__COUNTER__) MSG \DEFER_STRINGIFY(__FILE__) " line " DEFER_STRINGIFY(__LINE__) #define KEYWORDIFY try {} @catch (...) {} // 最終使用下面的巨集 #define TODO(MSG) KEYWORDIFY PRAGMA_MESSAGE(FORMATTED_MESSAGE(MSG)) |
What’s more
除此之外,還研究了半天如何在巨集裡面定義一個註釋,這樣就可以偷偷寫// TODO: ...
的註釋,讓Xcode導航欄中也出現這個TODO了:
但很可惜沒有找到一個可行的方法,歡迎一起解決。
Xcode外掛《XTodo》也是利用這個特性,可以嘗試下。
如果需要一個產生error的巨集,將這裡替換成這樣就好了:_Pragma(STRINGIFY(GCC error(MSG)))