我是C++语言的忠实拥趸,由于在上学时经历了资源匮乏的DOS时代,对C/C++这种更加接近硬件的语言由衷的喜爱。一直以来也是已C++作为工作的语言,对别的语言那是不屑一顾。在java火爆流行的时候,没有去深入了解过。现在看其实是一种损失,若非如此这篇博文或许能早几年就写出来了。然后由于工作的关系学了一点C#,在看到关于元数据的时候,萌生了在C++中实现类似功能的念头。后来接触过一些脚本语言,发现借助元数据是实现C++和脚本语言混合编程的好办法。
最初的时候,我尝试使用模板来实现这样的功能。经过一段时间的实践后,发现有些问题不易解决,而且用起来也不太方便,写了一半就放弃了。后来联想到微软使用idl这种接口描述语言来定义COM接口,决定使用类似的方法。大体就是定义一种接口描述语言,通过相应的编译器生成对应的C++头文件以及元数据描述代码,同时提供一个运行时库作为使用这些元数据的基础。这个编译器就是idlcpp,意为用接口描述语言生成C++代码,当然此处的idl不是微软的idl,只不过因意义相似,也用了同样的英文简写。
此idl语言到目前为止支持如下概念:命名空间,类和结构,数据成员,属性,方法,静态成员,虚函数,函数重载,运算符重载,多重继承,枚举,模板等。其中关键字大部分照抄的C++,所以乍看起来比较像C++代码。
除了idlcpp这个编译器外,还有一个基础的运行时库,以及针对不同的脚本语言相应的插件(目前只有lua和python),使用的开发工具是Visual Studio 2015,对非MS平台不太熟悉,所以暂时编译器只支持Windows平台(用到了一些Windows特有的函数)。不过运行时库没有用到特定平台的功能,较易移植到其他平台。此外还写了一些示例代码。上述所有代码托管在github上:https://github.com/fdyjfd/idlcpp/,另外还建了一个网站:http://idlcpp.org/ 目前网站还没什么内容,以后慢慢去丰富。
下面进入正题,先从工具的使用开始。
Idlcpp命令行工具
Idlcpp可以从github上下载代码编译得到,另外我也在网上放了一份已经编译好的执行文件,可以从 https://files.cnblogs.com/files/fdyjfd/idlcpp.zip 下载直接使用。若要自己编译idlcpp,请先从github上获取源代码。源代码结构如下:
其中idlcpp目录下是idlcpp的源代码。paf目录下是基础运行时库与脚本插件的代码。tutorials下是一些示例教程。目录bin是用来放置编译生成的dll及exe文件的,示例的lua和python脚本也放在这里。
编译idlcpp要用到第三方工具flex和bison。这两个工具可以从 https://sourceforge.net/projects/gnuwin32/ 下载。安装这两个工具后将其所在目录设置到环境变量Path中,以便编译时可以找到它们。编译成功后会在bin目录下生成idlcpp.exe。也可以下载我上传的以编译好的zip压缩包,将其解压到bin目录下,然后将bin目录也设置到环境变量Path中;或者将其解压到windows目录下供后面使用。
idlcpp通过编译接口描述文件(默认扩展名为.i),生成4个C++代码文件,分别是:
1. 相应的头文件(默认扩展名为.h)。
2. 内置函数的实现文件(默认扩展名为.ic)。
3. 元数据包装类的头文件(默认扩展名为.mh)。
4. 元数据包装类的实现文件(默认扩展名为.mc)。
运行cmd,进入idlcpp所在目录,运行命令idlcpp –h,结果如下图所示:
各选项含义为:
-h 显示帮助信息。
-ld 在生成的头文件中加入 #line 指令,用于编译器发现错误时正确定位到 .i文件中的对应位置。
-pc 指定运行时库pafcore头文件所在的路径。
-mp 元数据包装类的名字是根据对应类型的名字以及所在的命名空间生成的,此选项用于指定生成的元数据包装类型名的后缀,默认值为”_Type”。
-sp 用于生成子类代理类名字的后缀,默认值为”_Proxy”。子类代理类用于模拟脚本继承C++类型进而重写虚函数的功能。
-ic 指定内置函数的实现文件名的后缀,默认为”.ic”。
-mh 指定元数据头文件的后缀,默认为”.mh”。
-mc 指定元数据实现文件的后缀,默认为”.mc”。
-em 在Visual C++中,需用__declspec(dllexport)和__declspec(dllimport)处理dll中导出函数的情形,一般情况下用一个宏定义上述两者,在生成的元数据代码中也需要导出一些函数到dll中,此时idlcpp把此处定义的宏插到相应的代码处。
-I 将指定路径加入#import命令的搜索路径中。
编译paf
用Visual Studio 2015打开解决方案文件paf\build\vs2015\paf.sln
paf 中有3个工程,分别为:
1. pafcore,这是基础的运行时库,提供了元数据的实现。相当于.Net中的System.Reflection或者java中的java.lang.reflect。
2. paflua,这是lua的插件,用于pafcore和lua之间的交互,使用lua语言可以通过这个插件访问用户导出的各种功能。
3. pafpython,这是python的插件,用于pafcore和python之间的交互,使用python语言可以通过这个插件访问用户导出的各种功能。
在pafcore工程中除了C++代码文件之外,还有许多idl的代码文件(扩展名为.i),如下图:
这些文件都是需要用idlcpp编译的。此处通过Custom Build Tool设置编译命令和选项,查看任意一个.i文件的属性,入下图所示:
在Visual Studio中可以通过属性文件简化Custom Build Tool的设置,pafcore所使用的属性文件在paf\build\vs2015\pafcore\pafcore.props。
设定好idlcpp.exe的路径以便系统可以找到它(比如设置环境变量Path或将其复制到Windows目录下)后即可编译pafcore工程。
工程paflua依赖lua,先从lua官网下载源文件编译后,再设置合适的头文件以及库文件路径,即可编译。
工程pafpython依赖python,先从python官网下载源文件编译后,再设置合适的头文件以及库文件路径,即可编译。
以上三个工程都会编译为Dll文件,默认放置在bin目录下。若是release版,文件名分别为pafcore.dll, paflua.dll和pafpython.dll。若是debug版,文件名分别为pafcore_d.dll, paflua_d.dll和pafpython_d.dll。
到现在为止,准备工作已经完成,下次开始介绍 tutorials 下的各个例子程序。
Lua语言的例子从这 C++混合编程之idlcpp教程Lua篇(2) 开始
Python语言的例子从这 C++混合编程之idlcpp教程Python篇(2) 开始