<学习笔记>Algorithm Library Design 算法库设计in c++ IV(GenVoca 架构)
GenVoca 架构
GenVoca是一种软件架构,与policy-based架构类似的是,它的最终成品也是由各个组件组装而成的,但是组织的方式有所不同。
在Policy-based架构中,组件作为具体的policy实现被添加到host class以实现相应的功能,而在GenVoca中组件作为wrappers (layers) on top of a more basic components adding functionality to it.
个人感觉从原文给的list的一个实现来看,与基于policy的架构相比,GenVoca多了不同的层次,上层需要下层的不同实现。如B层需要下面的C来实现,而实现后的B又可作为实现提供给更上层的A。
R : A | B[R] S : C[R] | D[S] | E[R,S]
R可以由A实现,或者由B和R的一个实现合起来实现。
S可以由C以及R的某个实现,合起来实现。
GenVoca实例:List
上图是一个List的特征图。特征是从用户使用角度来看的,用户装配List要提供的参数。
必要的特征
我需要List 操纵什么样的数据?
List如何持有数据?通过引用持有还是深拷贝的方法持有。
对于数据操作,我们采用多态性吗?
可选的特征
需要一个变量记录长度吗->如果需要长度的数据类型是什么int short 还是long
需要Tracing吗,这里意思是说我们要追踪过程打印过程信息吗?
设计一个GenVoca架构需要考虑下面几个步骤:
- 确定特种图中提到的主要职责,要提供的主要功能。对于List:
- 数据的存储
- 数据的拷贝
- 数据的销毁(注意这个对应到特征图,如果用户选择copy持有数据,或者owned reference那么要自己负责delete,否则不需要,empty delete就好)
- 动态类型检查(为了monomorphism)
- 长度计数器
- tracing
- 确定组件分组:
- BasicList: PtrList //:左边代表策略组件,右边是不同的实现策略categories : components
- Copier: PolymorphicCopier, MonomorphicCopier, EmptyCopier
- Desroyer: ElementDestroyer, EmptyDestroyer
- TypeChecker: DynamicTypeChecker, EmptyTypeChecker
- Counter: LengthList
- Tracing: TracedList
- 确定使用依赖`use'' dependencies between component categories.
- Sort the categories into a layered architecture.得到具体架构。最底层的copier,destoryer,typechecker,作为config.
The component categories that do not depend on other categories are grouped into the bottom ConfigurationRepository (Config for short) layer category.
Tracing和Counter是可选的分组。
个人注,上图其实很清楚的标明了List的整体架构设计。
BasicList(其实就是用PtrList类实现)有3个策略copier,destoryer,typecheker,通过选择不同的策略组合不同的list。其实就是基于policy的设计。
然后counter用basicList
那么采用模版继承的方法。
<typename Base>
class Derived: public Base
通过把basicList作为模版参数,并继承,使得counter复用basiclist,同时它自己提供basicList没有的长度计数器,修改相应操作维护长度计数。
Tracing 使用counter那它在最上层,同样通过相同方式继承Counter即可,然后修改相应操作添加过程打印的信息。
- 写下GenVoca grammar
List : TracedList[OptCounterList] | OptCounterList OptCounterList : LengthList[BasicList] | BasicList BasicList : PtrList[Config] Config : Copier : PolymorphicCopier | MonomorphicCopier | EmptyCopier Destroyer : ElementDestroyer | EmptyDestroyer TypeChecker : DynamicTypeChecker | EmptyTypeChecker ElementType : [ElementType] LengthType : int | short | long | ...
实现细节
用户可以自己定义一个符合自己需求的的config
struct TracedIntListConfig { typedef int ElementType; typedef const ElementType ElementArgumentType; typedef MonomorphicCopier<ElementType> Copier; typedef ElementDestroyer<ElementType> Destroyer; typedef DynamicTypeChecker<ElementType> TypeChecker; typedef TracedList<PtrList<TracedIntlListConfig> > FinalListType; }; typedef TracedIntListConfig::FinalListType TracedIntList;
这里要注意的是出现了模版参数循环依赖。这样才能给出具体的最终List类型。
注,其实这个config不应该是最终提供给用户,这些参数是最终决定List类型的,但不应由用户设定这样的参数,这个还是一个比较内部的config,例如具体采用什么Destroyer对于用户应该是透明的。
用户可能会设置出使用MonomorphicCopier 和 EmptyDestroyer这样会带来内存泄漏的错误配置。
这里面应该多一层抽象层,隔离开来。用户只要声明我用copy持有数据,或者说我用ownwed refernce,那么库的内部就会决定使用ElementDestroyer。
而用户声明要使用external refernce持有数据的话,库的内部就会决定使用EmptyDestroyer,不做显示的delete,即我不负责数据的释放。
后面会给出如何加上这一层抽象,所谓的Generator方法,让用户更高层次的设定参数。
OK,我们先实现出一个基本的PtrList,这是一个最基本的list,它有很多策略参数,具体策略的实现用户给出在Config中。
template <class Config_> class PtrList { public: // export Config typedef Config_ Config; private: // retrieve the needed types from the repository typedef typename Config::ElementType ElementType; typedef typename Config::ElementArgumentType ElementArgumentType; typedef typename Config::Copier Copier; typedef typename Config::Destroyer Destroyer; typedef typename Config::TypeChecker TypeChecker; typedef typename Config::FinalListType FinalListType; // data members ElementType* head_; FinalListType* tail_; // note: not PtrList* but FinalListType* public: PtrList (ElementArgumentType& h, FinalListType *t = 0) : head_(0), tail_(t) { set_head(h); } ~PtrList() { Destroyer::destroy(head_); } void set_head(ElementArgumentType& h) { TypeChecker::check(h); head_ = Copier::copy(h); } ElementType& head() { return *head_; } void set_tail(FinalListType *t) { tail_ = t; } FinalListType* tail() const { return tail_; } };
//对这个最基本的list的使用
template <class List> void print_list(List* l) { std::cout << "[ "; for ( ; l; l = l->tail() ) std::cout << l->head() << " "; std::cout << "]\n"; } template <class List> void push_front(typename List::ElementArgumentType& e, List*& l) { l = new List(e, l); } int main() { typedef ListConfig::FinalListType List; List* ls = 0; push_front(1,ls); push_front(2,ls); push_front(3,ls); print_list(ls); // prints "3 2 1" }
注意list的实现中将一些操作转交给别的类去做,也即某些策略参数,如Copier,Destoyer
template <class ElementType> struct MonomorphicCopier { static ElementType* copy(const ElementType& e) { return new ElementType(e); } }; template <class ElementType> struct PolymorphicCopier { static ElementType* copy(const ElementType& e) { return e.clone(); // polymorphic copy using } // virtual member function clone() }; template <class ElementType> struct EmptyCopier { static ElementType* copy(ElementType& e) { // note: not const return &e; // no copy } }; template <class ElementType> struct ElementDestroyer { static void destroy(ElementType* e) { delete e; } }; template <class ElementType> struct EmptyDestroyer { static void destroy(ElementType* e) {} // do nothing }; template <class ElementType> struct DynamicTypeChecker { static void check(const ElementType& e) { assert(typeid(e)==typeid(ElementType)); } }; template <class ElementType> struct EmptyTypeChecker { static void check(const ElementType& e) {} };
最后更上层的laer采用inheritance-based wrappers实现。如LengthList
template <class BasicList> class LengthList : public BasicList { public: // export config typedef typename BasicList::Config Config; private: // retrieve the needed types from the repository typedef typename Config::ElementType ElementType; typedef typename Config::ElementArgumentType ElementArgumentType; typedef typename Config::LengthType LengthType; typedef typename Config::FinalListType FinalListType; LengthType length_; LengthType compute_length() const { return tail() ? tail()->length()+1 : 1; } public: LengthList (ElementArgumentType& h, FinalListType *t = 0) : BasicList(h, t) { length_ = compute_length(); } void set_tail(FinalListType *t) { BasicList::set_tail(t); length_ = compute_length(); } LengthType length() const { return length_; } };
Generators
下面给出为了给用户更好的设置接口,进行抽象的方法。
enum Ownership {ext_ref, own_ref, cp}; enum Morphology {mono, poly}; enum CounterFlag {with_counter, no_counter}; enum TracingFlag {with_tracing, no_tracing};
上面是用户需要知道的,他们所应该关心的List配置参数。
template <bool condition, class Then, class Else> struct IF { typedef Then RET; }; template <class Then, class Else> struct IF<false, Then, Else> { typedef Else RET; };
上面是利用模版偏特化,编译期间选择类型的一种技巧。
下面是具体的Generator的实现
template < class ElementType_, Ownership ownership = cp, Morphology morphology = mono, CounterFlag counter_flag = no_counter, TracingFlag tracing_flag = no_tracing, class LengthType_ = int > class LIST_GENERATOR { public: // forward declaration of the configuration repository struct Config; private: // define the constants used for type selection enum { is_copy = ownership==cp, is_own_ref = ownership==own_ref, is_mono = morphology==mono, has_counter = counter_flag==with_counter, does_tracing = tracing_flag==with_tracing }; // select the components typedef typename IF<is_copy || is_own_ref, ElementDestroyer<ElementType_>, EmptyDestroyer<ElementType_> >::RET Destroyer_; typedef typename IF<is_mono, DynamicTypeChecker<ElementType_>, EmptyTypeChecker<ElementType_> >::RET TypeChecker_; typedef typename IF<is_copy, typename IF<is_mono, MonomorphicCopier<ElementType_>, PolymorphicCopier<ElementType_> >::RET, EmptyCopier<ElementType_> >::RET Copier_; typedef typename IF<is_copy, const ElementType_, ElementType_ >::RET ElementArgumentType_; // define the list type typedef PtrList<Config> BasicList; typedef typename IF<has_counter, LengthList<BasicList>, BasicList >::RET OptLengthList; typedef typename IF<does_tracing, TracedList<OptLengthList>, OptLengthList >::RET List; public: // return the final list type typedef List RET; // define the configuration repository struct Config { typedef ElementType_ ElementType; typedef ElementArgumentType_ ElementArgumentType; typedef Copier_ Copier; typedef Destroyer_ Destroyer; typedef TypeChecker_ TypeChecker; typedef LengthType_ LengthType; typedef RET FinalListType; }; };
用户可以这样使用来配置LIST
ypedef LIST_GENERATOR<int,cp,mono,no_counter,with_tracing>::RET TracedIntList;
如果不需要traing
typedef LIST_GENERATOR<int>::RET IntList;
最后Shape,Square,Circle用到虚函数,因为我们操作一些列图形几何,需要动态多态。
而list没有采用虚函数,只是继承类对父类的覆盖,因为对list没有用到动态多态,直接用子类指针。
另外当前这个程序只是演示GenVoca构架的思想,其实还存在很多问题,比如当前list如果是拷贝构造而来或者owned reference,它的析构函数只能析构一个list的首元素,其它的
元素无法析构,需要写类外面的一个destory函数,destory(list* l);