Linux mobile development

欢迎到Linux mobile development(www.limodev.cn)上交流。Limodev主要致力于基于linux的嵌入式系统的学习和研究,包括内核、驱动、GUI、MMI、软件设计方法和软件优化等方面,欢迎大家加入,无论是高手还是新手,一起学习共同进步。下载BLOG示例代码请先到limodev.cn/bbs上注册,谢谢

原创 开发代码产生器的经验收藏

 

开发代码产生器的经验

 

转载时请注明出处:http://blog.csdn.net/absurd/

 

提到代码产生器,很容易让人联想到Rrose之类的工具,它们根据UML图产生相应的代码,同时还可以从代码反向生成UML图。Rrose是一个庞大的系统,自然让人觉得代码产生器也是一个复杂的东西,所以不少人对代码产生器抱着敬而远之的态度。其实,代码产生器可大可小,小则几行代码,大则数万行代码。大小本身不是问题,重要的是它能提高我们的工作效率。

 

产生式编程已经不是什么新概念了,早在好几年前,国外一些专家都曾预言,产生式编程将在软件开发中占据重要地位。事实也是如些,产生式编程已经是软件开发中不可或缺的方法之一了。根据《产生式编程方法、工具与应用》一书中的描述,泛型编程、面向方面的编程、面向意图的编程和代码产生器都属于产生式编程。

 

本文重点在代码产生器上,当然我们的目标是研究代码产生器本身的开发,而不是某个特定代码产生器的使用方法。说来很惭愧,我从来没有去研究过某些经典的代码产生器,它们的实现方法是非常值得学习的。本文讲述的仅仅一些个人经验,但我相信这些的经验仍然有些实用价值的。

 

代码产生器的基本模型为:

 codegen

代码产生器要根据一些规则才能产生代码,即使这个规则蕴涵在代码产生器之中,从逻辑上讲,它仍然是存在的,我们把这些规则称为元代码。元数据就是用来描述数据的数据,比如DTD是用来描述XML文件的,XML文件本身也是数据,所以可以把DTD称为元数据。元代码有类似的含义,它是对将要产生的代码描述,从某种意义上讲,它本身也是一段代码。

 

代码产生器的目标是要产生代码,我们把产生出来的代码称为目标代码。所谓目标代码是针对代码产生器而言的,这里的目标代码通常会成为编译器的源代码。至于这些目标代码的具体语言,要视的情况而定,实际上可以为任何语言,甚至是自然语言。

 

代码产生器有点像编译器,把一种语言转换成另外一种语言。从更广泛的角度来看,很多应用程序本身就是一个代码产生器,比如用CGI/ASP/PHP等开发的WEB程序,接收用户输入的参数,输出HTML文件,把这样的应用程序当作代码产生器也是可以的。这里不讨论广义的代码产生器,而专注于程序员所用的代码产生器。这类代码产生器有一定特殊之处,下面我们看看开发代码产生器所要考虑的问题。

 

1.         是否有必要开发代码产生器。

这是我们要考虑的第一问题,有人说代码产生器是个好东西,为什么不要呢?问题在于任何好东西都是有代价的,开发代码产生器也是一样,至少要耗费我们的时间和精力。如果开发代码产生器本身的代价远大于直接编写代码本身,开发这样代码产生器意义就不大了。可以参考下面的公式:

 

(编写代码的工作量 - 编写规则的工作量 ) * 重用次数 大于 开发代码产生器的工作量

 

从功利上讲,只有上述公式成立时才考虑使用代码产生器。当然有例外,比如出于兴趣或者研究的目的去开发也是可以的。

 

比如在决定开发gclassfactory时所考虑的,如果只是编写一个gobject子类,开发代码产生器要花数几倍的时间,那我会选择手工去编写那些宏、安装属性、创建信号和重载函数,忍一忍也就过去了。但是我们整个平台是基于glib/gtk+的,以后有很多这样的子类要写,若利用代码产生器,累积起来的工作量非常可观了。同时我也希望练习一下用glib写程序,所以最终决定glib来开发gclassfactory了。

 

2.         是否能够使用代码产生器。

代码产生器的适用范围是有限的,它不是万能的,否则程序员这个职业就消失了。工具是愚蠢的,好的工匠应该了解工具的长处和短处,充分利用它们的特长。代码产生器只适用于那些规则性比较强的情况。因为规则是抽象的,以少量的规则可以产生大量的代码,也就是说代码产生器的输出一定要大于输入(根据工作量来衡量),否则花了很长时间开发工具,结果只是让事情复杂化了。

 

比如对于gclassfactory来说,输入可以用标准idl或者xml格式描述gobject子类,这些描述几乎没有什么冗余信息,输出gobject子类代码则有很多类似又有差异的代码,比如函数原型、函数外包装、实现函数的原型、实现函数的框架、大量的宏、安装属性和创建信号等等。输出的代码量是输入的代码量的三倍以上,而且编写输入规则时根本不用思考。

 

3.         选择开发语言。

个人认为每个程序员至少熟练掌握一门脚本语言和一门编译语言,我用过不少语言,比如汇编(ARM/Z80/x86)PBVBAutolispC/C++等,但真正熟练的只有bash+awk系列和C/C++。脚本语言的好处在于方便,不需要编译; 也更高级,用少量的代码可以实现很强大的功能。但脚本在规模变大时,极难维护,变得很复杂,也就失去了优势。

 

通常情况下,如果代码产生器的规则简单,用脚本来写比较有利。当规则比较复杂,这些规则本身就是一种微型语言或者用XML之类的语言来描述时,使用编译语言更方便一点。当然像pythonruby这样功能强大的语言,集脚本语言和编译语言两者之所长,因为我不懂,就不讨论它们了。

 

