代码自动生成

2024-08-09

代码自动生成(精选9篇)

代码自动生成 篇1

0 引 言

高性能并行计算是现代科学研究、工程技术开发和大规模数据处理的关键技术,而并行编译系统是并行计算机系统软件中十分重要的一部分,提高并行编译技术对充分利用并行机资源和提高并行机效率起着十分重要的作用。

并行编译器包括前端处理和后端处理两部分:前端处理主要包括逻辑上的并行识别、计算和数据划分、依赖关系识别;后端处理主要是并行代码的自动生成,代码生成的关键在于如何高效地生成同步通信代码。

并行程序中的通信主要由四部分组成:数据初始分布通信、计算前的数据准备通信、计算过程中的同步通信以及数据收集通信。初始数据分布通信是在进行数据的初始分布过程中引起的通信。由于初始分布中数据划分和计算划分不能做到完全对齐所引起的通信称之为数据准备通信。同步通信是在进行并行计算过程中产生的通信。数据收集通信是所有进程计算结束后,主进程把所有进程的计算结果收集起来得到程序的最终执行结果时所引起的通信。本文重点讨论计算中的同步通信问题。

在文献[1]中对代码生成和通信优化做了介绍,但对具体如何实现没有讨论,本文则提出利用命名的线性不等式系统来表示数组数据空间、循环迭代空间、虚拟处理器空间和物理处理器空间,并建立了它们之间的内联关系,在此基础上给出了同步通信代码的自动生成算法。

1 计算划分

在分布式存储的大型计算机中,循环级的并行性一般是通过对循环嵌套迭代空间进行计算划分并将循环迭代分布到多个进程同时执行来实现的。下面给出与计算划分相关的定义。

定义1 迭代空间 迭代空间I表示一个循环边界是循环索引的线性函数且深度为m的循环嵌套,该空间是一个m维多面体。循环嵌套的每个迭代对应多面体中的一个整数点,即一次计算操作,用索引向量undefined表示。

定义2 处理器空间P 处理器空间P表示一个n维的处理器数组。

定义3 计算划分[4]C 计算划分undefined是满足特定关系的迭代和处理器对undefined的集合,处理器undefined执行迭代undefined当且仅当undefined。其中U是一个扩展的幺模矩阵,undefined是整数向量,B是整数矩阵,undefined是符号向量,undefined。

在计算划分C中,U指示计算划分是对迭代空间的哪些维进行划分以及是正分还是斜分;undefined给出分块的大小;undefined是偏移的大小。计算划分在代码生成的前一遍自动生成。

2 读写依赖关系与LWT树

计算中的同步通信由读写依赖关系和计算划分共同确定,对依赖关系有如下定义。

定义4[5] 嵌套循环L中的语句T的一个实例T(j)和语句S的一个实例S(i),如果存在一个存储单元M满足下述条件,则称语句T的实例T(j)依赖于语句S的实例S(i):

(1) S(i)和T(j)都引用(读或写)M;

(2) 在程序串行执行时,S(i)在T(j)之前执行;

(3)在程序串行执行时,从S(i)执行结束到T(j)开始执行前,没有其他实例对M进行写操作。

一对语句实例可以用4种不同的方式引用相同的存储单元,因此有4种类型的依赖关系:

① 如果S(i)写M而T(j)读M,则T(j)流依赖于S(i);

② 如果S(i)读M而T(j)写M,则T(j)反依赖于S(i);

③ 如果S(i)写M而T(j)也写M,则T(j)输出依赖于S(i);

④ 如果S(i)读M而T(j)也读M,则T(j)输入依赖于S(i)。

在这4种依赖关系中,④不会影响程序的并行化,不会引起通信,②和③的依赖关系是可以消除的,也不会引起通信,只有①需要通信,本文中的同步通信即指由流依赖所引起的通信。

在进行依赖关系分析时,用LWT树来表示数据之间的读写关系,每对读写对对应一棵LWT树。LWT树是一棵二叉树,表示精确的数据流信息,是描述从读操作实例到提供该读操作所读数据的最后一次写操作实例的映射关系。若读、写分别用循环索引undefined和undefined表示,则该函数的定义域是所有满足循环边界限制的undefined的集合。LWT树包括根节点、叶节点和内节点,内节点是对读undefined进一步的限制,叶节点又分为⊥节点和非⊥节点,表示了不同的依赖关系。

LWT[3]树把循环嵌套划分为以其每个叶节点的内容(contexts)ι为元素的集合,undefined。如果一个contextι∈I中迭代所读的值是在循环中产生的,则存在最后写关系undefined,且读迭代undefined所读值在写迭代undefined中产生},其中undefined和undefined是线性函数。

对给定的读迭代,从LWT知道一个写迭代undefined修改了undefined所读的数据且undefined是修改该数据的最后一个写操作,因此可以定义一个写迭代和读迭代之间的函数来表示undefined到undefined之间的关系,记为L,这样就可以通过LWT计算出两次引用之间的数据流依赖向量。

定义5 最后写关系LasarLasar是从读数组访问undefined和读迭代undefined到写数组访问undefined和写迭代undefined的映射,undefined当且仅当undefined;undefined;undefined;undefined且undefined使得undefined;undefined;undefined,其中undefined和undefined是读写访问函数。

由定义5知道,如果写迭代undefined和读迭代undefined都访问同一数组元素undefined之前执行,而且在undefined之间不存在其他迭代修改数组元素undefined则undefined之间存在最后写关系。

通过建立LWT树的方法可以将依赖关系精确到具体数值,这就为各进程之间的通信提供了关键依据。依赖关系分析是在代码生成的前一遍自动生成的。

3计算代码和计算中同步通信代码的自动生成

在并行程序代码自动生成中将涉及到多个多维整数空间,包括数组数据空间、循环迭代空间、虚拟处理器空间和物理处理器空间等。定义1和定义2分别给出了循环迭代空间和处理器空间的定义,下面给出数据空间的定义:

定义6 一个m维的数组A[n0][n1]…[nm-1],li≤ni≤ui,0≤i≤m-1,定义了一个m维的数据空间,该空间每一维的上下界即是数组每一维的上下界li和ui。

用命名的符号系数不等式系统统一表示这些空间,该不等式系统由多个不等式组成,每个不等式表示一种变量之间的关系,每个变量表示的即是空间的某一维。变量的所有可能的整数解的集合用n维离散的笛卡尔空间表示(n是变量数),所有满足该不等式系统的解都与笛卡尔空间中的一个整数点相对应。

计算代码和同步通信代码的自动生成过程分三部分:生成数据的接收和解包代码、生成计算代码、生成数据打包和发送代码。自动代码生成的关键是在程序的什么地方插入何种方式的通信代码?首先判断是否需要进行同步通信,如果LWT树中读的数据不在当前进程则需要通信,且利用计算划分来确定应该与哪个进程进行通信。

定理1 计算划分C满足最后写关系μ的通信集是undefined的集合,其中,undefined。

定理1所处理的是LWT树中的非⊥节点,根据LWT提供的读写依赖关系和读写变量所在

迭代的取值范围以及计算划分来判断两个迭代之间是否需要进行通信,见图1[1]。从图中可以看出,读/写迭代通过计算划分C分布到不同的进程pr和ps,两者之间通过LWT树联系在一起,若undefined则需要通信。

对于每一个物理进程mypid,通过以下算法判断是否需要发送数据给其他进程。

算法输入:计算划分C、依赖关系LWT、处理器空间P、迭代空间I

算法输出:并行同步通信代码和计算代码

算法描述:

(1) 建立虚拟拓扑结构把当前进程的标识mypid转换为多维坐标表示的pids;

(2) 通过计算划分和循环迭代范围得到参与计算的进程范围Pe;

(3) 如果pids∉Pe则不需要通信,否则;

(4) 根据计算划分C得到pids的迭代范围is;

(5) 根据LWT树中非⊥节点提供的读写依赖关系信息得到与is对应的ir;

(6) 依据ir和计算划分C得到ir所在的进程pidr;

(7) 比较pidr和pids是否是同一进程,如果二者相同则不需要同步通信,否则;

(8) 产生同步发送代码:打包数据并发送给pidr。

相应的,进程也需要判断是否需要接收其他进程发送来的数据:

(9) 通过建立的虚拟拓扑结构把当前进程的标识mypid转换为多维坐标表示的pidr;

(10) 通过计算划分和循环迭代范围得到参与计算的进程范围Pe;

(11) 如果pidr∉Pe则不需要通信,否则;

(12) 根据计算划分C得到pidr的迭代范围ir;

(13) 根据LWT树中非⊥节点提供的读写依赖关系信息计算出ir对应的is;

(14) 依据is和计算划分C得到is所在的进程pids;

(15) 比较pidr和pids是否是同一进程,如果二者相同则不需要同步通信,否则;

(16) 产生同步接收代码:接收pids发送来的数据并解包数据。

