在c++中实现反射的初步想法
最近在思考如何在c++中实现反射。事情的起因是这样的:我们服务器是用c++开发的,如果需要写一些测试用的GM指令的话,需要编写完GM代码后重新编译并且重启进程,工序繁琐且比较耗时。因此就有了想用脚本(lua或py)来写GM的想法。用脚本来做这事我觉得还挺适合的。首先可以免去编译、重启(通过脚本的reload)的过程。其次测试用的GM没有高性能的需求,并且一般在进行自测的阶段时,代码往往是频繁改动的,刚好适合用灵活的脚本:P
接下来的问题就是,要如何在项目中引入脚本。考虑到使用场景,我觉得这个脚本机制最核心的一点就是:不需要显示的定义出c++暴露给脚本的接口。因为这块主要是在测试环境下工作,因此主要关注的是使用更便捷。如果每一个c++模块都需要再手动写一个wrapper类才能用脚本调用它,这样就太繁琐了,完全背离了减负的初衷。
首先我想到的是自动生成wrapper类的代码,比如说使用SWIG。方法可以是把需要生成wrapper的模块(实际上c++没有模块,只有编译单元)的文件名集中写在一个文件,在编译构建时扫描文件中记录的模块,统一生成wrapper类。但这样其实并没有做到业务代码完全无感知,只是把写wrapper类的工作优化成写模块名:-(
那么扫描项目中的所有模块呢?不管是否需要,统统都生成一份wrapper。这似乎可行。不过我先不考虑它,因为我心里还有另外一个方法。
在前一个项目中,我了解到了一种通过反射来在c#中嵌入cpython虚拟机的方法。因此我在想,如果能在c++中实现一个反射,那不就可以做到用脚本调用c++代码了吗。
要在c++实现反射,不可避免的是要把一个模块(编译单元)的入口记录下来。其中入口包括了函数、全局变量、类的类型信息等等。一般的做法可能是显示的定义出一个模块的入口。优雅一点的可能是在.h文件里稍动手脚,悄悄的把入口信息记录下来。
但是我更希望是,在实现反射时能对原有代码做尽可能少的改动。并且最好可以和业务代码完全的分离。在线上环境下,能够不产生任何性能开销。因此,我想到了是另外一种方法。
实际上在编译的过程中,是有模块的入口信息的(比如说符号表)。但是在编译完之后,这些信息就被丢掉了。如果把这些编译的中间信息存下来,存成一个单独的文件,那么在运行时就可以在通过在文件中查找对应的信息,来实现反射了。类似于在调试时通过载入可执行文件对应的pdb,来获取调试信息一样。