摘要
元编程是目前应用最广泛的技术之一,元编程就是生成其他程序的程序。元编程有很多实现方式,最常用的有如下几种
1 文本宏替换:C/C++语言的宏定义,在编译期直接文本替换。事实上,文本替换是一种很有效但也非常有局限性的特性:文本替换与其他表达式一起使用时会降低代码可读性,同时由于类型系统的限制,不同类型的参数需要定义不同的宏
2 类型模板:C++语言的模板编程,相比宏,模板能操作类型,更加智能,可根据上下文自动特化
3 反射机制:把编程语言自身作为头等对象如lisp/js/ruby等动态语言,通过应用程序接口来暴漏运行时内部信息或动态执行包含编程命令的字符串
4 代码生成:编写符合个性化需求的代码生成器,对于简单重复性的逻辑代码的生成,相比人工编写代码具有更多优势
本文试图设计一种新的元编程工具,该工具提供一套完整的抽象机制和基本语法规则,可用于任意语言和任意模式的代码生成
设计思路介绍
代码生成其实也就是用一段程序来生成另一段程序,该工具的本质也是一种用编写程序的方式来生成其他代码,它必须提供一套完整的抽象机制和最小的语法规则,用于代码生成。但作为生成代码的程序,抽象机制还是有别于传统的编程语言
1 代码生成的抽象在一定意义上就是对某种特定模式的程序替换掉可变部分的语言要素,该工具就是围绕着这一思路来设计
2 变量是提供抽象的最直接的方式,提供变量定义,此处的变量抽象不是具体逻辑变量,不能参与逻辑运算和逻辑流程,但它能描述某段代码的模式,类似于数学上的代数变量,能执行表达式替换,比如一个复杂的数学公式,里面重复出现的一些代数式可用某个变量替换,参与运算化简后再带入具体的代数式
3 提供模式定义和模式替换两个过程,模式定义主要提供能定义抽象的方法,模式替换则提供能将抽象的内容实例化同时能对抽象的内容执行各种组合的方法
4 在模式替换的时候能支持可变部分的抽象和实例化过程,其中可变部分又可以是另外的某个模式的定义,这样就类似于函数式编程里面的高阶抽象。经验表明,高阶抽象能提供非常强大的组合方法
5 提供模块化支持,引入类似import、include这种机制,可使得模式定义不局限于一个文件中
6 很多时候代码生成的模版强依赖于第三方的配置文件或工具的输出,比如生成的某些模式代码是excel中配置的某行或莫列的内容。于是该工具提供了对系统命令行执行的支持,系统命令执行后输出的文本内容以管道的方式提供给该工具作为内置变量来使用
工具使用说明
1 定义模式
<Var1 Param1 Param2> => < …. Param1 …. Param2 …. >
其中 => 左边是定义模式的名称(变量名)和参数的名称(模式中可变部分的标识符),在以上的定义中,Var1为模式名,Param1、Param2是可变参数
=> 右边是模式的内容,在内容里与参数同名的部分是模式中可替换的部分。如
<Switch Type X Y> =>
<
Type tmp = X;
X = Y;
Y = tmp
>
此处定义了一个名为Switch的模式,带有可变的三个参数Type、X、Y
2 模式替换(不输出到文件)
<<Var2 Param1 Param2>>
这段代码一般是放到模式的内容定义中,含义是将此处的程序替换为Var2定义的模式内容,其中模式内部的可变参数替换为Param1和Param2,如
<StructSwitch s1 s2 > =>
<
<<Switch int s1.a s2.a>>
<<Switch string s1.b s2.b>>
>
在StructSwitch的定义里面,会执行模式替换<<Switch int s1.a s2.a>> 为代码
int tmp = s1.a;
s1.a = s2.a;
s2.a = tmp;
执行模式替换<<Switch string s1.b s2.b>> 为代码
string tmp = s1.b;
s1.b = s2.b;
s2.b = tmp;
3 模式替换(输出到文件)
<<Var2 Param1 Param2::test.cpp>>
一般在非定义的地方,直接这样输出则将模式替换后的最终代码输出到名叫test.cpp的文件中
4 For关键字的使用
<<For Var2 Param1 Param2 Param3 Param4>>
此处For提供了一种高阶的抽象方式,等价于多次执行<<Var2 … >> 替换,具体执行次数依赖于Var2模式的参数数量与传入的实例化参数数量,比如此处Var2模式含有两个参数,而这里传入了Param1、Param2、Param3、Param4这4个变量,语句等价于执行
<<For Var2 Param1 Param2>>
<<For Var2 Param3 Param4>>
之所以提供For机制,主要是满足模式参数数量动态的需求,同时一些依赖于第三方配置文件或工具的模式,参数数量完全依赖于配置数量或工具的返回结果,具体可参看以下第5点说明
5 第三方配置或工具的使用
<Var3> =| <cat xxx.csv | grep yyy | awk zzz>
注意此处定义方式的不同(=|),Var2再也不是模式,而是某种变量,变量的内容则是后面命令”cat xxx.csv | grep yyy | awk zzz“的执行结果
Var3只能作为作为实例化的参数传入待替换的模式使用
<<Var2 Var3>>或<<For Var2 Var3>>
对于<<Var2 Var3>> 是将Var3按空格、制表符、换行符分割的字符串依次传入模式Var2执行替换
同理<<For Var2 Var3>> 也是将Var3按空格、制表符、换行符分割的字符串依次传入执行替换
注意:以上的命令行是linux系统的命令,如在windows下执行也支持windows的命令行。另外为了防止命令行代码执行失败,建议命令代码全部编写到一行
6 模块化方法
<<Import xxx>>
目前该工具识别以.me结尾为后缀的文件名的解析,以上代码表示执行xxx.me文件的解析
示例
该工具已发布到http://git.oschina.net/happygao/MetaProgramming。 由于该工具是基于python开发,需要用户在python环境下执行该项目的示例代码。在项目目录下src目录里含有该项目的示例代码(xxx.me),在项目根目录直接执行python main.py xxx,输出的代码在目录dst下查看