最后生成计算代码,根据上一遍提供的计算划分C和循环迭代I的边界信息计算出执行计算的进程范围,再依据分块执行的原理把计算分布到各进程执行。

4 实例分析

以下例来说明上面的算法:

例1 for(i=0;i<=N-1;i++)

for(j=i;j<=N-1;j++)

for(k=N-1;k>=i;k--)

a[j][k][i]=a[j][k][i]+a[i][k][j]*a[j][i][k]/a[i][j][k];

该例计算划分C:pid0=-k,pid1=i;迭代空间I:0≤i≤N-1;i≤j≤N-1;i≤k≤N-1,LWT树见图2。从图中可以知道数组引用a[j][k][i]和a[i][j][k]之间存在读写依赖关系,其依赖关系为:ks=-jr;js=ir;is=kr。

设N=4,则由计算划分C和迭代空间I可以计算出虚拟处理器空间P为-3≤pid0≤0,0≤pid1≤3。在生成同步通信代码时,首先判断是否需要进行通信,如果需要通信则产生同步通信代码。以pidr0=-2,pidr1=1为例来说明,根据计算划分C知道ir=1;kr=2;ir≤jr≤3,进而根据LWT树提供的读写依赖关系得到is=2;js=1;-3≤ks≤-1。得到写迭代之后,再根据计算划分C就可以找到该写迭代所在的进程为-3≤pids0≤-1,pids1=2。最后比较pids和pidr是否是同一进程,不是则产生两个进程之间的同步通信代码。此例pidr≠pids,则两个进程之间需要通信,进程pids产生同步发送代码,进程pidr产生同步接收代码。

5 总结与展望

本文主要讨论串行程序并行化中涉及到的计算代码和计算中同步通信代码的自动生成。文中所介绍的算法已在SUIF编译架构上实现,并利用ppopp benchmark程序集进行了验证,实验结果表明该算法能够正确生成计算代码和同步通信代码,但在该算法中未对同步通信的优化进行处理。下一步将主要研究计算中同步通信的优化问题,如多维并行条件下的计算和通信的重叠等。

摘要:简要介绍了并行编译中的计算划分和依赖关系分析,提出如何利用计算划分和依赖关系自动生成并行程序中的计算代码和同步通信代码。

关键词:计算划分,依赖关系,最后写树,同步通信

参考文献

[1]Amarasinghe S P,Lam M S.Communication optimization and CodeGeneration for distributed memory machines.In the Proceedings of TheACMSIGPLAN′93 Conference on Programming Language Design andImplementation,Albuquerque,New Mexico,June,1993:126-138.

[2]Ferner G S.The Paraguin compiler Message-passing code generation u-sing SUIF.In the Proceedings of the IEEE SoutheastCon 2002,Colum-bia,SC,April 5-7,2002:1-6.

[3]Maydan D E,Amarasinghe S P,LamMS.Array data-_flowanalysis andits use in array privatization.In the Proceedings of ACMSIGP-LAN-SI-GACTSymposium on Principles of Programming Languages.Charles-ton,South Carolina,January 10-13,1993:2-15.

[4]Anderson J M,Lam M S.Global Optimizations for Parallelism and Lo-cality on Scalable Parallel Machines.In Proceedings of the SIGPLAN′93 Conference on Program Language Design and Implementation,June1993.

[5]沈志宇,胡子昂,廖湘科,等.并行编译方法[M].北京:国防工业出版社,2000.

代码自动生成 篇2

二、Ws的调用方式有三种,http post、http get、soap post。本人建议使用http post,他访问远程ws接口的速度比使用soap post要快些。象使用axis框架实现soap post方式来说,简单的接口还好,对于使用了大量代理类及带有soaphead的ws,且还要在本地生成一大堆JAVA类来和服务端对应。特别如下这种情况,axis好像无法实现。Soaphead如下 0039434454

67677 象这种只能根据org.apache.axis.client.Call.addHead(ElementSoapHead)来增加报头。但这个报头没有根元素,无法创建一个这种Element。

下面就如何书写客户端代码详细讲解下。1.通过发送http post请求来调用ws。

SOAPUI是个不错的工具,他可以根据wsdl文件生成测试例子。另外重要的是,它可以生成wsdl对应的请求报文和响应报文。这样我们在调用别人写的ws时,不管多复杂的ws都可以简单的调用。首先按照SOAPUI显示的请求报文格式拼装我们的请求报文,使用java.net.HttpURLConnection对象来发送http post请求。然后使用输出流、输出流获得响应报文,再用Element解析报文得到要取的数据。示例代码如下: JAVA类SoapInvoke: package test;

import java.io.InputStream;import java.io.OutputStream;import java.net.HttpURLConnection;import java.net.URL;

public class SoapInvoke {

public static void main(String[] args){

} try {

} soapSpecialConnection();e.printStackTrace();} catch(Exception e){ public static void soapSpecialConnection()throws Exception {

//拼装soap请求报文

StringBuilder sb = new StringBuilder();StringBuilder soapHeader = new StringBuilder();soapHeader.append(“”);soapHeader.append(“”);soapHeader.append(“zhouyun”);soapHeader.append(“123”);soapHeader.append(“”);soapHeader.append(“”);soapHeader.append(“”);soapHeader.append(“”);soapHeader.append(“zhouyun”);soapHeader.append(“”);soapHeader.append(“”);soapHeader.append(“”);soapHeader.append(“”);

//设置soap请求报文的相关属性

String url=“http://localhost:8080/CXFServer/SayHelloService”;URL u = new URL(url);HttpURLConnection conn =(HttpURLConnection)u.openConnection();conn.setDoInput(true);conn.setDoOutput(true);conn.setUseCaches(false);conn.setDefaultUseCaches(false);conn.setRequestProperty(“Host”, “localhost:8080”);conn.setRequestProperty(“Content-Type”, “text/xml;charset=utf-8”);conn.setRequestProperty(“Content-Length”, String.valueOf(soapHeader.length()));conn.setRequestProperty(“SOAPAction”, “");conn.setRequestMethod(”POST“);//定义输出流

OutputStream output = conn.getOutputStream();if(null!= soapHeader){ byte[] b = soapHeader.toString().getBytes(”utf-8“);//发送soap请求报文

output.write(b, 0, b.length);} output.flush();output.close();//定义输入流,获取soap响应报文

InputStream input = conn.getInputStream();int c =-1;//sb为返回的soap响应报文字符串

while(-1!=(c = input.read())){ sb.append((char)c);} input.close();}

} 2.通过axis来调用ws。

对于使用了复杂代理类的ws,我们在调用时可以使用AXIS、CXF、xfire架包来自动生成ws客户端JAVA代码。下面以axis为例,来展示。服务器端主要JAVA代码 package test;

import javax.jws.WebService;

@WebService public class SayHelloImpl implements SayHelloService {

public wsResult sayHelloMr(String name){

wsResult retObj = new wsResult();

retObj.setResultVal(”Hello,mr “ + name);

return retObj;

}

public wsResult sayHelloMiss(InputClass input){

wsResult retObj = new wsResult();

retObj.setResultVal(”Hello,Miss “ + input.getName());

return retObj;

} } 客户端:

1)新建wsdltojava.bat文件,放到C盘,文件内容如下: set Axis_Lib=E:axis-bin-1_4axis-1_4lib set Java_Cmd=java-Djava.ext.dirs=%Axis_Lib% set Axis_Servlet=http://localhost:8080/CXFServer/SayHelloService?wsdl %Java_Cmd% org.apache.axis.wsdl.WSDL2Java-u %Axis_Servlet% 其中Axis_Lib为本地axis架包的路径;Axis_Servlet为本地ws的URL,这里也可以设置为此ws服务器对应的wsdl文件的路径。

2)在DOS里,执行wsdltojava.bat。在C盘根目录下就会生成JAVA客户端的代码了 3)新建一个java类Invoke,代码如下:

package test;

public class invoke {

} }

SayHelloService stub = svc.getSayHelloImplPort();//调用

//WsResult wsResult = stub.sayHelloMr(”zhouyun“);InputClass inputClass=new InputClass();inputClass.setName(”zhouyun“);WsResult wsResult = stub.sayHelloMiss(inputClass);System.out.println(”结果是:" + wsResult.getResultVal());public static void getResult(){ try { SayHelloImplServiceLocator svc = new } public static void main(String[] args)throws Exception { getResult();SayHelloImplServiceLocator();} catch(Exception e){ } System.out.println(e);Ps: 1.通过命令生成的java客户端代码里SayHelloImplServiceLocator类是调用ws的入口。

2.执行此类,Myelipse控制台会输出Hello,Miss zhouyun,代表调用成功。

基于XML的代码自动生成工具 篇3

关键词:XML,代码生成器,程序自动化

1 技术背景和优势

1. 1 代码生成器的技术发展

自动代码生成技术[1]作为一种出现在上世纪的软件开发技术,首先出现在编译器的开发和设计之中,并在此领域获得了广泛应用。在编译器模型中,编译器前段将输入的源程序翻译成一种中间表示,后端以源程序的中间表示为输入,并产生等价的目标程序作为输出。在此编译器的后端就是编译器的代码生成部分。

