有关语法数据库Clone的设计方案
2008-12-27 20:49
253 查看
最近有这样的需求:需要为自己设计的语法数据库GDB( Grammar DataBase)提供Clone机制的支
持,具体来说,就是给定一个GDB语法树TreeA,希望能够复制出一份跟TreeA内容一模一样的语法树
TreeB,TreeB除了内容跟TreeA一模一样外,跟TreeA不存在任何依赖关系,即使删除了TreeA,也可以
正常访问TreeB中的内容。
这样的一个功能,说起来比较简单,实现起来还有一点讲究,也遇到了一些问题。
第一个问题就是,选择什么样的策略来实现对GDB的Clone支持?第一眼看见这个需求,映射入我脑海
中的是Prototype模式。但是细想起来,Prototype并不完全适合我这里的需求。
在GDB里边,类的层次关系比较复杂,为了便于描述,让我们将GDB的类层次结构视为一个树形结
构,其中叶子结点对应具体的语法类,可以据以创建出一个语法结点,非叶子节点对应的是抽象类或基
类,不能据以创建出语法结点(但是出于程序设计的弹性方面的考虑,在GDB中存在大量的通过基类,
抽象类指针,指向一个具体的语法类对象的场景)。在这个树形结构中,有出现在第一层的叶子结点,
也有出现在第二层,第三层的叶子结点。而引用这些叶子结点类的地方,既有通过具体类型指针的,也有
通过基类指针的。比如说:
class GDBCompilerDirective : GDBNode {
......
};
class GDBStmt : GDBNode {
......
};
class GDBMainProg {
public:
GDBCompilerDirective * directive_;
vector< GDBStmt* > stmts_;
};
GDBStmt就是一个抽象语句类,从它会派生出具体语句对应的语法类,比如
GDBForStmt,GDBWhileStmt,。。。
而GDBCompilerDirective则是一个具体的语法类,用来存放编译制导控制的相关信息。
那么想要实现Clone一个GDBMainProg对象的操作,就需要对它内部的directive_和stmts_区分处
理了。
对stmts_的Clone可以通过为GDBStmt增加clone()虚方法的接口,在GDBStmt的具体派生类中
增加相应的clone()方法实现来完成。
为GDBStmt增加的clone()接口描述如下:
virtual GDBStmt * clone();
这样的接口,符合GDBMainProg的需求,即GDBMainProg关心的只是GDBStmt基类指针。
下面再来看一下对directive_的Clone操作的实现。directive_是用一个具体语法类的指针来指向的。所
以Clone的结果也需要返回一个具体语法类的指针。但是怎样为GDBCompilerDirective定义一个
clone()方法就有些为难了。将clone()方法定义为这样的形式;
形式1:
virtual GDBNode * clone()
或是这样的形式;
形式2:
GDBCompilerDirective * clone();
感觉都不是太好。
定义成形式1的话,需要对clone()的结果进行一个类型cast的动作以后才能供GDBMainProg使用,增
加了类型不安全的风险。
而定义成形式2的话,clone()内部只会包括一个干巴巴的copy constructor的调用,又有些多余,颇有
杀鸡用牛刀的感觉。
最后我的方案是作了一个折衷,对于会以基类指针形式访问的语法类,为其增加一个copy
constructor及一个clone()接口,该接口在基类中定义为一个纯虚方法,在具体类中给出实现内容,
该方法返回的是基类的指针;对于以具体类指针形式访问的语法类,只为其增加一个copy
constructor。
第二个问题就是关于copy constructor的实现。如上所述,在GDB中,有些语法类提供了clone()接
口,对其的Clone操作直接通过调用clone()接口即可;有些语法类则没有提供clone()接口,仅提供
了copy constructor的实现;还有些特殊的语法类,设计时出于效率的考虑,该类型的指针指向的
具体内容是由一个全局容器管理的, 为其执行Clone操作也只是进行指针级别的复制。
所以对不同的语法类,执行Clone操作所需调用的具体动作也是有所区别,如果只是手工编写代码来处
理这种差异是肯定可以的,但是考虑到需要增加Clone操作的类很多,纯手工处理是一件既耗时
也容易出错的力气活。所以,引入了一些GP的小技巧来协助处理。细节就不说了,大致的思想就是定
义一个泛型函数g_clone_t(),在需要执行Clone动作的地方调用泛型函数g_clone_t,由
g_clone_t根据当前的上下文信息(包括具体需要Clone的语法类的类型以及该语法类的Clone动
作类型)来执行相应的具体动作。
g_clone_t的伪码描述如下:
<template class cloneType>
cloneType* g_clone_t( cloneType* obj )
{
// Type1: obj提供了clone()接口
return obj->clone();
// Type2: obj未提供clone()接口,仅实现了copy constructor
return new cloneType( *obj );
// Type3: obj指向的内容是通过全局容器管理的
return obj;
}
g_clone_t具体的实现运用到了一些template specialization, traits的技巧,在这里不再多述。
第三个问题就是GDB对应的类比较多,粗略统计了一下,需要增加Clone()方法及相关的copy
constructor的类大概有40个之多,clone()方法的内容并不多,copy constructor的实现也不算复杂,
但是要为这么多类增加支持,工作量上可不小。我没有选择纯手工完成这些工作,而是借助了宏处理
来简化工作量:
1)。定义了一组宏来简化clone()接口的定义
#define CLONE_HOOK_1( classType ) virtual classType * clone() const = 0;
#define CLONE_HOOK_2( returnClassType, realClassType ) virtual returnClassType * clone() { /
return new realClassType( *this ); /
}
2). 定义了一组宏来简化copy constructor的实现
#define CLONE_FIELD( srcObj, field ) clone_t( field, srcObj.field )
#define CLONE_FIELDS_2( srcObj, field1, field2 ) do { /
CLONE_FIELD( srcObj, field1 ); /
CLONE_FIELD( srcObj, field2 ); /
} while ( 0 )
#define CLONE_FIELDS_3( srcObj, field1, field2, field3 ) do { /
CLONE_FIELDS_2( srcObj, field1, field2 );
CLONE_FIELD( srcObj, field3 );
} while ( 0 )
#define CLONE_FIELDS_4 ...
有了这些宏以后,在为一个语法类增加clone()接口的时候,只需要调用宏CLONE_HOOK_1或
CLONE_HOOK_2即可,为一个语法类实现copy constructor的时候,只需要调用
CLONE_FIELD/_1/_2/_3/_4/...即可,具体的重复的事情,就由预处理器替我们完成了。
注:文中提到的以大写C开头的Clone表示Clone操作,以小写c开头的clone则表示的是具体实现的
clone()方法,为一个语法类实现Clone操作可能会需要定义并实现相应的clone()方法,也可以不为该
语法类定义clone方法,而是通过别的方式来完成。
持,具体来说,就是给定一个GDB语法树TreeA,希望能够复制出一份跟TreeA内容一模一样的语法树
TreeB,TreeB除了内容跟TreeA一模一样外,跟TreeA不存在任何依赖关系,即使删除了TreeA,也可以
正常访问TreeB中的内容。
这样的一个功能,说起来比较简单,实现起来还有一点讲究,也遇到了一些问题。
第一个问题就是,选择什么样的策略来实现对GDB的Clone支持?第一眼看见这个需求,映射入我脑海
中的是Prototype模式。但是细想起来,Prototype并不完全适合我这里的需求。
在GDB里边,类的层次关系比较复杂,为了便于描述,让我们将GDB的类层次结构视为一个树形结
构,其中叶子结点对应具体的语法类,可以据以创建出一个语法结点,非叶子节点对应的是抽象类或基
类,不能据以创建出语法结点(但是出于程序设计的弹性方面的考虑,在GDB中存在大量的通过基类,
抽象类指针,指向一个具体的语法类对象的场景)。在这个树形结构中,有出现在第一层的叶子结点,
也有出现在第二层,第三层的叶子结点。而引用这些叶子结点类的地方,既有通过具体类型指针的,也有
通过基类指针的。比如说:
class GDBCompilerDirective : GDBNode {
......
};
class GDBStmt : GDBNode {
......
};
class GDBMainProg {
public:
GDBCompilerDirective * directive_;
vector< GDBStmt* > stmts_;
};
GDBStmt就是一个抽象语句类,从它会派生出具体语句对应的语法类,比如
GDBForStmt,GDBWhileStmt,。。。
而GDBCompilerDirective则是一个具体的语法类,用来存放编译制导控制的相关信息。
那么想要实现Clone一个GDBMainProg对象的操作,就需要对它内部的directive_和stmts_区分处
理了。
对stmts_的Clone可以通过为GDBStmt增加clone()虚方法的接口,在GDBStmt的具体派生类中
增加相应的clone()方法实现来完成。
为GDBStmt增加的clone()接口描述如下:
virtual GDBStmt * clone();
这样的接口,符合GDBMainProg的需求,即GDBMainProg关心的只是GDBStmt基类指针。
下面再来看一下对directive_的Clone操作的实现。directive_是用一个具体语法类的指针来指向的。所
以Clone的结果也需要返回一个具体语法类的指针。但是怎样为GDBCompilerDirective定义一个
clone()方法就有些为难了。将clone()方法定义为这样的形式;
形式1:
virtual GDBNode * clone()
或是这样的形式;
形式2:
GDBCompilerDirective * clone();
感觉都不是太好。
定义成形式1的话,需要对clone()的结果进行一个类型cast的动作以后才能供GDBMainProg使用,增
加了类型不安全的风险。
而定义成形式2的话,clone()内部只会包括一个干巴巴的copy constructor的调用,又有些多余,颇有
杀鸡用牛刀的感觉。
最后我的方案是作了一个折衷,对于会以基类指针形式访问的语法类,为其增加一个copy
constructor及一个clone()接口,该接口在基类中定义为一个纯虚方法,在具体类中给出实现内容,
该方法返回的是基类的指针;对于以具体类指针形式访问的语法类,只为其增加一个copy
constructor。
第二个问题就是关于copy constructor的实现。如上所述,在GDB中,有些语法类提供了clone()接
口,对其的Clone操作直接通过调用clone()接口即可;有些语法类则没有提供clone()接口,仅提供
了copy constructor的实现;还有些特殊的语法类,设计时出于效率的考虑,该类型的指针指向的
具体内容是由一个全局容器管理的, 为其执行Clone操作也只是进行指针级别的复制。
所以对不同的语法类,执行Clone操作所需调用的具体动作也是有所区别,如果只是手工编写代码来处
理这种差异是肯定可以的,但是考虑到需要增加Clone操作的类很多,纯手工处理是一件既耗时
也容易出错的力气活。所以,引入了一些GP的小技巧来协助处理。细节就不说了,大致的思想就是定
义一个泛型函数g_clone_t(),在需要执行Clone动作的地方调用泛型函数g_clone_t,由
g_clone_t根据当前的上下文信息(包括具体需要Clone的语法类的类型以及该语法类的Clone动
作类型)来执行相应的具体动作。
g_clone_t的伪码描述如下:
<template class cloneType>
cloneType* g_clone_t( cloneType* obj )
{
// Type1: obj提供了clone()接口
return obj->clone();
// Type2: obj未提供clone()接口,仅实现了copy constructor
return new cloneType( *obj );
// Type3: obj指向的内容是通过全局容器管理的
return obj;
}
g_clone_t具体的实现运用到了一些template specialization, traits的技巧,在这里不再多述。
第三个问题就是GDB对应的类比较多,粗略统计了一下,需要增加Clone()方法及相关的copy
constructor的类大概有40个之多,clone()方法的内容并不多,copy constructor的实现也不算复杂,
但是要为这么多类增加支持,工作量上可不小。我没有选择纯手工完成这些工作,而是借助了宏处理
来简化工作量:
1)。定义了一组宏来简化clone()接口的定义
#define CLONE_HOOK_1( classType ) virtual classType * clone() const = 0;
#define CLONE_HOOK_2( returnClassType, realClassType ) virtual returnClassType * clone() { /
return new realClassType( *this ); /
}
2). 定义了一组宏来简化copy constructor的实现
#define CLONE_FIELD( srcObj, field ) clone_t( field, srcObj.field )
#define CLONE_FIELDS_2( srcObj, field1, field2 ) do { /
CLONE_FIELD( srcObj, field1 ); /
CLONE_FIELD( srcObj, field2 ); /
} while ( 0 )
#define CLONE_FIELDS_3( srcObj, field1, field2, field3 ) do { /
CLONE_FIELDS_2( srcObj, field1, field2 );
CLONE_FIELD( srcObj, field3 );
} while ( 0 )
#define CLONE_FIELDS_4 ...
有了这些宏以后,在为一个语法类增加clone()接口的时候,只需要调用宏CLONE_HOOK_1或
CLONE_HOOK_2即可,为一个语法类实现copy constructor的时候,只需要调用
CLONE_FIELD/_1/_2/_3/_4/...即可,具体的重复的事情,就由预处理器替我们完成了。
注:文中提到的以大写C开头的Clone表示Clone操作,以小写c开头的clone则表示的是具体实现的
clone()方法,为一个语法类实现Clone操作可能会需要定义并实现相应的clone()方法,也可以不为该
语法类定义clone方法,而是通过别的方式来完成。