前几天遇到一个问题,要定义一组宏,它的格式是这样的:

 

KEYMAP(GDK_Op_Left, GDK_F12, DIKS_F12)

KEYMAP(GDK_Op_Right, GDK_F13, DIKS_F13)

 

大约有30多行,第一列的Op_Left之类是自定义的按键,是我们讨论的结果,放在一个表格中,手工把这份表格转换成以上的宏,不难也要不了多少时间,但这样单调的事很容易出错,特别对于我这样粗心大意的人来说。于是决定用awk来做:

 

awk 'BEGIN{i = 4} {print "KEYMAP(GDK_" $1 ", GDK_F" i ", DIKS_F" i ")"; i++}' keys.txt

 

这就是代码产生器!就一行代码。简单吧,它却产生了30多行代码。其实我经常在用这样的代码产生器,给我节省了不少时间,减少了出错的可能。所以能用脚本就用脚本,脚本实现困难时才考虑用C/C++等编译语言。

 

4.         分离规则读入与代码产生两个部分。

如果代码产生器比较复杂,应该规划一下代码产生器的架构。代码产生器架构是一个典型的管道--过滤器模型,所以应该把读取规则的部分和代码产生的部分独立开来,两者互不影响。如果与编译器对应起来,读取规则的部分就对应于编译器的前端,代码产生的部分对应于编译器的后端。

 

为什么要这样做呢?原因在于隔离变化和代码重用。

 

规则的格式是可以变化的,只要语义不变,它的变化不会影响到代码产生器,这为支持多种规则格式提供了方便。比如在开发gclassfactroy的时候,想到描述接口的通常采用IDL,为了简单我采用了XML,但我把规则读取部分抽象成一个loader,不同的loader动态的加载进来,要支持IDL只需要编写一个IDLloader即可,不需要对gclassfactory做一点改动。

 

同样的,把代码产生部分抽象成一个coder,也是动态加载的。现在支持产生C语言代码,若要支持产生C++代码,就编写一个产生C++代码的coder,也不需要对gclassfactory做任何改动。甚至用它来产生测试程序,来产生RPC的程序,理想情况下也不需要对gclassfactory做任何修改,只要编写新的coder即可。

 

5.         选择中间语言。

中间语言是个好东西,它本身就是一种抽象,即对规则的抽象。抽象后的规则不再依赖于特定的loader,比如就接口描述而言,不管你用IDL描述的还是用XML描述的,到了中间语言这里,这些差异已经泯灭了,都表示成同样的东西。

 

分离规则读入和代码产生两个部分,中间语言起了非常重要的作用,把两者联系起来了,但耦合很松散,两者仍然可以独立变化。中间语言几乎在任何与格式转换相关的程序中,都广泛的使用着,特别是在编译器和文档格式转换等系统中。

 

中间语言可以是一种实际的语言,有自己特定的格式,也可以只是一种内存中的数据结构。因此到中间语言的转换也有两种方式,对于前者,可以先把原始输入转换成中间语言,保存在文件中,再由中间语言的loader加载进来。对于后者,每种loader都是独立,直接转换成中间语言。

 

比如,在gclassfactory(参考[open source] gclassfactory)中,它的中间语言是一个内存中的数据结构,每个loader是独立的,直接转换成中间语言,然后交给代码产生组件处理。而在cilc (参考[open source] cilc) 中,它的中间语言是XML格式的,要先把其它格式转换成XML后,才能用cilc处理。

 

一个典型的代码产生器的结构如下:

codegen_impl

有的代码产生器还带有图形用户界面,但是对于良好的设计,图形用户界面是可以独立存在的,其核心部分应该与上图大同小异。比如拿Rrose来说,它一定有一个中间语言,先把UML的图形表示转换成中间语言,再根据中间语言来产生代码,而不会直接通过界面元素产生代码。

 

~~end~~

 

 

发表于 @ 2006年08月26日 17:14:00|评论(loading...)

新一篇: 仅仅是好看一些吗? | 旧一篇: 两则C语言技巧

用户操作
[即时聊天] [发私信] [加为好友]
李先静
订阅我的博客
XML聚合  FeedSky
李先静的公告

BLOG评论请到


下载BLOG示例代码请先到上注册,谢谢。

文章分类
收藏
1.友情链接
0华清远见-嵌入式培训专家
aimself@CSDN(RSS)
directfb中文网站(RSS)
Eric's Little Hut
eye_of_back的专栏(RSS)
Linux Mobile Research
Phoenix@上海(RSS)
segments的专栏(RSS)
study's Blog(RSS)
tracestudio
伐木丁丁鸟鸣嘤嘤(RSS)
会飞的鱼的专栏(RSS)
创系的技术博客
小四的BLOG(RSS)
小马哥的博客(RSS)
开源电信(RSS)
御风剑客
新奇的BLOG
易军军的网络家
李吉群的专栏(RSS)
2.亲情链接
凤凰的幸福蓄水池(RSS)
我的相册
3.软界高手
Donald E. Knuth (RSS)
孟岩(RSS)
透明(RSS)
4.LinuxMobile
celinuxforum(RSS)
GPE(RSS)
maemo.org(RSS)
opensource.motorola
palowireless
5.XWindow
Jserv's blog(RSS)
Keith Packard(RSS)
6.技术资源
7.开源项目
freedesktop(RSS)
GNU(RSS)
GTK+(RSS)
matchbox(RSS)
pxa27x-linux/
8.我的BLOG镜像
absurd@chinaunix
absurd@msn
My English BLOG(RSS)
存档
Csdn Blog version 3.1a
Copyright © 李先静