在这里,自动代码生成并不是指作为编译器后端的代码生成,而是指通过生成器,读取相关的代码或文档中的定义,生成如C、C ++ 、Java、Perl、Ruby、Python及HTML等高级语言代码。

自动代码生成技术从上世纪起步发展以来,取得了长足进步。但随着计算机科学与技术的不断发展, 自动代码生成技术也越来越成熟并被广泛应用[2]。

1. 2 采用代码生成器的好处

对于软件工程师而言,代码生成技术有如下优点[3]:

( 1) 保证代码的质量。一个项目周期中大量的手写代码通常会由于软件工程师在编码时不断采用新的或更好的方法而良莠不齐。代码生成技术从编码的初始阶段创建通用模板,而通过修改模板和再次运行代码生成器来对所有已生成的基本代码修正缺陷或优化。

( 2) 保证代码的一致性。由代码生成器生成的代码在API和变量名上的写法完全一致,这就为使用者提供了易懂易用的接口,更利于分层思想的实现。

( 3) 产生代码的高效性。一旦模板等设计好后, 只需简单的运行代码生成器便可高效地生成用户需求的代码。

( 4) 利于维护。接口和变量的一致性有利于后续的维护工作。

2 代码生成工具的框架

一般而言代码生成器有着必不可少的3要素: ( 1) 模板。即生成代码的格式和结构模板。( 2) 元数据。即在代码中需建模的结构相关资源。( 3) 业务规则。用于指定元数据和行为的规则,这一部分通常封装在代码生成器中[4]。

常见的典型代码生成方式有3种: ( 1) CORBA中间件所采用的IDL( 接口定义语言) 的代码生成方式。 ( 2) . NET提供的Code DOM机制。( 3) 基于XML的生成方式。表1列出了3种方式采用的模板、元数据、业务规则[5]。

由表1可看出,这3种代码生成方式各有不同,但就实现的简易程度而言,基于XML的代码生成技术明显更具优势。其以XML技术和XLST文档转换技术作为支持,而XSLT语言则直接定义文档转换规则,与生成代码采用的语言无关,故可更方便快捷地实现代码的自动生成; 而其他两种代码生成方式由于其业务规则与要生成的代码语言相关,而生成某种特定语言, 其映射关系的建立一般不由程序员确立[6]。

另外,从发展前景来看,XML具有良好的可读性, 方便的可扩展性,数据内容与其形式的分离,可轻松地跨平台应用,适合面向对象的程序开发等多方面优势。人们可通过DOM或SAX等技术对XML数据进行访问; 更可通过XPath和XSLT对其进行文档转换,将其转换为其他格式的文档。

基于以上的自动代码生成方式的对比及XML的应用优势,文中选择基于XML的代码生成方式来实现代码生成。

基于XML的代码生成方式是一种常见的典型代码生成方式。其模板语言一般为XSLT; 其元数据一般用XML文件记录; 其业务规则一般通过XSLT转换语言定义,通过XSLT引擎自动产生代码[7]。基于XML的代码生成工具的整体框架,如图1所示。

图1是具体的代码生成工具的框架。其中,数据文件即三要素中的元数据; 模板文件即模板; 而业务规则被封装在代码生成工具中。基于XMl的代码生成工具中的数据文件和模板文件均为XML表,代码生成工具则依赖于XML解析器的实现[8]。

3 代码生成工具的实现

由于在雷达系统建模与仿真中存在众多的代码重复编写的问题,为避免人为编写带来不必要的麻烦和错误,文中使用基于XML的代码生成工具来自动帮助生成底层仿真模型代码。为对XML数据进行访问,设计了一个适合于C /C ++ 的基于DOM解析规则的XML解析器。在现有开源软件CMark Up的基础上,为适应遍历普通树的各个节点的需求,文中对CMark Up进行了二次封装,重新设计了数据结构,其结构如下:

Typedef struct xml Elem_tag

{

/ / 节点操作

struct xml Elem_tag * parent; / / 父节点

struct xml Elem_tag * current; / / 当前节点

struct xml Elem _tag * child Head; / / 子节点的头

结点

struct xml Elem_tag * forward; / / 前向指针

struct xml Elem_tag * next; / / 后向指针数组

/ / 数据操作

char * attrib Head; / / 属性指针头结点

int n Attrib; / / 属性数目

/ / 节点内容

char elem Name[MAXLENGTH + 1 ]; / / 节点名称,MAXLENGTH为一个宏

int n Depth Elem; / / 当前节点的在树形结构中的深度

} xml Elem,* pxml Elem;

重新设计后的XML解析器可更容易的进行递归遍历,方便解析XML数据。

对于代码自动生成工具中重要的模板设计,为适应雷达系统建模与仿真的需求,将模板信息分为5大类: ( 1) 系统信息。代码生成后的存放路径、代码生成后的文件名、生成C ++ 代码的类名、成员函数名等。 ( 2) 参数信息。时宽、采样频率、发射功率、带宽、脉冲重复周期等。( 3) 模型的输入信息。发射信号脉冲重复周期类型、发射信号载频类型、发射信号调制类型、发射信号中心载频等。( 4) 模型的输出信息。输出数据结构体、输出信号类型。( 5) 模型之间的连接信息。两个模型之间的连接关系,两个模型之间的数据传输。

同时为生成C ++ 代码,必须为头文件( . h) 和源文件( . cpp) ,分别设置模板。为方便管理,将其放在同一XML表中以”template_h”和”template_cpp”节点区别,如图2所示。

图 2 XML 模板的设计与实现

图 3 数据文件交互式界面

为更人性化、交互性更好,给该数据文件配备了可视化的界面,如图3所示。在用户填入需要一些数据后就可点击“选项”页面的“生成C ++ 代码”按钮即可生成所需要代码。假设文件名填入“sub Transmitter”其他为默认值,生成代码:

/ / 参数定义模块,主要完成对参数的定义

typedef struct para_sub Transmitter_tag

{

double tau; / / 脉冲宽度

double fs; / / 采样频率

double band; / / 带宽

double pt; / / 发射机瞬时功率

} para_sub Transmitter; / /参数定义部分

/ / 接口定义模块,主要完成对接口的定义

typedef struct io_sub Transmitter_tag

{

/ / 输入接口定义

int in_prt Type; / / 发射信号脉冲重复周期类型

int in_fc Type; / / 发射信号载频类型

int in_mod Type; / / 发射信号调制类型

int in_mod Phase Type; / / 发射相位信号调制类型

/ / 输出接口定义

struct signal out_struct; / / 发射信号结构体

struct pmat Matrix pout_msignal; / / 发射信号

} io_sub Transmitter; / /接口定义部分

typedef struct sub Transmitter_tag

{

para_sub Transmitter para Data; / / 参数结构体

io_sub Transmitter io Data; / / 输入输出结构体

struct sub Transmitter _ tag * pm _ data; / / 指向自己的指针

} sub Transmitter,* psub Transmitter; / /用户模型 定义

4 结束语

代码自动生成 篇4

比如连连看的数据生成。

比较简单,这里不包括判断是否一定有解的部分,只是一个随意生成地图(给定行,列,每种图片的生成个数),没有思路的可以参考一下,高手也可以指正。

//生成地图的类

package src.ww.llk.map

{

import mx.collections.ArrayCollection;

public class MakeMap

{

private var mapParam:MapParam = null;

private var types:ArrayCollection = null;

//保证取得的数据不被重取

private var flags:ArrayCollection = null;

public function MakeMap(param:MapParam):void {

mapParam = param;

initData;

}

// 初始化必要的数据

private function initData():void {

types = new ArrayCollection();

var typeNum:int = Math.floor(mapParam.cols*mapParam.rows/mapParam.numberPerType);

for(var i:int=1;i<=typeNum;i++) {

for (var j:int=0;j

types.addItem(i);

}

}

var yushu:int = mapParam.cols*mapParam.rows - typeNum*mapParam.numberPerType;

for (var yI:int = 0; yI

types.addItem(1);

}

}

//随机生成地图

public function make():Array {

flags = new ArrayCollection();

for(var i:int=0;i

flags.addItem(i);

}

var mapData:Array = new Array();

var rowData:Array = null;

var col:int = 0;

rowData = new Array();

rowData.push(0);

for(col = 0;col

rowData.push(0);

}

rowData.push(0);

mapData.push(rowData);

for(var row:int = 0;row

rowData = new Array();

rowData.push(0);

for(col = 0;col

rowData.push(getType());

}

rowData.push(0);

mapData.push(rowData);

}

rowData = new Array();

rowData.push(0);

for(col = 0;col

rowData.push(0);

}

rowData.push(0);

mapData.push(rowData);

return mapData;

}

// 随机取得单个数据

private function getType():int {

var ran:int=-1;

var ret:int = 0;

ran = randomIndex();

var index:int = int(flags.getItemAt(ran));

flags.removeItemAt(ran);

ret = int(types.getItemAt(index));

return ret;

}

private function randomIndex():int {

return Math.random() * (flags.length - 1);

}

}

}

