你用過宏##粘貼函數,然後用函數指針查找執行嗎?今天就給你說道說道

{"type":"doc","content":[{"type":"blockquote","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"總述"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"    "},{"type":"text","text":"宏的使用,大家經常會用,但是一般只是簡單定義一個符號常量,類似於#define WHEEL_SCALE_MM 0.53f、"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"#define LOG_I(tag, text_fmt, ...) log_i(tag, text_fmt, ##__VA_ARGS__) ,但是除此之外還有宏還有個##粘貼作用,可以配合#define這個常量表達式,可以做成一個宏定義指針函數列表,繼而查詢執行函數。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"    話不多說,我們開始今天的分享,首先還是會進行一下常規的描述,再分享\"##\"粘貼的妙用。"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"作者:良知猶存"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"轉載授權以及圍觀:歡迎添加微信公衆號:羽林君"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"},{"type":"strong"}],"text":"一、#define的常規操作"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/aa/aab546cccfd9c47bce4d0f1f27f8f9ff.png","alt":"","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"​"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"    #define預處理器指令和其他預處理器指令一樣, 以#號作爲一行的開"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"始。ANSI和後來的標準都允許#號前面有空格或製表符, 而且還允許在#和"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"指令的其餘部分之間有空格。但是舊版本的C要求指令從一行最左邊開始,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"而且#和指令其餘部分之間不能有空格。指令可以出現在源文件的任何地"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"方, 其定義從指令出現的地方到該文件末尾有效。我們大量使用#define指令來定義明示常量(manifest constant) (也叫做符號常量) 。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"    預處理器指令從#開始運行, 到後面的第1個換行符爲止。也就是說, 指"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"令的長度僅限於一行。然而, 在預處理開始前, 編譯器會把多行物理行處理爲一行邏輯行。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"一般我們會用#define 來進行明示常量,或者做一個簡單的宏替換函數"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"#define RX_BUF_SIZE 30#define MBEDTLS_DES_C /*數據加密*/\n#define ExitIsr Encoder_Isr \nvoid Encoder_Isr(void){ \n\tg.dir_count += (g.dir == 1)? 1 : -1;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"    每行#define(邏輯行) 都由3部分組成。第1部分是#define指令本身。第"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"2部分是選定的縮寫, 也稱爲宏。有些宏代表值(如本例) , 這些宏被稱爲"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"類對象宏 。C 語言還有類函數宏 , 稍後討論。宏的名稱中不允許有空格, 而且必須遵循C變量的命名規則:只能使用字符、 數字和下劃線(_) 字符, 而且首字符不能是數字。第3部分(指令行的其餘部分) 稱爲替換列表或替換體 。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"    "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"    一旦預處理器在程序中找到宏的實例後, 就會用替換體代替該宏。從宏變成最終替換文本的過程稱爲宏展開。注意, 可以在#define行使用標準C註釋。如前所述, 每條註釋都會被一個空格代替。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/81/811dbb499649f701ea3d1f9123b26efd.png","alt":"","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"此外我們還會比較多的使用變宏參"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過把宏參數列表中最後的參數寫成省略號(即, 3個點...) 來實現這"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"一功能。這樣, 預定義宏_ _VA_ARGS_ _可用在替換部分中, 表明省略號代表什麼。 "}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"#define PR(...) printf(_ _VA_ARGS_ _)\n"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"假設稍後調用該宏:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"PR(\"Howdy\");PR(\"weight = %d, shipping = $%.2f\\n\", wt, sp);"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於第1次調用, _ _VA_ARGS_ _展開爲1個參數:\"Howdy\"。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"對於第2次調用, _ _VA_ARGS_ _展開爲3個參數:\"weight = %d,"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"shipping = $%.2f\\n\"、 wt、 sp。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"因此, 展開後的代碼是:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"​​​​​​​"}]},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"printf(\"Howdy\");printf(\"weight = %d, shipping = $%.2f\\n\", wt, sp);"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"},{"type":"strong"}],"text":"二、#define配合##使用"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/aa/aab546cccfd9c47bce4d0f1f27f8f9ff.png","alt":"","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"​"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"    很多人應該都知道\"##\"的用法,它被稱爲預處理的粘合劑,與#運算符類似,##運算符可用於類函數宏的替換部分。而且,##還可以用於對象宏的替換部分。##運算符可以把兩個記號組合成一個記號。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"#define def_u32_array(__name, __size)     uint32_t array_##__name[__size];"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"實際中,我們可以這樣使用:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"def_u32_array(sample_buffer, 64)"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"宏展開的效果是:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"uint32_t array_sample_buffer[64];"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"同樣類比於初始化一個數組,我們也可以粘貼形成一個函數"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"下面就是在Linux內核裏面的源代碼:"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"其中這個__pcpu_size_call_return宏,通過##粘貼選擇要使用的raw_cpu_read_x 函數。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"#define __pcpu_size_call_return(stem, variable) \\\n({ \\\n typeof(variable) pscr_ret__; \\\n __verify_pcpu_ptr(&(variable)); \\\n switch(sizeof(variable)) { \\\n case 1: pscr_ret__ = stem##1(variable); break; \\\n case 2: pscr_ret__ = stem##2(variable); break; \\\n case 4: pscr_ret__ = stem##4(variable); break; \\\n case 8: pscr_ret__ = stem##8(variable); break; \\\n default: \\\n __bad_size_call_parameter(); break; \\\n } \\\n pscr_ret__; \\\n})\n#define raw_cpu_read_1(pcp) raw_cpu_generic_read(pcp)\n#define raw_cpu_generic_read(pcp) \\\n({ \\\n *raw_cpu_ptr(&(pcp)); \\\n})"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這部分是更高層次的宏定義,將##粘貼的函數再次定義爲一個宏函數"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"#define raw_cpu_read(pcp) __pcpu_size_call_return(raw_cpu_read_, pcp)\n#define __this_cpu_read(pcp) \\\n({ \\\n __this_cpu_preempt_check(\"read\"); \\\n raw_cpu_read(pcp); \\\n})"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"最後面進行執行 __this_cpu_read(current_kprobe);"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"int __kprobes arc_kprobe_handler(unsigned long addr, struct pt_regs *regs)\n{\n struct kprobe *p\n p = __this_cpu_read(current_kprobe);\n  p = get_kprobe((unsigned long *)addr);\n  \n  ... 省略多行代碼\n  \n if (p->break_handler && p->break_handler(p, regs)) {\n setup_singlestep(p, regs);\n kcb->kprobe_status = KPROBE_HIT_SS;\n return 1;\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在C++中我們也可以將做成一個指針列表,對應好每個函數的名稱後,再次調用該定義的宏參數,就實現了指針調用。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":null},"content":[{"type":"text","text":"#define AddFunc(Func) \\\nFuncPtrTemplate Func##Map(int mode_name, int state_name) \\\n{ \\\n static auto modeMap = Func##Register(); \\\n auto pair = std::make_pair(mode_name, state_name); \\\n auto mapEntry = modeMap->find(pair); \\\n if (mapEntry == modeMap->end()) \\\n return nullptr; \\\n return mapEntry->second; \\\n} \\\nbool Mode::Func(State *state) { \\\n auto state_id = getStateId(); \\\n auto p_function = Func##Map(getId(), state_id); \\\n if (p_function) \\\n return p_function(this, state); \\\n return false; \\\n}\nAddFunc(IsExit);\nint main(){\n IsExit(p.get());\n}"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這也是Linux內核中的代碼,用來print不同狀態的打印信息,如果大家想要快速掌握這些使用方法,建議大家擼一擼Linux內核源碼呢。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/9f/9f2bbf50f548385e100ed0ea3393d2e0.png","alt":"","title":null,"style":null,"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"​"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":" 這就是我分享的#define的操作方法,裏面代碼是實踐過的,如果大家有什麼更好的思路,歡迎分享交流哈。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"italic"},{"type":"strong"}],"text":"—"},{"type":"text","marks":[{"type":"italic"}],"text":"END"},{"type":"text","text":"—"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","marks":[{"type":"strong"}],"text":"推薦閱讀"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"【1】"},{"type":"link","attrs":{"href":"http://mp.weixin.qq.com/s?__biz=MzI0MTI2MDY3NQ==&mid=2247491648&idx=1&sn=523ffec9bc089bf41a4a146dfe0cb610&chksm=e90ce1acde7b68baecf4ed9e7a997801ac48939a1d0e72167e692c4cceca0a5a65ef9fa76dba&scene=21#wechat_redirect","title":null},"content":[{"type":"text","text":"c++nullptr(空指針常量)、constexpr(常量表達式)"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"link","attrs":{"href":"http://mp.weixin.qq.com/s?__biz=MzUxMjEyNDgyNw==&mid=2247487776&idx=1&sn=6f41a0b12195e1a8c808a9d317dd132e&chksm=f96863d4ce1feac277bc0218ea9f7f65754b0e652c8543149df598f4c8ef87f3e1646de0de4c&scene=21#wechat_redirect","title":null},"content":[{"type":"text","text":"【2】"}]},{"type":"link","attrs":{"href":"http://mp.weixin.qq.com/s?__biz=MzI0MTI2MDY3NQ==&mid=2247483803&idx=1&sn=1cbc72c1985965c052c374eb627d4446&chksm=e90f0077de788961258f46eed6149498f6f7c907b8da4a2090ada295158bec3b46c2a9b628a6&scene=21#wechat_redirect","title":null},"content":[{"type":"text","text":"嵌入式底層開發的軟件框架簡述"}]},{"type":"text","text":" "},{"type":"link","attrs":{"href":"http://mp.weixin.qq.com/s?__biz=MzUxMjEyNDgyNw==&mid=2247488626&idx=1&sn=c9c045382502de4417a48cf63fffc691&chksm=f9686686ce1fef90b096ddbf70129785b8e0b9666a3367e706364543ea81d728b61323747e2f&scene=21#wechat_redirect","title":null},"content":[{"type":"text","text":"必讀"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"【3】"},{"type":"link","attrs":{"href":"http://mp.weixin.qq.com/s?__biz=MzI0MTI2MDY3NQ==&mid=2247489695&idx=1&sn=d7dd58878070d34a6ee3a21be035e6e1&chksm=e90f1973de789065101b678830244790e06a19318162765327e6d0103a01b7d2a11827643956&scene=21#wechat_redirect","title":null},"content":[{"type":"text","text":"CPU中的程序是怎麼運行起來的"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"【4】"},{"type":"link","attrs":{"href":"http://mp.weixin.qq.com/s?__biz=MzI0MTI2MDY3NQ==&mid=2247490441&idx=1&sn=3be088b25663c72ba43d3f42f916acf5&chksm=e90f1a65de78937331e9beb61f463d332c705b086b763d04f027bcda2c68b655c34d1c66f9e6&scene=21#wechat_redirect","title":null},"content":[{"type":"text","text":"C++的匿名函數(lambda表達式)"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"【5】"},{"type":"link","attrs":{"href":"http://mp.weixin.qq.com/s?__biz=MzI0MTI2MDY3NQ==&mid=2247490871&idx=1&sn=c0fb6e8303302247b3a08c4b16acc4bd&chksm=e90f1cdbde7895cdcdf789e7056a6295cbdffc05cd1272f78450d5cbbdf62bc3edee995d3e92&scene=21#wechat_redirect","title":null},"content":[{"type":"text","text":"階段性文章總結分析"}]}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"本公衆號全部原創乾貨已整理成一個目錄,後臺回覆【資源】即可獲得。"}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章