GP技术的展望——道生一,一生二by莫华枫长期以来,我们始终把GP(泛型编程)作为一种辅助技术,用于简化代码结构、提高开发效率。从某种程度上来讲,这种观念是对的。因为迄今为止,GP技术还只是一种编译期技术。只能在编译期发挥作用,一旦软件完成编译,成为可执行代码,便失去了利用GP的机会。对于现在的多数应用而言,运行时的多态能力显得尤为重要。而现有的GP无法在这个层面发挥作用,以至于我这个“GP迷”也不得不灰溜溜地声称“用OOP构建系统,用GP优化代码”。然而,不久前,在TopLanguagegroup上的一次讨论,促使我们注意到runtimeGP这个概念。从中,我们看到了希望——使GPruntime化的希望——使得GP有望在运行时发挥其巨大的威力,进一步为软件的设计与开发带来更高的效率和更灵活的结构。在这个新的系列文章中,我试图运用runtimeGP实现一些简单,但典型的案例,来检测runtimeGP的能力和限制,同时也可以进一步探讨和展示这种技术的特性。运行时多态现在的应用侧重于交互式的运作形式,要求软件在用户输入下作出响应。为了在这种情况下,软件的整体结构的优化,大量使用组件技术,使得软件成为“可组装”的系统。而接口-实现分离的结构形式很好地实现了这个目标。多态在此中起到了关键性的作用。其中,以OOP为代表的“动多态”(也称为“subtyping多态”),构建起在运行时可调控的可组装系统。GP作为“静多态”,运用泛化的类型体系,大大简化这种系统的构建,消除重复劳动。另外还有一种鲜为人知的多态形式,被《C++Template》的作者DavidVandevoorde和NicolaiM.Josuttis称为runtimeunbound多态。而原来的“动多态”,即OOP多态,被细化为runtimebound多态;“静多态”,也就是模板,则被称为staticunbound多态。不过这种称谓容易引起误解,主要就是unbound这个词上。在这里unbound和bound是指在编写代码时,一个symbol是否同一个具体的类型bound。从这点来看,由于GP代码在编写之时,面向的是泛型,不是具体的类型,那么GP是unbound的。因为现有的GP是编译期的技术,所以是static的。OOP的动多态则必须针对一个具体的类型编写代码,所以是bound的。但因为动多态可以在运行时确定真正的类型,所以是runtime的。至于runtimeunbound,以往只出现在动态语言中,比如SmallTalk、Python、Ruby,一种形象地称谓是“duck-typing”多态。关于多态的更完整的分类和介绍可以看这里。通过动态语言机制实现的runtimeunbound,存在的性能和类型安全问题。但当我们将GP中的concept技术推广到runtime时会发现,rungimeunbound可以拥有同OOP动多态相当的效率和类型安全性,但却具有更大的灵活性和更丰富的特性。关于这方面,我已经写过一篇文章,大致描述了一种实现runtimeconcept的途径(本文的附录里,我也给出了这种runtimeconcept实现的改进)。RuntimeConceptRuntimeconcept的实现并不会很复杂,基本上可以沿用OOP中的“虚表”技术,并且可以更加简单。真正复杂的部分是如何在语言层面表达出这种runtimeGP,而不对已有的staticGP和其他语言特性造成干扰。在这里,我首先建立一个基本的方案,然后通过一些案例对其进行检验,在发现问题后再做调整。考虑到runtimeconcept本身也是concept,那么沿用staticconcept的定义形式是不会有问题的:conceptmyconcept{T©(T&lhs,Tconst&rhs);voidT::fun();...}具体的concept定义和使用规则,可以参考C++0x的concept提案或这篇文章,以及这篇文章。另一方面,我们可以通过concept_map将符合一个concept的类型绑定到该concept之上:concept_mapmyconcept{}相关内容也可参考上述文件。有了concept之后,我们便可以用它们约束一个模板:templatevoidmyfun(Tconst&val);//函数模板templateclassX//类模板{...};到此为止,runtimeconcept同staticconcept还是同一个事物。它们真正的分离在于使用。对于staticconcept应用,我们使用一个具体的类型在实例化(特化)一个模板:Xx1;//实例化一个类模板MyTypeobj1;myfun(obj1);//编译器推导obj1对象的类型实例化函数模板myfun(obj1);//函数模板的显式实例化现在,我们将允许一种非常规的做法,以使runtimeconcept成为可能:允许使用concept实例化一个模板,或定义一个对象。Xx2;myconcept*obj2=newmyconcept;myfun(obj2);//此处,编译器将会生成runtime版本的myfun这里的含义非常明确:对于x2,接受任何符合myconcept的类型的对象。obj2是一个“动态对象”(这里将runtimeconcept引入的那种不知道真实类型,但符合某个concept的对象称为“动态对象”。而类型明确已知的对象成为“静态对象”),符合myconcept要求。至于实际的类型,随便,只要符合myconcept就行。这种情形非常类似于传统动多态的interface。但是,它们有着根本的差异。interface是一个具体的类型,并且要求类型通过继承这种形式实现这个接口。而concept则不是一种类型,而是一种“泛型”——具备某种特征的类型的抽象(或集合),不需要在类型创建时立刻与接口绑定。与concept的绑定(concept_map)可以发生在任何时候。于是,runtimeconcept实际上成为了一种非侵入的接口。相比interface这种侵入型的接口,更加灵活便捷。通过这样一种做法,我们便可以获得一种能够在运行时工作的GP系统。在此基础上,为了便于后续案例展开,进一步引入一些有用的特性:一个concept的assosiatetype被视为一个concept。一个concept的指针/引用(concept_id*/concept_id&,含义是指向一个符合concept_id的动态对象,其实际类型未知),都被视作concept。一个类模板用concept实例化后,逻辑上也是一个concept。动态对象的创建。如果需要在栈上创建动态对象,那么可以使用语法:concept_idtype_idobj_id;这里concept_id是concept名,type_id是具体的类型名,obj_id是对象名称。这样,便在栈上创建了一个符合concept_id的动态对象,其实际类型是type_id。如果需要在堆上创建动态对象,那么可以用语法:concept_id*obj_id=newconcept_id;这实际上可以看作“concept指针/引用”。concept推导(编译期)。对于表达式concept_idobj_id=Exp,其中Exp是一个表达式,如果表达式Exp的类型是具体的类型,那么obj_id代表了一个静态对象,其类型为Exp的类型。如果表达式Exp的类型是concept,那么obj_id是一个动态对象,其类型为Exp所代表的concept。那么如何确定Exp是具体类型还是concept?可以使用这么一个规则:如果Exp中涉及的对象,比如函数的实参、表达式的操作数等等,只要有一个是动态对象(类型是concept),那么Exp的类型就是concept;反之,如果所有涉及的对象都是静态对象(类型为具体的类型),那么Exp的类型为相应的具体类型。同样的规则适用于concept*或concept&。concept转换。类似在类的继承结构上执行转换。refinedconcept可以隐式地转换成baseconcept,反过来必须显式地进行,并且通过concept_cast操作符执行。兄弟concept之间也必须通过concept_cast转换。基于concept的重载,也可以在runtime时执行,实现泛化的dynamic-dispatch操作。下面,就开始第一个案例。案例:升级的坦克假设我们做一个游戏,主题是开坦克打仗。按游戏的惯例,消灭敌人可以得到积分,积分到一定数量,便可以升级。为了简便起见,我们只考虑对主炮升级。第一级的主炮是90mm的;第二级的主炮升级到120mm。主炮分两种,一种只能发射穿甲弹,另一种只能发射高爆弹。因此,坦克也分为两种:能打穿甲弹的和能打高爆弹的。为了使代码容易开发和维护,我们考虑采用模块化的方式:开发一个坦克的框架,然后通过更换不同的主炮,实现不同种类的坦克和升级://一些基本的concept定义//炮弹头conceptconceptWarheads{doubleexplode(TargetTypett);//炮弹爆炸,返回杀伤率。不同弹头,对不同类型目标杀伤率不一样。}//炮弹concept,我们关心的当然是弹头,所以用Warheads定义一个associatetypeconceptRounds{WarheadsWH;...}//主炮conceptconceptCannons{RoundsR;voidT::load(R&r);//装填炮弹,load之后炮弹会存放在炮膛里,不能再load,除非把炮弹打出去R::WHT::fire();//开炮,返回弹头。发射后炮膛变空,可以再load}//类型和模板定义//坦克类模板TemplateclassTank{...public:voidload(typenamC::R&r){m_cannon.load(r);}typenameC::R::WHfire(){returnm_cannon.fire();}private:Cm_cannon;};//主炮类模板TemplateclassCannon{public:typedefRdR;voidload(R&r){...}typenameR::WHfire(){...}}templateconcept_mapCannons{}//炮弹类模板TemplateclassRound{public:typedefWWH;staticconstintcaliber=W::caliber;Wshoot(){...}...};templateconcept_map{}//弹头类模板,通过traits把各类弹头的不同行为弹头的代码框架分离,使类型“可组装”conceptWH_Traits{returnT::exploed(int,TargetType,double,Material);}TemplateclassWarhead{public:conststaticintcaliber=c;doubleexplode(TargetTypett){returnwht::exploed(c,tt,...);}...};templateconcept_map{}//弹头traitsstructKE_WHTraits{staticdoubleexploed(intcaliber,TargetTypett,doubleweight,Materialm){...}};concept_map{}structHE_WHTraits{staticdoubleexploed(intcaliber,TargetTypett,doubleweight,Materialm){...}};concept_map{}//定义各类弹头typedefWarheadWH_KE_90;typedefWarheadWH_KE_120;typedefWarheadWH_HE_90;typedefWarheadWH_HE_120;//定义各类炮弹ty