参数类,就是一个bean,

package src.ww.llk.map

{

public class MapParam

{

public var rows:Number=0;

public var cols:Number=0;

public var numberPerType:int=4;

}

}

测试类:

public function test():void {

var param:MapParam = new MapParam();

param.rows = 6;

param.cols = 6;

param.numberPerType = 4;

var maker:MakeMap = new MakeMap(param);

var map:Array = maker.make();

for each(var rowData:Array in map) {

var rowStr:String = “”;

for each(var cell:int in rowData) {

rowStr += cell + “ ”;

}

trace(rowStr);

}

}

运行结果:

0 0 0 0 0 0 0 0

0 3 3 1 1 7 6 0

0 6 8 9 7 9 5 0

0 2 4 4 4 1 7 0

0 5 8 4 9 5 8 0

0 2 6 2 3 1 7 0

0 6 2 5 3 8 9 0

代码自动生成 篇5

统一建模语言UML (Unified Modeling Language) 的出现和广泛应用大大提升了模型在软件开发中的作用, 以模型为核心的软件开发思想已逐渐为人们所接受, 近年来出现的模型驱动开发方法MDD (Model Driven Development) [1]就是这种开发思想的典型代表。尽管MDD与基于UML的面向对象的开发方法都以模型为核心, 但二者存在着重要的区别。基于UML的面向对象方法强调的是"大而全"的开发思想, 这主要是因为UML是作为一种通用建模语言而设计的[2], 它的功能不仅多而且复杂, 很难为非软件开发人员 (如领域专家) 学习和掌握。整个建模过程领域专家的参与度低, 往往是由软件设计人员独自承担, 且软件设计人员对领域知识不熟悉, 需要花时间学习, 造成不必要的资源浪费。MDD更注重应用领域, 它强调的是"小而专"的开发思想。也就是说, 它不要求像UML这样的通用建模语言提供很多很全的功能, 只要求提供一种面向特定领域的建模语言DSL (Domain Specific Language) [3]能快速有效地解决实际问题即可。由于DSL是面向特定领域的, 其提供的功能仅限于本领域且为领域专家所熟知, 所以简单易学, 整个建模工作可由领域专家独自完成。MDD的另一个开发思想是通过模型自动生成程序代码而无需手工编写, 这样开发人员的主要任务就变成程序代码生成器的设计了。

1、MetaEdit+

通常我们把设计DSL的工具软件称为元建模工具 (meta-modeling tools) , 为设计DSL所建的模型称为元模型 (meta-model) , 而建立元模型和设计DSL的过程称为元建模 (meta-modeling) 。由于领域专家对其所在领域更熟悉, 所以建立元模型、设计DSL以及建立模型的任务应该由领域专家来完成。因此在选择元建模工具时必须保证它简单易学, 这样只要对领域专家稍加培训他们即可掌握, 另外选择的元建模工具必须具有从模型直接生成程序代码的功能。

目前元建模工具有很多, 比较著名的有MetaEdit+、GME、DOME、EMF、GMF等[4], 从易用性和代码生成等因素考虑, 本文选择了MetaEdit+作为元建模工具。

MetaEdit+是一款出现较早、使用最广的商用元建模工具, 是一个基于图形的建模工具。实际上MetaEdit+是一个建模工具集, 它提供了一组建模工具, 如diagram editors, matrix editor, table editor browsers, report and code generation, method tools, API&XML connectivity, object repository等[5]。使用MetaEdit+可以大大缩短软件开发周期, 软件开发效率提高5-10倍[6], 因为它提供了强大代码生成功能, 整个开发过程可不必编写代码[7]。在下面的部分中, 本文以一个从系统功能结构图直接生成应用软件主窗口和菜单系统的例子为例详细介绍如何通过MetaEdit+实现模型驱动下的Java代码自动生成。

2、基于MetaEdit+的元建模

在设计代码生成器之前, 首先要建立领域模型。在UML中通常用类图对静态结构建模, 用顺序图, 状态图对动态行为建模[8], 而MetaEdit+是一种元建模工具, 它并没有事先将图分为类图、顺序图或状态图等, 图的含义是由建模者自己定义。通过MetaEdit+提供的工具建模者可以设计具有自己风格的图形符号和图。

2.1 建立领域元模型

本文中建立领域元模型就是设计绘制系统功能结构图和代码生成所需的各种图形符号和元素。具体地讲, 就是创建MetaEdit+的Property (属性) 、Object (对象) 、Relationship (联系) 、Role (角色) 和Graph (图) 。

2.1.1 创建属性

通过MetaEdit+的Property Tool工具创建每个对象的属性, 创建的属性及说明如下:

Name[Software]:对象的名称

Index[Software]:对象在菜单中的顺序

CallFunction[Software]:叶菜单项调用的功能模块

2.1.2 创建对象

元模型的对象实际上就是模型中的类, 通过Object Tool工具创建。创建的对象有:

MainWindow[Software]:主窗口

Object[Software]:一般对象, 如菜单、菜单项

MainWindow[Software]对象设计界面如图1所示, 其中左图的窗口可以定义对象包含的属性, 右图的窗口用于设计表示对象的图形符号。

2.1.3 创建联系

使用Relationship Tool工具创建对象之间的联系如下:

FromTo[Software]:MainWindow[Software]与Object[Software]之间以及Object[Software]与Object[Software]之间的联系。

2.1.4 创建角色

用Role Tool工具创建角色如下:

From[Software]:连接父对象

To[Software]:连接子对象

2.1.5 创建图

通过Graph Tool工具创建的图为:

FunctionStructure:可用于设计功能结构图的元模型图。图的绑定情况如图2所示:

2.2 建立领域模型

建立领域模型就是用上面所建元模型绘制系统功能结构图, 可通过Diagram Editor工具来设计, 图3给出了设计好的Shopping System的功能结构图。

3、代码生成器设计

模型建好后, 接下来就是设计代码生成器将模型直接转换为Java程序而无需手工编写一行Java语句。也许有的读者会说:"设计代码生成器的工作量可能和设计菜单程序的工作量差不多", 初次设计的确如此, 但它们有质的不同, 因为代码生成器设计好后可用于生成任何系统的菜单程序, 只需绘制出功能结构图即可。这说明生成器的重用性好, 另外, 如果前面设计的功能结构图已做了多处修改, 这时, 只需重新运行代码生成器生成一下代码即可, 而无需修改生成器, 这说明系统易维护性好, 这对那些变化频繁的应用领域特别有意义。

MetaEdit+提供了功能强大的代码生成功能, 包括生成器定义语言MERL和生成器设计器Generator Editor。限于篇幅, 下面只给出主生成器_MainWindow[Software]的设计代码及说明:

为了能生成任意多级菜单 (级数只取决于功能结构图的层次数) , 代码生成器_Menu[Software]采用了递归调用设计方式。

4、结语

我们用MetaEdit+设计的代码生成器可以直接从图形模型自动生成Java代码, 所生成的代码可以在Netbeans6.0和eclipse6.0环境下正常运行。限于篇幅, 本文所述的模型驱动下的代码自动生成方法只介绍了系统主窗口和多级菜单的生成, 虽然如此, 但它不失一般性。也就是说, 用类似的方法我们还可以设计各种功能模块, 如输入对话框的设计[9], 关于功能模块的设计将另文论述。总之, 模型驱动下的代码自动生成方法可以大大提高软件开发速度, 使得软件维护变得更简单更容易, 必将成为未来软件开发的主要方法。

参考文献

[1]Richard S, David S F, John P.The MDA Journal:Model Driv-en Architecture Straight from the Masters[M].New York:MeghanKiffer Press, 2004:135-146.

[2]谢正良, 赵建华, 李宣东等.一种基于J2EE平台的MDA模型转换技术[J].计算机应用研究, 2005 (3) :51-54.

[3]Martin F.Domain Specific Languages[M].Toronto:Addison-Wesley Professional, 2010.

[4]刘辉, 麻志毅, 邯维忠.元建模技术研究进展[J].软件学报, 2008, 19 (6) :1317-1327.

[5]周金根.MetaModelEngine:元模型引擎开发思路[EB/OL].[2010-10-12].http://www.cnblogs.com/zhoujg/archive/2010/07/28/1786155.html

[6]MetaCase.MetaEdit+Domain-Specific Modeling environment[EB/OL].[2010-09-10].http://www.metacase.com/MetaEdit.html.

[7]Juha-Pekka T.MetaEdit+:MetaEdit+:integrated modeling andmetamodeling environment for domain-specific languages[C].//Companion to the 21st ACM SIGPLAN symposium on Object-oriented programming systems, languages, and applications.NewYork:ACM, 2003:690-691.

