如何实现类型名跟类型的对应, 我们很容易想到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时保存下来的调试信息.当我发现这个时有点小兴奋, 他解除了一个限制条件, 使得这种想法或许可以有更大的实用之处.
最后再说点什么:
这是自己第一次写博客, 自从上了大学以后, 没学过语文, 以前并不知道语文是这么重要, 直达现在发现自己并不能将自己想要说的流畅地, 有层次地表达出来, 写下来. 这几篇博客修修补补还是花了自己一点时间. 如果你在看完之后, 笑道这都写了写什么东西啊, 我也并不会介意的. 以前是懒与去经营自己的博客, 当发现自己写出属于自己的博客时, 还是挺有满足感的, 我也会继续写下去, 继续锻炼, 或许多年以后翻翻自己写过的东西, 回信一下自己当时的所思所想, 也将是已将很有趣的事情吧.