如何實現類型名跟類型的對應, 我們很容易想到map, 沒錯, 就是使用map實現的. std::map<std::string, .....>, 等下, 第二部分該填什麼類型, 一個函數指針, auto create()? auto只是占位符, 編譯器好像不會讓你通過吧. 我們需要一種容器, 可以存放所有的類型, 模板.
由於聲明這個容器是並不能包含模板參數, 這裡借鑒了boost 庫中any的代碼, 原理如下:
1 class Container { 2 3 private: 4 5 class bridge { 6 7 public: 8 bridge(){} 9 virtual ~bridge(){} 10 virtual void invoke(unsigned long long addr, const std::vector<const char*>& args) = 0; 11 virtual void* instanceOfClass() = 0; 12 }; 13 14 template<typename ValueType> 15 class holder: public bridge { 16 17 public: 18 virtual void invoke(unsigned long long addr, const std::vector<const char*>& args) override{}; 19 virtual void* instanceOfClass() override{}; 20 }; 21 22 template<typename ValueType> 23 class holder<ValueType*>: public bridge { 24 25 public: 26 holder(ValueType* item){} 27 virtual ~holder() {} 28 29 virtual void invoke(unsigned long long addr, const std::vector<const char*>& args) override{ 30 31 } 32 33 virtual void* instanceOfClass() override{ 34 35
36
37 } 38 }; 39 40 bridge* content_; 41 public: 42 Container(){} 43 ~Container(){ 44 Alloc::dellocate(content_); 45 } 46 47 template<typename ValueType> 48 Container(ValueType* item) { 49 50 holder<ValueType*>* h = static_cast<holder<ValueType*>*>(Alloc::allocate(sizeof(holder<ValueType*>))); 51 new(h) holder<ValueType*>(item); 52 content_ = h; 53 } 54 55 // three arguments most, and type are char* 56 void invoke(unsigned long long addr, const std::vector<const char*>& args) { 57 58 content_->invoke(addr, args); 59 } 60 61 void* instanceOfClass() { 62 63 return content_->instanceOfClass(); 64 } 65 66 };
Container只是一層包裝, 隱藏了模板參數, 真正存儲類型的是繼承bridge的holder子類, bridge提供接口, 由於純虛函數不能是模板函數, 所以返回實例是必須強制轉型為void* 指針, 由客戶端再強制轉型回來.如何存儲一個類類型的信息呢, 最簡單, 保存該類型的一個指針變量就行. 其他的交由編譯器的模板處理. 然後將類注冊, 即插入map中. 可以通過宏來實現.
好了, 到了最後一步, 函數類型問題. 我們的目標是在配置文件中聲明類似的語句:
<Function name="declation", scale = "Init">
<Argument>One</Argument>
<Argument>Two</Argument>
</Function>
只要這樣聲明就能生產 void declation(const Init*, One, Two)這樣的函數聲明.
由於文本中只能保存基本的類型, 你不可能用在文本中指定某個參數是指針吧. 還有是函數個數的問題, 我本以為模板的可變參數能起點作用, 結果發現並不是我想要的. 好吧, 所以只能再做一個限制條件, 3個參數最多, 如果你寫的函數參數多余三個, 我想你肯定有辦法減少參數個數的, 還有參數類型都是字符串, 從字符串到其他基本類型的轉換必須由函數自己解決. 這樣, 解決思路很清晰了, 首先根據scale類型獲取容器, 根據函數名獲取地址, 根據參數個數獲取函數類型, 最後執行.
所有的源代碼我都上傳在github上面, reflect
補充:
之前我一直以為該實現反射的方式只能在debug下才有效, 應為debug下編譯器會玩可執行文件中加入大量的調試信息, 當使用release版本時, 並沒有這些編譯信息, 或許可以使用之前保存過的信息, 但可能會對內存地址有所影響. 之後我詳細了解了elf文件格式後, 發現並不會影響, 至少我目前的測試是沒有影響的. debug時是將調試信息以dwarf格式的段添加到可執行文件後, 而並不是隨機穿插在程序中, 所以只有release時代碼並沒有修改, 注意宏的修改, 就可以使用debug時保存下來的調試信息.當我發現這個時有點小興奮, 他解除了一個限制條件, 使得這種想法或許可以有更大的實用之處.
最後再說點什麼:
這是自己第一次寫博客, 自從上了大學以後, 沒學過語文, 以前並不知道語文是這麼重要, 直達現在發現自己並不能將自己想要說的流暢地, 有層次地表達出來, 寫下來. 這幾篇博客修修補補還是花了自己一點時間. 如果你在看完之後, 笑道這都寫了寫什麼東西啊, 我也並不會介意的. 以前是懶與去經營自己的博客, 當發現自己寫出屬於自己的博客時, 還是挺有滿足感的, 我也會繼續寫下去, 繼續鍛煉, 或許多年以後翻翻自己寫過的東西, 回信一下自己當時的所思所想, 也將是已將很有趣的事情吧.