[8]李思广, 林子禹, 胡峰, 等.基于UML的软件过程建模方法研究[J].计算机工程与应用, 2003, 39 (6) :76-78.

代码自动生成 篇6

UML已经被广泛应用于当前的软件开发中。在模型驱动开发中, 自动生成代码可以快速得到原型, 有助于早期需求和设计的验证, 对最终实现有着重要帮助;更远的目标是模型自动生成代码将可以取代手工编码, 从而提高软件开发的抽象层次。可是, 模型与代码之间的沟堑至今也未能填平。一方面, UML代码自动生成技术的研究方兴未艾;另一方面, UML本身也处在进化当中。当前UML2即将正式公布, 它融入了MDA的结构化开发思想。

二、结构的实现

可执行UML中以类图来建模系统的结构, 描述了对象的类型和对象间的各种静态关系。在模型-代码的映射关系中, 结构对应代码的框架部分。类图的代码生成技术比较成熟, 它的各种成分 (属性、方法、继承、关联、聚合等等) 都有明确直观的映射关系, 这里不讨论细节, 而是给出结构实现的总体方案, 其中的策略和技术同样适用于约束和行为。

一个系统, 不论是模型形态还是代码形态, 不同的只是承载信息的格式即具体语法, 相同的是其中蕴含的信息即语义。从这个意义上说, 代码生成的过程就是信息改变承载格式的过程。信息的承载格式分为四层, 分别是UML模型层、XMI模型层、树模型层、代码层。分层可以屏蔽复杂性, 提高各层的可重用性。

三、约束的实现

约束是系统的限制条件, 用来保持结构的特性和控制动作的活动时机。可执行UML用OCL表达式精确的刻画约束。OCL可以弥补图形表达的不足, 使得UML模型更加精确;作为一种基于集合操作的声明式语言, 使得UML模型更加形式化。

可执行UML中的OCL约束分为三类:约束属性, 即以不变式限制类的属性;约束方法, 即用前置、后置条件限制方法;约束行为, 即用布尔表达式刻画动作的触发条件。建模中, 前两类OCL约束在类图中定义, 第三类则在行为模型如序列图、状态图中定义。第三类约束在讨论序列图的时候研究, 先讨论前两类约束的代码生成。

以一个简单的例子来说明几种典型的OCL表达式的代码映射思路。有两个类Person和Company, 其中的约束非形式化的表述如下:

C1.人的年龄必须大于等于1C2.已婚的人年龄必须大于等于18

C3.所有公司雇员的年龄必须大于18 C4.存在一个叫Jack的雇员

C5.只有在员工数小于100时才雇佣新人C6.雇佣成功后员工数增1

分析约束C1到C6的OCL表达式不难发现:布尔表达式是共同的成分, 类型和句式则不尽相同。在映射关系中, 布尔表达式直接对应一个代码片段, 具有式4_1的形式;类型和句式则决定这个代码片段的插入位置。

if (Boolean expression==false) throw except ion式4_1

C1―C6本质上都是对属性的约束, 如果具有改变某属性值能力的相关方法都遵循该属性的约束, 那么我们就说约束得到了实现。于是这些相关方法也就是约束代码片段的插入点。前后置条件是一种局部约束, 作用域仅在其所属方法内, 插入位置分别在方法的开头和结尾。所以C5和C6分别对应成员函数hire实现中开头和结尾的两条if语句。不变式是一种全局约束, 作用于对象的整个生存期, 插入点是全部相关方法。C1和C2约束的相关方法是Person类的构造函数, 故此在构造函数中应当添加两条if语句, 以保证创建的Person对象是合法的。C3和C4通过关联关系约束了雇员的某些属性, 而建立链结的是hire方法, 所以在hire的实现中加入两条if语句。所不同的是每一次调用hire方法时, C3的实现是敏感的, 即一旦行参的属性违法就立刻抛出异常;而C4的实现是迟钝的, 只要hire方法还能执行 (total Num<100) 就认为合法, 但用一个标志位来记录约束是否得到了满足, 只在最后一次hire方法执行时才检查标志位, 不满足则抛出异常。出于自动生成代码的需要, 我们应先建立属性和方法之间的依赖关系, 因为依赖于被约束属性的那些方法就是约束的相关方法。这里我们说一个方法依赖于某个属性, 如果该属性以左值的形式出现在这个方法实现的赋值表达式中。由于属性的改变一般都在算法中进行, 所以一种选择是在建模算法时明确的指出这种依赖关系。另一个选择是静态分析方法实现体的代码, 由算法建立这种依赖关系。

摘要:UML模型的代码自动生成是模型驱动开发中的一个关键问题, 对早期需求、设计的验证和最终实现有着重要帮助。本文系统研究了模型的代码自动生成技术, 并给出了带OCL约束的类图的实现。

关键词:模型,代码生成,约束

参考文献

代码自动生成 篇7

电力线载波 (Power Line Carrier, PLC) 通信是一种利用电力线载波进行信息传输的通信方式。随着高速电力线载波通信技术的不断进步, 电力线载波通信不仅被用于远程抄表、家居自动化, 国内外还相继开展了利用低压电力线传输1 Mbit/s以上的高速电力线载波技术研究, 从而实现传输数据、语音、视频和电力为一线的“四网合一”。然而, 由于在线路上电压高、电流大、噪声大、负载种类多等原因, 造成信号在电力线上传输时必须要求设备具有较高的抗干扰性和稳定性[1]。

目前, 高速电力线载波通信采用的主要调制技术有单载波、扩展频谱和正交频分复用 (Orthogonal Frequency Division Multiplexing, OFDM) 调制技术3种[2]。本文研究OFDM调制技术在电力线载波通信中的应用。

1 基于OFDM的电力线载波通信

由于目前国内常用的载波技术都是基于固定频点、窄带、简单的调制技术, 因而传统电力载波技术在实际应用中具有一系列的局限性, 主要体现在[3]:对信道的时变性、频率选择性衰落不具备自适应能力;对窄带干扰及多径衰落不具备抵抗能力;数据速率低, 一般为几百bit/s;通信性能受环境影响较大。

针对智能电网对电力线载波通信提出的数据速率、通信可靠性、通信实时性等要求, 电力线载波通信的性能提高成为当前研究和应用的重点。OFDM是一种先进的多载波调制技术, 近年来被广泛应用于信道环境恶劣的通信系统中并取得较好的通信效率。OFDM是一种将若干个彼此独立的信号合并为一个可在同一信道上传输的复合信号的方法。OFDM各传输子载波相互正交, 从而具有很强的抗信道衰落能力和较高的频谱利用率, 并能很好地抑制码间干扰[4]。

将OFDM技术应用到电力线载波通信将会大大提高电力线载波通信的性能, 不仅可以有效提高数据的通信速率, 利用OFDM技术的抗干扰能力以及对信道自适应能力也可以加强电力线载波通信的可靠性。同时由于通信速率以及可靠性的提高, 通信时延也必将得到缩短, 通信的实时性也可以得到保证。

2 OFDM技术原理

OFDM的基本原理是把高速的串行数据流分解成若干个并行子数据流, 每个子数据流再加载到相应的子载波上进行调制, 最后把各个子载波上的信号叠加合成输出到信道中, 在接收端再进行相应地分离解调。OFDM系统原理如图1所示。

数据流经过数字调制后进行串/并转换, 并行数据映射到各个子载波上, 经过快速傅里叶逆变换 (IFFT) 运算, 并/串转换后加入保护间隔, 经数/模转换以模拟信号在信道中传输。接收端进行相应的逆运算, 包括去除保护间隔、FFT、数字解调等得到所传输的数据。

3 OFDM的SSiimmuulliinnkk仿真及DSP代代码码自自动动生生成成

根据OFDM的基本原理, 设计的OFDM系统的Simulink仿真模型如图2所示。

信源采用伯努利信号发生器。系统流程如下:十六进制数据产生→16QAM调制→IFFT变换→加循环前缀→AWGN信道→移除循环前缀→FFT变换→16QAM解调→误比特率计算。OFDM信号调制子模块设置根据构造对称频谱进行IFFT运算的方法建立OFDM信号调制子系统。对数据流进行共轭对称性构造, 所以IFFT变换后所得的数据为实数序列[5]。

为了缩短DSP代码编程的开发周期, 利用M AT L A B L I N K f o r C C S D e v e l o p m e n t To o l s和Embedded Target for the TI Platform工具包可以直接由MATLAB下的Simulink模型直接自动生成DSP的可执行代码[6], 研发者可以更直观地在MATLAB的环境下完成DSP的开发。

OFDM系统发送端的DSP代码生成仿真模型如图3所示。

仿真系统设置模块假设子载波数为50, IFFT点数为128, 子载波频率为f3~f52, 构造共轭对称谱频率为f76~f125。模型模块设置及参数设置如下。

1) 帧转换模块:将数据转换成帧形式传输。

2) Unbuffered模块:对数据进行并/串转换, 将并行低速传输数据流转换成串行高速数据, 以便实现在DSP发送板的GPIO口输出。

