类库大魔王
类库大魔王 懒惰,傲慢,以及无耐心

使用SWIG扩展EXE内嵌入的脚本解释器

  虽然几年前就知道SWIG了,但一直以来都没真正用过,又是一次叶公好龙。
  今天在公司里跟同事偶然谈起项目里嵌入的Ruby解释器的问题,于是回来兴致高涨,决定研究继续研究一下CryptTool遗留下来的问题。
  首先介绍一下CryptTool。这是一个可以使用多种算法计算文本或文件内容的hash值的小工具,最早的原型是几年前还在大学时写的一个计算文件md5的小程序,去年做一体化平台时,有一项内容是要用一个单向散列值来近似唯一地标识一个文件,所以顺便找了一个几种常用的hash算法源代码,包括md2/4/5、SHA1/224/256/384/512、haval3/4/5、CRC、GHash、Gost3/5、RMD128/160/256/320、Adler32、FCS等。回家用MFC写了一个,完成了多种算法计算的功能,当时突然兴起,觉得可以嵌入脚本解释器,实现外部插件扩展。结果只是实现了搜索指定目录下的脚本文件,并添加相应的项到主菜单中,通过菜单项激活脚本执行。连脚本执行都没实现,因为当时贪心,又觉得好玩,打算把Python、Ruby、Lua都嵌入进来,确实都链接进来了,却没有实际的功能。
  再简单介绍一下SWIG。早先,我只知道它是用于嵌入和扩展脚本解释器的,但具体详细的作用却并不清楚。现在,我有了新的认识,SWIG主要作的是扩展脚本解释器时,将C++代码做一层封装,实现自动向脚本解释器注册相关的信息(如函数、变量、模块等等)。说起脚本解释器的扩展,大多数的资料,甚至连SWIG的帮助文档里,都是说的将C++代码编译生成动态链接库,然后官方的脚本解释器会载入该动态链接库,并在解释执行脚本时使用动态链接库中的内容。而我现在的需求,前面已经略有提及,是把我自己的exe程序里嵌入的脚本解释器扩展了,并且我要的是实现该扩展功能的代码是exe的一部分,而不是独立的动态链接库。两年前,用C++ Builder写过一个用于数据处理的小程序,其中就花了不少精力实现了嵌入Python、TCL、Lua,并扩展了几个函数,当时全部都是通过手工编写代码,按照标准的官方推荐通用作法实现,以至于连续近2个月几乎每天都是后半夜2点才睡,到后来精疲力竭,连上WC掀马桶盖都觉得异常吃力。
  现在用VC实现嵌入和扩展理论上应该也不会有多少差别,只不过扩展部分是用SWIG完成的。先写一个后缀为.i的文件,定义扩展的模块名和扩展的函数原型,如果有变量也写上。然后在命令行中运行SWIG,用法行简单,命令行参数也很简单,因为是放在MFC工程中,所以理所当然地把输出限定为C++类型的代码。这里值得提一下的是,指定输出的文件名,用.h文件比较合适,它里面是一堆函数的实现,但并没有单独的原型声明,所以在实际工程中,只要直接include该文件就行,而不是当成源代码.cpp添加到工程,不然编译会比较麻烦。另外还有一点是,写在.i文件中的变量类型,不要用宏定义,而是用实际的标识法,开始我用LPCSTR和LPSTR分别表示const char *和char *,结果在脚本调用时,就会报类型不匹配,直接写成const char*和char *就没有问题了。再有一点,SWIG生成的代码中,会有一个初始化的函数,该函数完成各个扩展的登记注册功能,在EXE内嵌入脚本解释器后,需要自己调用这个函数。而且从该函数可以看出,Lua的就需要一个lua_State指针,说明Lua是能支持进程内嵌入多个解释器的,而Ruby和Python都是没有参数的,说明一般说来只提倡一个进程内嵌入一个解释器。
  曾经看到有人(似乎是SWIG的作者吧)说,SWIG生成的代码可读性很差,今天我看了看,觉得还可以,需要关心的部分还是能大概猜出一点意思的,而其他的都是辅助性的代码,也就是完成实际的接口转换工作的。除了这个问题,还看到网上有人说过,SWIG实现的粘合层,在效率上会不如直接手工写的那种,今天我看这些生成的代码,觉得此种言论是立不住脚的,因为完成实际接口转换工作的代码都是固定的,应该也是SWIG的开发人员先手工写好的,在效率问题上并不是值得纠缠的。
  用SWIG扩展exe内嵌入的脚本解释器,可以大大减少工作量,降低出错的机率,实在是一种值得推广的工具。比如最近在考虑的,在C++程序中实现一套仿Eclipse的插件机制,使用外部脚本实现程序的业务逻辑,这时用SWIG就可以直接将大量内部核心接口转换成内嵌的脚本解释器可接受的形式,极大地提高工作效率,对于使用COM接口的方案,真的是不太感冒!

感觉本文不错,不妨小额鼓励我一下!
支付宝扫一扫

支付宝扫一扫

微信扫一扫

微信扫一扫

如果你看不到评论框,说明Disqus被墙了。