[C++] 優雅玩耍函數指標呼叫,把你同事玩弄得嫑嫑的。(離職前記得回顧這篇文)
前言
最近正好寫一些玩具想模組化,以前在處理 Function Pointer 都是強轉型 + typedef 然後 ^C^V 瘋狂複製貼上函數型別來做到 C/C++ 內對指標函數呼叫。最近正好摸到一些 C++11 有支援到的正規轉型別方式,發現乾...XD 不用在手殘複製貼上啦,原來 C++11 一堆關鍵字已經可以讓你快速定義型別 + 把函數模組化放到自己的樣板內了超ㄎㄧㄤ
btw 這篇只是筆記文,最一開始我只是想查 C/C++ 內到底能不能編譯時期就取得函數的型別XDDDD(結果是:幹,居然真的可以咧XDDDDD)
另外,這篇都在講 Windows 上的做法,不過理論上把 LoadLibrary 跟 GetProcAddress 替換為對應 macOS/Linux 的函數應該都可以 work(N 年前好像有玩一下 macOS 上是可以跑的)
考.考.考.考考古學家
可以先參考這篇 [C++] How to GetProcAddress() like a boss 裡面純 C 寫法的強轉型函數指標做法,這相信常玩函數指標的人都有用過這樣玩法:
原理就只是:
- 宣告一個新的函數型別 ShellAboutProc,型別、呼叫約制跟你想呼叫的系統函數必須一致(對啦,不然等等堆疊爛掉你就知道惹)
- 接著透過 LoadLibrary() 載入系統函數模組取得模組地址、透過 GetProcAddress() 取得該函數位於該函數庫上真正地址
- 以 ShellAboutProc 函數型別宣告一個變數 shellAbout
- 最後將該函數地址強轉型為我們定義好的 ShellAboutProc 型別覆寫入 shellAbout 變數內,大功告成 la,shellAbout 變數就可以被當原生函數呼叫了
這做法超簡單、也實用,不過看也知道一堆地方可以省略XD,比方說根本不需要額外開一個變數來暫存函數指標,可以直接用內聯函數型態的方式、取代宣告函數型別與變數(離題惹)
進階優雅玩法
我寫這篇筆記前有先 survey 了一些文章,有興趣可以參考:Fixing Function Pointers with decltype、Get types of C++ function parameters、 [C++] How to GetProcAddress() like a boss,看完之後你應該會知道大概就是像這樣:
簡單來說:
- typedef decltype(&函數名) 型別名; 可以由 &函數名 refer 到函數型別(純函數名拿到的是函數記憶體地址)再透過 decltype 取出函數宣告型別,然後透過 typedef 將其命名為 a 型別。
- 透過 GetProcAddress() 取得函數地址後,透過 reinterpret_cast<a> 關鍵字可以將括弧中物件轉型為 T 型別,最後這樣回傳回來就是 a 型別的函數宣告好的原生函數,就可以當原生函數呼叫啦
當然上面看起來很醜,你也可以像底下這樣一句話:
(reinterpret_cast(GetProcAddress(LoadLibraryA("User32"), "MessageBoxA")))(0, "hi", "info", 0);
這樣還不夠
如果你跟我一樣非常偷懶,希望把函數物件化來呼叫...XD。參考了一下 [C++] How to GetProcAddress() like a boss,大概就可以寫出這樣的東西:
後續
這種花式玩法主要依賴的兩個關鍵字,可以上微軟爸爸 MSDN 官網上查閱 decltype (C++)、reinterpret_cast Operator。reinterpret_cast 早在 VC2008 之後就支持了、而 decltype 也在 VC2010 也支持了,基本上大部分現在的 IDE 要這樣玩應該都 ok la 我猜
留言
張貼留言