3) Float to Interger模块:完成浮点数据到整型数据的转换。

4) Digital Output模块:即TMS320F2812DSP发送板的GPIO口, 设置为8位输出口, 分别输出用8位比特表示的发送信号, 将其接到D/A转换器进行数/模转换, 经过处理转换成模拟信号经滤波、功放后送入传输信道。

5) Target Perferences模块:DSP参数配置模块, 对DSP板的型号以及目标板参数进行配置。

其余模块与Simulink的仿真设置相同。

模块搭建完成后在Real-Time中设置关键参数 (见表1) 。通过相配套的仿真器以及CCSLink工具把MATLAB、代码调试器 (Code Composer Studio, CCS) 和硬件DSP连接起来, 点击“Incremental build”按钮, MATLAB完成相应的编译等建立工程的工作并连接至CCS, 由CCS完成代码的生成、编译链接、代码加载[7], 完成OFDM的调制。

接收端同样可以在Simulink上搭建系统模型, 系统结构基本与发送端相似, 添加同步、信道估计、均衡等相应模块, 类似于发送端方法经过MATLAB快速转换生成DSP代码。

4 实验结果

利用MATLAB平台快速DSP代码转换生成的办法, 建立OFDM的DSP仿真模型并进行快速DSP代码转换, DSP运行后输出的发送信号通过D/A转换、低通滤波器及运放后输出OFDM时域信号波形。

实验参数设置如下:OFDM符号产生速度为50个/s, 采用16QAM调制, IFFT点数为128点, 有效子载波数50, 选定子载波频率范围f3~f52, 构造对称频谱, 频率为f76~f125, 其余补零。在示波器上显示OFDM的时域信号波形 (见图4) , 示波器显示的OFDM符号周期为20 ms, 与仿真模型所设置的参数一致。DSP输出的OFDM波形与MATLAB仿真输出的波形一致。在验证DSP运行正确性的同时, 利用该实验方法可以很方便地测试不同参数情况下的系统性能。

为方便比较与观察IFFT点数对输出波形的影响, 将OFDM系统的子载波数量设置为1, OFDM符号周期为20 ms, IFFT点数选定为32点和128点, 设定子载波频率为f3, 构造共轭对称频谱频率分别为32点IFFT对应f31和128点IFFT对应共轭对称谱频率f127, 其他参数设置同前面介绍。为了研究IFFT点数对波形的影响, 暂时断开滤波器, 分别在Simulink仿真模型和DSP实际输出端得到发送信号的波形 (见图5、图6) 。由图对比可以得到, 示波器观测到的实际波形与仿真波形完全一致。IFFT点数不同导致波形图的周期内样本数不相同, 当IFFT点数增加时, 输出的模拟波形也较为光滑, 可以相应地降低对低通滤波器的负担。

5 结语

实验对电力线载波通信中OFDM技术的应用进行MATLAB建模仿真, 并利用Embedded Target for TI Platform工具包进行DSP代码转换及DSP实现, 同时比较IFFT点数对输出波形的影响, 对OFDM技术在电力线载波通信中的应用及其DSP代码自动生成进行了研究。

实验分析说明, 通过Simulink工具箱对OFDM通信系统进行建模仿真, 并通过与DSP目标板相配套的仿真器及连接DSP开发板与电脑, 应用MATLAB平台的Embedded Target for TI Platform工具包快速DSP代码转换生成的方法可行。并且利用这种方法快速生成DSP代码, 大大减少了DSP代码编程的开发周期, 将开发者从繁琐的手动编写代码中解放出来, 为DSP设计的学习提供了一个快速设计DSP的捷径。

摘要:为了研究OFDM技术在PLC通信中的应用及其DSP实现, 利用MATLAB系统中的Simulink建立OFDM模型, 仿真验证模型参数设置正确及技术指标符合要求后, 通过自动转换生成DSP代码, 经CCS编译后加载到DSP开发板运行产生OFDM波形。实验结果显示, DSP输出的OFDM调制波形与理论仿真结果一致, 验证了利用MATLAB模型转换方式生成DSP代码的可行性与正确性, 并分析了不同IFFT点数对输出波形的影响。利用该方法可以方便地研究OFDM等通信系统的性能, 快速地进行DSP开发应用。

关键词:电力线载波通信,OFDM,代码生成,MATLAB

参考文献

[1]何书毅.OFDM技术在电力线载波通信中的应用研究[J].中国新技术新产品, 2010 (4) :32–33.

[2]李晓亮.PLC电力线载波通信研究[D].西安:西安电子科技大学, 2009.

[3]崔玉峰, 杨晴, 张林山.OFDM通信技术在AMI及智能用电中的应用[J].云南电力技术, 2010, 38 (6) :1–3.

[4]曹一.基于Simulink的OFDM通信系统仿真分析[D].天津:天津大学, 2009.

[5]BODHE R.Design of simulink model for OFDM and comparison of FFT-OFDM and DWT-OFDM[J].International Journal of Engineering Science and Technology, 2012, 4 (5) :1914–1924.

[6]俞大泉, 林东.DSTFT的FSK通信系统建模仿真及DSP代码生成[J].单片机与嵌入式统应用, 2011, 11 (1) :33–35.YU Da-quan, LIN Dong.MATLAB simulation of FSK communication system and auto-generation of DSP code based on DSTFT[[J].Microcontrollers&Embedded Systems, 2011, 11 (1) :33–35.

代码自动生成 篇8

现在比较流行的代码生成方法有基于XSLT处理器的方法和基于模板的方法。XSLT定义了XML文档的转换语言,采用XPath对XML文档各部分进行定位,对XML文档的操作非常简单高效。现在大部分的代码自动生成系统采用的都是基于模板的方法。基于模板的方法就是把静态内容抽取成模板,把动态内容用特殊标记嵌在模板页面里面,利用模板引擎将动态标记赋值,即可生成按照模板定制的具体文件[2]。基于这种技术的工作过程是:代码生成器读取一个抽象需求的定义作为输入,然后根据规则将模板变量进行替代,再根据模板产生基于该需求的一个或多个文件。因为模板能够容易的改变而不会反过来影响模块和模块工具。这种方法为代码生成提供了快速的开发周期。为代码生成创建基于平台的模型的方法能够随要求的变化而快速的改变,而不会对应用程序模型带来任何不良后果。所以本文采用这种方式来实现代码的自动生成。

1 代码自动生成系统的设计

基于J2EE平台的代码自动生成工具主要是生成java类文件、数据访问对象(DAO)文件、持久层配置文件、表单映射文件、jsp文件等等。代码自动生成系统的输入可以是UML模型、数据库模型等。它的总体设计思路是:通过第三方的建模工具(例如MagicDraw UML),将UML表示的类图用XMI格式表示。XMI格式的文件保留了类的一些基本属性,例如类的属性,方法,关系等,是原始类的XML化的结果。然后通过解析所生成的XMI文件,可以获取类的相关信息,并将之存储在Java对象中。最后的部分就是代码生成,将已经有的java对象与事先准备好的代码模板合并,调用freemark模板引擎,即可生成目标文件。整个代码生成工具的工作过程可以用图1表示。

3 代码自动生成系统的实现

3.1 模型的解析

文作为模型交换的标准,XMI是代码自动生成工具必需支持的模型格式,只有这样,不同代码自动生成工具所使用的模型才具备可交互性。因此,本文的工具采用XMI格式的模型作为输入。这里的XMI文件是根据类图通过工具转换而来,包含了此类图的所有信息。

设计上把XML解析类设计为一个接口IParse,里面包括getClassInfo方法和getAttributeInfo方法,分别用来取得类的信息和取得属性的相关信息。然后通过调用JDOM提供的API库,编写一个JdomParserImp类,实现解析XML文件的功能,实现IParse接口。这样设计的好处在于,搭建了一个解析器的框架,而不局限于某种具体的解析引擎技术,当以后出现更好的解析引擎时,就可以重新实现IParse接口,而不需要修改其他模块的代码。设计类的模块如图2所示。

3.2 模板库的设计

由于本文采用基于模板的代码生成方法,那么模板库是这个转换工具的底层元素。模板库的样式即为所生成代码的原型。本文采用FreeMarker模板引擎,构造了javabean的模板库。

所有的Javabean文件都有它自己固定的结构,主要包括:该类所有的属性的声明,关于这些属性的get方法和set方法。下面就是关于javabean的模板文件javabean.ftl的内容。

所以只要知道某个类的属性名和它的类型,就可以很容易地制造出该类的javabean文件。从上述javabean的模板文件可以看出,模板文件可以直观地反映要生成的代码,易于管理。如果要生成其他内容,例如持久层的配置文件,数据访问层的相关文件,只需制作相应的模板即可。

3.3 目标文件的生成

目标文件生成器设计的主要思想就是:将从XMI文件解析出来的对象填充在模板文件适当的位置。IMerge中的generate方法就是实现此功能的方法。本系统的generate方法可以有多种实现,对应各种不同平台的代码生成,各生成器只需要实现自己特有的generate方法,就可以完成自己代码的生成。对应于本文的目标平台J2EE平台,我们需要分别生成javabean文件、持久层映射的配置文件、DAO文件、formbean文件。目标文件生成的设计图如图3所示。

4 代码自动生成系统的应用

下面以一个在线考试系统为例,实现该工具的应用。在线考试系统需要保存试卷信息,包括试卷本身和它所包含的每道题目信息;还需要保存用户的答题情况,包括用户整张试卷的答题情况和每道题目的答案。所以可以得到该模块的类图如图4所示。

运行代码自动生成系统,可以将输入的UML类图转换成一系列的目标文件输出。由于篇幅有限就不一一列出所生成的代码了。将生成的目标文件部署到应用服务器,就可以直接运行了。图5显示了运行成功的获取所有试卷的jsp页面。

该在线考试系统的开发中,由于运用了代码自动生成工具,大大减少了开发人员的工作量。经统计,该工具可以自动生成75%以上的代码,只有25%不到的代码需要程序员手动添加。因为代码都是工具自动生成的,代码的质量得到了保证,项目开发周期大大缩短。本来需要分配给一个团队四个人两个月开发时间的项目,现在只需要一个开发人员和一个美工在不到一个月的时间内即可完成,充分体现了该工具的实用价值。

5 结束语

本文描述了一种基于模板的代码自动生成系统的设计和实现,并利用它完成了基于J2EE-Web应用系统的代码自动生成。有了这种工具,项目开发组长可以快速地创建系统的原型;分析阶段和开发阶段被清楚地解耦。原型系统可以让用户在早期开发阶段就有参与系统设计的机会。此外,根据用户的反馈,原型系统可以很容易地被再次自动生成[5]。实践证明,代码自动生成系统有利于标准化和代码复用,方便了代码维护和代码迁移,提高了软件开发的速度和质量。

摘要:采用代码自动生成技术能为软件开发带来代码质量的提高、开发风险的降低等优点,提高了软件开发的速度和质量。该文描述了一种基于模板的代码自动生成系统的设计和实现,并利用它完成了基于J2EE-Web应用系统的代码自动生成工作。

关键词:代码生成,模板技术,模型驱动架构,J2EE

参考文献

[1]Object Management Group.MDA Guide[EB/OL].[2008-06-15].http://www.omg.org.

[2]Herrington J.Code Generation:The one page guide[EB/OL].http://codegeneration.net/files/JavaOne-OnePageGuide-v1.pdf.2003.

[3]陈翔,王学斌.代码生成技术在MDA中的实现[J].计算机应用研究,2006(1):147-150.

[4]Kleppe A,Warmer J,Bast W.MDA explained:the model driven architecture practice and promise[M].Boston:Addison Wesley Professional,2003:25.

代码自动生成 篇9

在软件开发的过程中,开发人员总是重复编写一些简单的代码,而且每当新技术来临,又不得不一再地重复过去的工作。同时,需求的变化也从来没有停止过[1]。为了解决这些问题,人们提出了代码自动生成技术。代码自动生成技术根据模型驱动架构MDA(Model Driven Architecture)的思想,将由开发人员描述的软件系统模型转换为代码,使得模型成为软件开发的核心制品,提升了软件开发的抽象层次,从而提高软件开发效率和软件的可维护性[2]。

统一建模语言UML是一种以图形方式对系统进行分析、设计的标准建模语言,使用UML建模可以清晰地表示系统的结构和行为信息。UML模型中的类图显示了系统中各个类的静态结构,顺序图描述了对象之间消息传递的时间顺序。因此,软件开发人员通常将二者结合描述软件系统的详细设计模型。代码自动生成就以类图和顺序图为输入,依据一定的转换规则生成具有静态和动态信息的代码。

目前关于将UML模型图生成代码的研究很多,文献[3,4]提出了一种将类图和顺序图生成具有结构和行为信息的Java代码的方法,文献[5]列出了多条顺序图到Java代码的转换规则,文献[6]提出了一种将类图生成C++代码的方法,文献[7]提出了静态模型到C代码的转换规则,文献[8]提出了一种将类图生成.Net组件代码的方法,文献[9]研究了代码自动生成技术中代码信息的来源。但对如何结合类图和顺序图生成与完整应用系统相接近的C++代码的研究却比较少。

本文提出了一种将类图和顺序图相结合生成包括静态结构和动态行为信息的C++代码的方法。

1 转换方法

图1描述了由UML模型转换到C++代码的过程框架,包括UML模型、UML元模型、代码生成器以及C++代码。UML模型包括语法正确的类图和顺序图,用来描述软件系统的静态结构和业务逻辑信息,由编程人员绘制。UML元模型包括类图、顺序图的元模型,用来定义类图、顺序图的绘制规则。代码生成器的核心是代码转换规则,转换规则是根据UML模型元素的特点和C++语言的代码结构建立的。自动生成代码时,首先输入符合UML元模型规则的UML模型,然后根据元模型的转换规则生成C++代码,其中类图生成C++中的类,顺序图生成方法内部的具体实现。

1.1 类图元模型

UML的元模型定义了使用UML描述对象模型的完整语法规则[10],图2为类图的元模型。UML类图中的类与元模型中的Class对应,属性和操作与Attribute和Operation对应。操作的参数对应Parameter,其中kind表示该参数的类型,若kind=in表示该参数为调用操作时传递的参数,kind=out则表示该参数为操作的返回值。各个类之间的关联关系用Association表示,Association End记录关联端的名称和属性。属性和参数的类型用Classifier表示。

1.2 顺序图元模型

图3为顺序图的元模型。一个顺序图用来描述类中的一个方法,对应元模型中的Interaction、Collaboration和Operation。其中Operation用来记录该顺序图所描述的方法的名称和可见性。顺序图中的对象对应元模型中的Classifier Role,不同对象间通信的消息对应Message,消息中要执行的动作对应Action,该动作执行的条件用recurrence表示,动作的内容用Request表示。动作分为调用(Call Action)、创建(Create Action)、发送(Send Action)、返回(Return Action)以及销毁(Destroy Action)。调用和创建操作的返回值用Return Var表示。参数的类型记录在Classifier中。

2 转换规则

下面介绍UML类图和顺序图到C++代码的转换规则。为了清楚准确地描述转换规则,UML模型元素使用文献[11]中定义的元素,转换规则中用到的标记如表1所示。转换规则使用表格描述,其中第一列为待转换的模型元素,第二列为该模型元素对应的元模型,第三列为该元模型对应的转换规则。

2.1 类图的转换规则

依据UML模型和C++代码的特性,得出类图和C++代码的关系为:UML模型中的类、属性、操作分别对应C++中的类、成员变量、成员函数。因此,UML类图到C++代码的转换规则包括类的转换规则、属性和操作的转换规则。

规则1 类的转换规则,如表2所示。待转换的模型元素是一个有名称的类,元模型中对象c的属性name记录了该类的名称。转换的第一步是.h文件(HFILE)和.cpp文件(CFILE)的生成,如表2转换规则第一行所示。.h文件包括头文件的引用IN-CLUDE、成员变量ATTRIBUTE和成员函数OPERATIONH的声明,被规则2中的内容替换。非终结符号c.name表示本规则所描述类的类名,被第一列中的类名所替换,语句#ifndef和#define后的c.name全部大写。在C++中,通常成员变量是私有的,成员函数是公有的,分别使用终结符号描述,如转换规则第二行所示。.cpp文件包括对应头文件的引用和成员函数OPERATIONC的具体实现,如转换规则第三行所示。

规则2 属性和操作的转换规则,如表3所示。待转换的是一个包含属性和操作的类,元模型中对象o和a分别记录操作和属性的名称,p1、p2分别为操作的参数和返回值,c1,c2,c3对应属性、参数、返回值的类型。转换规则的第一行定义了AT-TRIBUTE的转换过程,表示属性的类型和名称,二者组合完成了C++中成员变量的声明。该转换规则中的符号“_”是带下划线的空格,表示直接生成代码中的空格。OP-ERATIONH的转换过程与此相同。OPERATIONC的具体实现被顺序图生成的代码所替换,用非终结符号SEQUENCE表示。具体如规则4所示。

下面通过Reader类描述类转换到.h文件的过程,如表4所示,Reader类有一个int类型的属性id和一个返回值类型是void的操作create()。根据其元模型,对应的转换规则分别包括属性、操作、和参数的转换规则。根据规则1生成的代码如表中转换过程第一行所示,ATTRIBUTE和OPERATION还需要做进一步的转换,其他部分为最终生成的代码。根据规则2生成的代码如表中第二到四行所示,属性和操作的声明分别为int id和void create(int id)。最后经过合并,得到的代码如下所示。

规则3 关联关系的转规则,如表5所示。元模型中c1,c2表示两个相互关联的类,它们之间的关联关系用a记录。转换规则中定义了在Class A中添加对Class B的引用,用符号IN-CLUDE表示。

2.2 顺序图的转换规则

依据UML模型和C++代码的特性,得出顺序图和C++代码的关系为:UML顺序图中的内容对应C++成员函数的具体实现细节,顺序图中的分支、函数调用、对象创建分别对应C++中的if语句、“.”运算符、new关键字。因此,顺序图到C++代码的转换规则包括顺序图的转换规则、条件的转换规则、变量赋值的转换规则、对象创建的转换规则、方法调用的转换规则、消息发送的转换规则。消息返回和对象销毁操作不需要生成对应的代码。

规则4 顺序图的转换规则,如表6所示。对象o表示该顺序图是方法oper()的具体实现。非终结符号SEQUENCE被LOCALDATA和MESSAGE替换,说明一个方法的实现细节中包括局部变量的定义以及各对象之间相互传递消息的过程。

规则5 条件的转换规则,如表7所示。对象a向对象b发送了一个包含条件的消息。元模型中对象m的属性recurrence记录了条件的内容,当该条件满足时,执行这个消息上的操作。C++中使用if语句表示条件,如表中第三列所示。

规则6 变量赋值的转换规则,如表8所示。将Class B的方法oper()的返回值赋给变量x,元模型中对象rv表示变量的名称,r记录了方法的名称,c表示函数的返回值类型即变量的类型。转换规则的第一行显示了变量x的定义,第二行中非终结符号ASIGNMENT表示该变量会被赋值,它可以被函数的返回值赋值,如规则8所示,也可以被常量或表达式赋值。

规则7 对象创建的转换规则,如表9所示。“<<create>>”表示该消息是一个创建对象的操作。元模型中r表示待创建的对象的名称,p和c2表示参数的名称和类型。C++中有两种创建对象的方式,分别为Class Name object(param)和ClassName*object=new Class Name(param)。为了避免用户使用完对象后忘记删除而造成内存泄露,本文采用第一种对象创建的方法。转换规则第一行定义了创建对象时需要使用的参数,第二行MESSAGE生成对象创建的代码,第三行中非终结符号PARA转换为待传递的参数(PARAMETER包含参数的类型和名称,PARA仅包含参数的名称)。

规则8 方法调用的转换规则,如表10所示。消息的内容为调用对象object B中的方法oper()。元模型中cr记录对象的名称,r表示方法的名称,p和c2表示参数的名称及类型。方法调用包括其他对象方法的调用和对象自身方法的调用。C++中使用符号“.”访问成员函数,转换规则的第二行显示了调用其他对象的方法并将结果赋值给一个变量的规则,当调用自身方法时,去掉即可。无返回值的方法的转换规则如表中第四行所示。

规则9 消息发送的转换规则,如表11所示。消息中不包含任何方法调用或者对象创建的内容,元模型中r记录了消息的内容。消息发送包括发送到其他对象的消息和发送给自身的反身消息。二者都只需直接将消息中的内容直接生成代码,如表中第三列所示。

3 实例分析

本文以图书管理系统为例,描述如何结合类图和顺序图生成包含结构和行为信息的C++代码。为了便于理解,对实例进行了简化,只给出了一个概念性的模型。该模型包括图书Book、读者Reader、借书记录Record以及系统交互界面Sys Interface四个类,如图4所示。读者可以使用该系统借书、还书、查看书籍的详细信息。

图5是Sys Interface类中方法return()对应的顺序图。还书时,系统首先提示用户输入图书编号book ID,并根据该编号创建一个Book类的对象b,调用search()方法将该书的信息从数据库读入b中。然后将该书的数量加1,调用modify()方法将更新后的结果保存至数据库中。最后创建一个图书编号为book ID,读者编号为reader ID的Record对象rc,调用该对象的方法del()在数据库中删掉该条借阅记录。

类Sys Interface生成代码的过程为:

(1)应用规则1,生成Sys Interface.h和Sys Interface.cpp文件;

(2)应用规则2,生成Sys Interface.h文件中的成员变量choice、book ID、reader ID和成员函数run()、login()、borrow()、return()、display Book()的声明,Sys Interface.cpp文件中成员函数run()、login()、borrow()、return()、display Book()的框架;

(3)应用规则3,生成Sys Interface.h文件中对Book.h、Record.h、Reader.h文件的引用;

(4)应用规则4,生成return()函数的具体实现结构,先声明变量,然后顺序显示对象间传递的消息;

(5)应用规则9,生成提示用户输入图书编号,并存储该编号到变量book ID的语句;

(6)应用规则7,生成创建Book类的对象b的语句;

(7)应用规则8,生成调用对象b的方法search()的语句;

(8)应用规则6和规则8,生成变量num的声明和赋值语句;

(9)应用规则9,生成变量num加1操作的语句;

(10)应用规则8,生成调用对象b的方法set Num()和modify()的语句;

(11)应用规则7,生成类Record的对象rc的创建语句;

(12)应用规则8,生成调用对象rc的方法del()的语句;

经过以上步骤后自动生成的代码如表12所示,由于篇幅有限,Sys Interface.cpp中没有列出run()、login()、display Book()的框架。结果显示,在search()、modify()、get Num()、set Num()、del()已定义的条件下,自动生成的return()方法清楚地描述了还书操作的执行流程,是可执行的C++代码。由此可见,可以使用细粒度的顺序图描述各个方法的动态行为信息,应用本文提出的转换规则生成与图书管理系统相接近的C++代码。与多数文献中提出的只能生成代码框架的方法相比,本文提出的方法可以生成内容更加完整的代码。

根据以上研究成果,本文实现了一个基于PSM的代码自动生成工具,该工具采用Eclipse插件开发的方式,元模型使用基于XML的元数据表示方法,转换规则使用VTL编写的模板实现,代码生成器使用JDom技术解析XML文件内容,然后使用Velocity引擎根据模板的访问请求返回相应的模型数据并与代码模板合并生成目标代码。如图6所示,其中包括Sys Interface类对应的XML文件、类图到C++代码的转换模板、自动生成的C++代码。由于时间有限,顺序图到C++代码的转换模板还未完成。Veolcity技术的使用提高了代码的生成效率与代码模板的灵活度,从而为生成可执行的代码提供了技术支持。

4 结语

本文描述了一种结合类图和顺序图生成具有结构和行为信息的C++代码的方法。先将UML模型中的各个元素映射到相应的元模型,然后根据元模型的转换规则逐步生成C++代码。与多数文献中提出的只能生成C++代码框架的方法相比,本文提出的方法可以生成内容更加完整的代码,减少了编程人员手动添加代码的工作,从而提高了软件的开发效率和软件产品的质量。由于顺序图不易于描述代码中复杂的逻辑信息,所以部分逻辑复杂的代码需要编程人员手动添加或者参考活动图的信息。此外本论文只关注了类图和顺序图中的主要模型元素,类图中的关联类、受限关联以及顺序图中的片段都还没有相应的转换规则。所以,下一步将继续完善类图和顺序图的转换规则并提出活动图的转换规则。

参考文献

[1]Anneke Kleppe,Jos Warmer,Wim Bast.解析MDA[M].鲍志云,译.北京:人民邮电出版社,2004.

[2]刘辉,麻志毅,邵维忠.元建模技术研究进展[J].软件学报,2008,19(6):1317-1327.

[3]Abilio G Parada,Eliane Siegert,Lisane B de Brisolara.Generationgjava code from UML class and sequence diagrams[C]//2011 BrazilianSymposium on Computing System Engineering(SBESC).Pelotas,Brazil,2011.

[4]Usman M,Nadeem A.Automatic generation of java code from UML di-agrams using UJECTOR[C]//International Journal of Software Engi-neering and its applications(IJSEIA),2009,3.

[5]Mathupays Thongmak,Pornsiri Muenchaisri.Design of rules for trans-forming UML sequence diagrams into java code[C]//Proceedings ofNinth Asia-Pacific Software Engineering Conference.Gold Coast,Aus-tralia,2002.

[6]Dan Regep,Fabrice Kordon.Using metascribe to prototype an UML toC++/Ada code generator[C]//11th International Workshop on Rap-id System Prototyping.Paris,France,2000.

[7]由志远.基于MDA的嵌入式软件代码生成器设计与实现[D].西安:西安电子科技大学,2010.

[8]Deuk Kyu Kum,Soo Dong Kim.A systematic method to generate.Netcomponents from MDA/PSM for pervasive service[C]//Fourth Inter-national Conference on Software Engineering Research,Managementand Applications.Washington,USA,2006.

[9]Jichen Fu,Wei Hao,Farlkh B Bastani.Model-Driven Development:Where Does the Code Come from?[C]//Fifth IEEE International Confer-ence on Semantic Computing.Palo Alto.USA,2011.

[10]OMG.UML Semantics.Version 1.1[R].The Object Management Group,Document ad/97-08-05,Framingham MA,1997.

上一篇:居住区活动区下一篇:接地装置设计及施工