一种高效的动态类型转换的实现方法

文档序号:6368636阅读:191来源:国知局

专利名称::一种高效的动态类型转换的实现方法
技术领域
:本发明属于程序编译
技术领域
,具体涉及C++语言中的动态类型转换的一种高效的实现方式,可用于编译器等设备中,用于产生高效的可执行程序,提升程序的运行时性倉泛。
背景技术
:编译是利用编译程序(即编译器)将源语言(如C++等)编写的源程序翻译转换为目标程序(即可以在机器上直接执行的二进制程序)的过程,它是现代软件开发的最重要基石之C++语言通过提供运行时类型信息(RTTI)来实现其它动态语言(如JavaScript、Python等)提供的动态类型特性,它允许一个变量在执行过程中被解释为多种类型。C++程序变量通过在运行时查找RTTI信息来获知自身的类型信息,验证是否能够被解释为目标类型,从而确保其类型安全。具体实现这一功能的是动态类型转换dynamic_cast操作符。C++的类型包括基本类型和自定义的类(即class),动态类型转换通常是类之间的转换。C++语言中类之间的关系包括公有public、保护protected和私有private三种继承关系,另外还有一种独立的virtual虚继承关系。如果类A直接继承自类B(记作A<B,或者B>A),则称B是类A的直接父类;对于类A和B,如果存在A到B的一个继承链A<Cl<...<Cn<B(或者称作B到A的一个派生链B>Cn>..·>Cl>A),则称B为A的父类(记作A<<B或B>>A),称A是B的子类,特别地,A的直接父类也是A的父类。在编译后的二进制程序中,同一个类的每个实例(即对象)都具有相同的内存布局。通常,每个类的对象都是由该类自有的数据成员,以及其直接父类对应的子对象构成。另外,虚继承在对象的内存布局中比较特殊。对于类A而言,如果有两个父类C和D都是直接虚继承自类B,则在A对象中只有一个B子对象,且同时被C和D子对象包含。换言之,如果A到其父类B存在η条非虚继承链和m(m>O)条虚继承链,则A对象中存在n+1个B子对象,其中I个B子对象被所有m条虚继承链上的子对象包含,其余每个B子对象都分别对应于一条非虚继承链。对于任何一个类Complete而言,及其任何一个Source类子对象,Source到Complete存在若干派生链。对其中每个派生链Source>BI>..·>Bn>Complete,截取从Source开始的最长公有继承,构成一个派生链Source>BI>...>Bk(即该链上都是公有派生,但Bk>Bk+1不是公有派生),称该截取的派生链为该Source子对象在Complete中的一个公有派生链。Dynamic_cast操作符的具体使用方式是dynamic_cast〈TargetType>(src),其中TargetType是类的指针、类的引用或者void*。当TargetType是类的指针时,src必须是一个对象的指针,返回的也是某个对象的指针;当TargetType是类的引用时,src必须是一个对象的引用,返回某个对象的引用。对于dynamic_cast而言,TargetType是类的指针或者类的引用,其转换规则都是类似的,只有在运行时的类型检查失败后返回值上有些差异。如果TargetType是类的引用,dynamic_cast失败时会抛出一个异常;如果TargetType是类的指针,dynamic_cast失败时返回NULL空指针。因而,为了叙述方便,本发明中假定TargetType是类的指针这种形式,而对于类的引用,本发明的实施方式仍然适用。即假设我们考察的dynamic_cast操作具有形式dynamic_cast〈Target*>(src),其中,src是指向一个Source类型的对象的指针。同时,假定src对象的最大派生对象是cmplt(即src对象包含于cmplt对象中,但是没有其他对象包含cmplt),且cmplt的类型是Complete。动态类型转换成功与否,依赖于源对象src的类型Source、目标类型Target、以及源对象src的最大派生对象cmplt的类型Complete这三个类型(即class)之间的关系。根据C++标准(参见TheC++Standard,IS0/IEC14882:2003)的规定,动态类型转换的规则如下I.如果Source与Target相同,贝u直接返回源对象src;2.如果源对象src是空指针NULL,则返回NULL;3.如果Target是Source的父类(上行转换),且若源对象src中有且只有一个Target类子对象tgt,并且tgt的某一条公有派生链包含src,则返回tgt。否则,返回NULL。4.如果不是前面3种情形,则Source类型必须是多态类型(即其中有虚函数)。5.如果转换目标Target*是void*,则返回源对象src的最大派生对象cmplt。6.如果不是前面几种情形,则动态类型转换必须在运行时进行如下类型检查a)如果Source是Target的某个父类(下行转换),且若源对象src的所有派生链中只包含一个Target类对象tgt,并且src的某一条公有派生链包含tgt,则返回tgt。b)如果Source与Target之间不存在父/子类关系(交叉转换),且若源对象src的某条公有派生链包含其最大派生对象cmplt,并且cmplt对象中有且只有一个Target子对象tgt,且tgt的某条公有派生链也包含cmplt,贝U返回tgt。c)否则,转换失败,返回NULL空指针。在编译时,前3条规则的动态类型转换可以直接由编译器在编译时完成,不需要运行时代码进行额外操作。因而,通常的动态类型转换的实现方案只需关注多态类型向void*(规则5)的转换、下行转换(规则6a)和交叉转换(规则6b)的运行时实现方案。现有的dynamic_cast实现,如微软的VisualC++编译器(简称VC)和GNUC++编译器(GCC)所采纳的实现方案,都面临着严重的性能问题。多态类型(见规则4)的对象通常前四个字节保存了一个虚表指针vfptr,而且每个对象通常与其第一个非虚多态直接父类共享同一个虚表指针vfptr。vfptr指向了该对象的虚函数表vftable,该表中保存了对象的虚函数指针,而在该表的底部(即地址为vfpt-4的内存位置)保存了对象的运行时类型信息RTTI。VC和GCC都是通过在运行时查找src对象的RTTI,并与Source类型和Target类型的类型信息typeinfo进行匹配。图I是一个C++不例程序的代码简图。图2展不的是附图I所不代码经过GCC编译之后,其运行时类型信息RTTI示例。在代码运行时,通过src对象的vfptr指针得到其RTTI指针,然后根据RTTI指针得到Complete类(即图中的类A)的类型信息typeinfo,该类型信息包含该类的名字、该类的直接父类数目、该类的所有直接父类的类型信息typeinfo,各直接父类的继承关系等。在GCC编译过的程序中,运行时代码通过递归地搜索typeinfo,找到Complete的所有父类信息,并匹配Source和Target的类型信息,进而判断是否能够转换成功。这个实现方案中,搜索typeinfo还原Complete的继承信息是一个性能瓶颈,其运行时耗时与父类对象数目成正比。因而,在类的继承层次比较复杂时(比如有四级父类时),动态类型转换在运行时花费的时间将是普通的乘法运算的几百倍。
发明内容本发明的目的在于解决现有技术中的问题,提供一种动态类型转换的实现方案,可提高动态类型转换的运行时性能,进而提升二进制程序的整体运行时性能。本发明的动态类型转换的实现方法,其步骤包括I)编译器对源代码进行解析,根据解析结果在编译过程中构建类继承图和首基类图;并根据所述首基类图,构建首基类链;2)对于每个类,编译器根据其类继承图构建交叉转换域;并根据首基类链,将该类的父类中不属于其交叉转换域的类划分为若干下行转换域;3)对于每个类,编译器为其创建一thread表,并将其存储到编译后的二进制程序中;对于与该类的父类集合的交集非空的首基类链,该thread表为其保留一表项;4)编译器在每个类所有的传统的虚函数表中,分别增加一指向所述thread表的指针、一处理非公有继承的private表指针和一转换域表;对每个交叉/下行转换域,该转换域表为其保留一表项;将该扩充后的虚函数表存储到编译后的二进制程序中;5)对于每一个dynamiC_Cast语句,编译器生成运行时代码,该运行时代码通过查找源对象的thread表、private表以及转换域表完成动态类型转换。下面对本发明的技术原理做进一步的说明。本发明的基本思想是通过采用一种新颖的编码方案,压缩缓存所有可能成功的动态类型转换信息;在运行时通过查找一个短小的数组,进而快速完成动态类型转换。该思想是基于如下事实或原则首先,对于动态类型转换dynamic_cast〈Target*>(src),如果在编译时已知src的最大派生对象cmplt的类型Complete,以及src到cmplt的所有派生链信息,则在编译时可以直接完成动态类型转换,而不需要运行时代码的额外工作。其次,每个对象src关联的最大派生对象cmplt的类型Complete,以及src到cmplt的所有派生链,都与该src对象的vtable是息息相关的,因为编译器会给每个Complete类的所有起始位置不同的多态子对象(本发明关注的动态类型转换其源对象src一定是多态对象)构建一个不同的vtable。因而,在编译时,可以为每个vtable附加额外信息,记录所有可能成功的动态类型转换的相关信息。在运行时,可以直接查询vtable附着的这些信息,进而快速地完成动态类型转换。下面说明本发明方法中相关概念的含义。类继承图一个有向图G(Complete,V,El,fV,fEV,fEP),其中Complete是该继承图所针对的具体类。(I)V是顶点的集合,分别代表源代码中所有的类;(2)E1是边的集合,边A—B属于E1,当且仅当B是A的一个直接父类;(3)fVV—bool是一个映射,描述了顶点是否是多态类。对于顶点A,fV(A)=true当且仅当A是多态类(即包含虚函数);(4)fEV:E1—bool是一个映射,描述了边是不是虚继承。对于边A—B,fEV(A—B)=true当且仅当A虚继承自B;(5)fEPEl-bool也是一个映射,描述了边是不是公有继承。对于边A—B,fEP(A—B)=true当且仅当A公有继承自B。整个程序的类继承图,可以通过解析程序的源代码来构建。对于图I所示代码,其类继承图即为图5所示。类布局图类布局图可以由类继承图得到,也可以先构建类布局图,再得到类继承图。一个有向图LG(Complete,V,E2,fT,fEV,fEP),其中Complete是该布局图所针对的具体类。(l)V’是顶点集合,分别代表Complete类的所有子对象;(2)E2是边的集合,边objA—objB属于E2,当且仅当objB子对象直接包含于objA子对象中;(3)fTV,—ClassType是一个映射,对于顶点objA,fT(objA)返回objA子对象的类;(4)fEVE2—bool是一个映射,描述了边是不是虚继承;对于边objA—objB,fEV(objA—objB)=true当且仅当objA虚继承自objB;(5)fEPE2—bool也是一个映射,描述了边是不是公有继承;对于边objA—objB,fEP(objA—objB)=true当且仅当objA公有继承自objB。同样,通过解析程序的源代码,对每个类Complete,都可以构建其布局图。对于图I所示代码,其类布局图即为图4所示。首基类图一个有向图FBG(V,E3),其中(I)V是顶点集合,分别代表源代码中的所有的类;(2)E3是边的集合,边A—B属于E3,当且仅当B是A的第一个非虚的多态的直接父类。首基类图同样可以通过解析源代码来构建,且首基类图是一个森林。对于首基类图中任何一条边A—B,子类对象A与父类对象B共享vfptr。对于图I所示代码,其首基类图即为图6所示。首基类链去除首基类图的若干边,可以得到若干个首基类链thread。具体来说,对于首基类图中任何一条边BI—B2,如果它是公有继承,且对于任何同时继承自BI和B2的类Complete而言,Complete类布局图中任何一个B2类子对象都恰好包含于一个BI类子对象中,则保留该边,否则去除该边。这样,首基类图变换成一个新的森林。然后,对于森林中的每棵树,采用贪心法逐个截取其中最长链,最后得到若干互不相交的首基类链thread。对每个首基类链,它都具有性质⑴对于任何首基类链Bn—...—B2—BI,它与任何类Complete的父类集合的交集一定是Bm—..·—B2—BI(其中m<=η)这种形式。(2)如果动态类型转换dynamic_cast〈Bi*>(src)能够成功,其中src对象的最大派生对象类型为Complete,贝U对于所有满足i<=j<=m的j来说,dynamic_cast〈Bj*>(src)都能够成功,并且它们返回的指针指向相同位置。对于图I所示代码,其首基类链图即为图7所示。交叉转换域对于每个类Complete,根据其继承图G,所有可访问的且唯一的父类节点构成其交叉转换域crossDom。首先,标识Complete节点为可访问的且唯一的。然后,自底向上逐个遍历Complete类继承图G中所有顶点V,标识其可访问性和唯一性。对于可访问性,如果存在公有继承w—V,且w是可访问的,则V是可访问的。而对于唯一性,(I)如果只存在一条边w—V指向V,且w是唯一的,则V是唯一的;(2)如果存在多条边指向V,且所有边都是虚继承,则V也是唯一的。(3)其它情形下,V都不是唯一的。最后,所有可访问的且唯一的顶点构成crοssDom。Complete类的交叉转换域具有如下性质(I)对于交叉转换域中的所有类Target,如果src的某条公有派生链包含Complete对象,则动态转换dynamic_cast〈Target*>(src)—定可以成功。(2)对于交叉转换域外的所有Target类,如果dynamic_cast<Target*>(src)成功,贝U—定是下行转换(注意前面已经说明了,运行时动态类型转换不关注上行转换)。下行转换域对于每个类Complete,其父类集合中,不属于交叉转换域的所有类,可以根据其所属的首基类链划分为若干下行转换域。具体来说,Complete的父类A和父类B属于同一个下行转换域,当且仅当A和B属于同一个首基类链。Complete类的下行转换域具有性质(I)对于任何首基类链Bn—.—B2—BI,假设它与Complete的父类集合的交集是Bm—·..—B2—BI,则BI,B2,...Bm全部属于Complete的交叉转换域,或者它们恰好构成Complete的某一个下行转换域。(2)对于Complete的任何下行转换域Bm—...—B2—BI以及任何子对象src,假设该域中包含于src的某一条公有派生链的最小类是Bk,则所有包含于src的某条公有派生链的类恰好是Bk,Bk+1,...,Bm。(3)对于Complete的任何下行转换域Bm—··.—B2—BI以及任何子对象src,假设所有包含于src的某条公有派生链的类是Bk,Bk+1,...,Bm,则dynamic—cast<Bj*>(src)能够成功当且仅当k<=j<=m,且src的所有公有派生链中只包含一个Bk对象。本发明的方案在运行时不需要对Complete类的布局图进行递归搜索,只需要对Complete的thread表进行查找,然后直接读取相关的表项完成动态类型转换。而thread表中的表项数目一般很小,比如在著名的大型的C++项目GoogleChrome浏览器中,其thread数目小于16。而且,thread表中的表项是按序放置的。因而,可以通过二分法查找该thread表,只需要logl6=4次查找即可。而传统的实现方案,需要搜索整个布局图,且布局图中的节点是无序的,因而其查找次数是与节点数目成正比的。更严重的是,如果继承图中的继承层次比较多,节点数目比较多的话,其查找性能更低。本发明具有如下独特优势I)本发明的方法相比传统的方法,其运行时性能有巨大提升。本发明的方法在实际测试中只花费十几个乘法运算时间,相比传统的方法需要花费几百个乘法运算时间(甚至更多),其性能提升10几倍;2)本发明的方法运行时所需时间是基本固定的,不会随目标程序的继承层次变化而发生较大变化。传统方法在继承层次变的复杂的时候,动态类型转换所需花费时间会明显增长。这一特性使得本方案适用于所有的对实时性要求很高的程序,例如可以用于嵌入式设备中。图I是一个C++示例程序的代码简图。图2是GCC编译得到的类A的实例对象的运行时内存布局以及RTTI信息图。图3是本发明的动态类型转换的实现方法的步骤流程图。图4是图I中类A和类H的布局图。图5是图I中类A和类H的继承图。图6是图I中示例程序的首基类图。图7是图I中示例程序的首基类链图。图8是图I中类A的交叉转换域。图9是图I中类A的各个下行转换域。图10是本发明提出的动态类型转换实现方案在运行时的内存布局示意图。图11是图I的示例程序在本发明方案下的实施结果图。图12是另外一个示例程序的类布局图。图13是针对图11所示代码进行动态类型转换时所耗时间的度量结果示意图。具体实施例方式下面通过实施例并结合附图,对本发明作进一步的说明。本发明是一种面向C++语言的高效的动态类型转换方法。图I是一段C++示例代码,该代码片段描述了示例程序中所有的类(共19个)以及其相互之间的继承关系。该代码片段是一个简化版,省略了各个类的内部成员函数和成员变量信息。其完整版代码中,每个类各包含一个虚函数以及一个整型成员变量,例如类D2包含一个虚函数voidfooD2O及一个整型成员变量intintD2。图2展示了该示例代码经GCC编译之后,一个A类对象在运行时的内存布局图。其中用于动态类型转换的RTTI信息就是类的typeinfo。C++标准中规定了typeinfo必须包含类的名字,其余实现细节由各编译器自由选择。GCC中存在三种typeinfo,分别对应于没有父类的类、只有一个非虚父类的类、其他类。每个类A的typeinfo中包含了A的所有直接父类的typeinfo信息,以及所有直接父类与A的继承关系,及它们之间的相互偏移。运行时的动态类型转换依赖于该typeinfo信息。简单来说,对于动态类型转换dynamic_cast〈Target*>(src),运行时代码会搜索src对象的最大派生对象的typeinfo,然后递归地搜索所有父类的typeinfo,并与Source和Target的typeinfo进行比较,最终计算出转换结果。这种实现方案的运行时性能很差,尤其对于继承结构比较复杂的类更是如此。本实施例的动态类型转换方法,适用于类的指针或类的引用,其主要步骤如图3所示,详细说明如下I)根据源代码解析结果,构建各个Complete类的布局图LG。图4是该示例代码中类A和类H的类布局图。以类A为例,A非虚继承自Al和A2,因而,A由一个Al和一个A2子对象构成。对应地,在类布局图中,有两条边A—Al和A—A2。同理,类A3非虚继承自C以及虚继承自A4,从而A3中包含一个C子对象,而A4子对象则是“虚”包含于A3,即该A4子对象被所有虚继承自A4且包含于完整的A对象的子对象共享。依此类推,可以构建A的类布局图。常见的编译器都已经实现了类布局图的构建。2)根据源代码解析结果,构建类继承图G。图5是该示例代码中类A和类H的类继承图。在类布局图中,每个类可能对应多个节点(即存在多个同类型的子对象)。而在类继承图中,每个类对应一个节点。同样,通过逐个扫描类的继承关系,可以很容易构建出类继承图。例如,A继承自Al和A2,则在图中存在边A—Al和A—A2。类继承图可以由类布局图得到;也可以根据源代码的解析结果直接构建类继承图,而不进行步骤I)。3)根据源代码解析结果,构建首基类图FBG。图6是示例代码的首基类图。每个类对应于该图中一个节点,边NI—N2存在,当且仅当NI类的第一个非虚多态父类是N2。同样,通过逐个扫描类的继承关系,可以很容易构建出首基类图。例如,A的第一个非虚多态父类是Al,则在图中存在边A—Al。基于此首基类图,给每个节点(即每个类)分配一个标识符。分配方式如下采用先根序遍历每棵树,按照遍历顺序依次给每个节点赋予标识符。例如,对于图6的第一棵树,米用先根序遍历C2-C1-C-A3-A1-B-A2-G,可依次给各节点分配标识符1,2,...,9。其它依此类推。此方案分配的标识符必定满足性质任何类M的标识符大于其首基类N的标识符。4)根据首基类图FBG,构建首基类链。图7是示例代码的首基类链。基于首基类图,可以构建首基类链。具体步骤是(I)对于首基类图中的每条边,如果它是非公有继承则删除该边。比如图6中的边A3->C和A2—B都是非公有继承,从而被删除。(2)对于首基类图中的每条边NI—N2,如果在原始程序中,存在某个类M同时继承自NI和N2,但是M中某个N2子对象的直接派生对象不是NI,则删除该边。例如对于图6中的边B—C,原始程序中类A同时继承自B和C,但是A的一个C类子对象A::A1::A3::C其直接父对象是一个A3子对象,而不是一个B子对象,从而该边被删除。(3)在余下的图中,采用贪心算法,对于图中每棵树,逐个取出其中的最长链,得到若干首基类链。例如图6中的首基类图经过上述(I)和(2)步骤之后,边A3->C、A2—B以及B—C被删除,余下的图中,经过贪心算法,可以得到图7所示的首基类链图。基于首基类链图,给每个首基类链分配一个编号,例如类C所在的首基类链被赋予编号1,类B所在的首基类链被赋予编号3。从而,每个类被编码为(cid,tid),其中cid是该类的标识符,tid是该类所属的首基类链编号。例如类A的编码为出,2)。该编码方案下,每个类的编码都是唯一且不同的。5)对每个类Complete,根据其类继承图构建交叉转换域。图8是类A的交叉转换域。当且仅当顶点(类)V是可访问的(即V是A的一个公有父类),且V是唯一的(即在A对象中只存在唯一一个V子对象)时,V属于A的交叉转换域。因而,A的交叉转换域的具体构建方式是首先,标识A为可访问的以及唯一的。然后,自底向上逐个遍历A类继承图G中所有顶点V,判定各顶点(类)的可访问性和唯一性。最后,所有可访问且唯一的顶点构成交叉转换域。对于节点V,其可访问性通过如下方式判定如果存在某条边w—V是公有继承,且W是可访问的,则V也是可访问的。由A是可访问的,可得出Al,A2,A3和A4都是可访问的,其它类都不可访问。对于节点V,其唯一性可以通过如下原则判定1)如果只存在一条指向V的边(假设为W—V),且w是唯一的,则V是唯一的。2)如果存在多条指向V的边,但是所有这些边都是虚继承,则V是唯一的。3)其他情形下,V都不是唯一的。由A是唯一的,可得出Al,A2,A3,A4,B,E,El,E2都是唯一的,其他都不是唯一的。这一结论与类的布局图相吻合。最后,得到A的交叉转换域A,Al,A2,A3,A4。6)对每个类Complete,对其父类中,所有不属于其交叉转换域的类,根据该类所属的首基类链,将其划分为若干下行转换域。图9是该示例代码中类A的所有下行转换域。对于A的父类集合中不属于交叉转换域(即A、A1、A2、A3、A4)的所有类,根据各个类所属的首基类链,可以得到若干下行转换域。例如类B属于首基类链3,它构成类A的一个下行转换域。而类D和Dl属于首基类链6,它们构成类A的另一个下行转换域。分别给类A的交叉转换域和所有下行转换域(这个示例代码中共有6个下行转换域)编号。其中交叉转换域编号为0(图8中标识为domO),其它下行转换域依次编号为1,2,···,6(图9中标识为doml,dom2,...,dom6)。7)每个类Complete,仓ij建一个thread表,存储到编译后的二进制程序中。对于Complete中每个虚函数表vtable,在其反方向额外附加一个threadPtr指针,一个privatePtr指针、及一个转换域表。对于Complete中每个虚函数表vtable(存储在编译后的二进制程序中),假设共享该vtable的所有子对象是ptr(Sourcel),.··,ptr(SourceN),其中类型Sourcel<SourceN,而ptr是子对象起始位置的内存地址。传统方案中已经在vtable反方向上地址为vfptr-4和vfptr-8的内存位置上已经分别存储了Complete类的RTTI指针和当前子对象到Complete对象的偏移offset,本发明方案在该虚函数表vtable的反方向额外附加一个threadPtr指针(地址为vfptr-12),一个privatePtr指针(地址为vfptr-16)、及一个转换域表(地址为vfptr-20)。图10是本发明提出的高效动态类型转换实现方案在运行时的对象内存布局示意图。对于类Complete的每个vftable,在其原有的RTTI指针和偏移offset信息之外,另外增加了threadPtr指针、privatePtr指针和一个转换域表。下面分别予以说明a)threadPtr指针该指针指向Complete类的thread表,该表中为每个与Complete父类集合交集非空的首基类链保存一个表项,分别记录该首基类链的编号tid,该首基类链中包含于Complete父类集合的类的最大标识符maxTgtCid,该首基类链对应的Complete类的转换域编号domid,该首基类链到该转换域的偏移tgtOffset。其中maxTgtCid和domid在前面构建首基类图和转换域时已经得到,而tgtOffset则根据domid可以快速计算。如果domid为0(即该首基类链包含于交叉转换域中),则tgtOffset为该首基类链中的子对象(只有一个)到Complete对象的偏移;否则tgtOffset=O。在实际存储中,考虑到运行时查找性能问题,thread表中的表项分两段存储,其中正向的表只存储首基类链的编号tid,反向的表中存储对应的首基类链的maxTgtCid,domid和tgtOffset。以类A作为Complete类进行示例(如图11所示),示例代码中与A的父类集合交集非空的首基类链包括除编号为5和10之外的所有9条首基类链。因而,在A的thread表中,包含9个表项,分别按照首基类链的编号依次存储。以首基类链4为例,它与A的父类集合交集为{A2},其中最大的类标识符就是A2自身的标识符8,故maxTgtCid=8;而且该首基类链属于交叉转换域,故domid=O;而该首基类链上的A2子对象到交叉转换域的根节点(即类A)的偏移即是A2相对于A对象的偏移44,故tgtOffset=44。以另外一个首基类链6为例,它包含类{D1、D、F},它与A的父类集合交集为{D1、D},其中最大的类标识符就是D的标识符11,故maxTgtCid=11;而且该首基类链属于下行转换域3,故domid=3;由于它不属于交叉转换域,故tgtOffset=O。b)转换域表在类Complete的每个vftable中另外增加的转换域表,为Complete的每个转换域domid保留一个表项,表项中包含一个minTgtCid和一个srcOffset。假设共享该vftable的所有子对象是ptr(Sourcel),...,ptr(SourceN)。对于编号为domid的域,这N个子对象到domid域的偏移即是srcOffset;而能从这N个子对象中某个对象成功转换的若干类Target(属于domid域)中,其对应的的cid的最小值即为minTgtCid。minTgtCid可以通过考察子对象ptr(Sourcel),...,ptr(SourceN)的公有派生链与domid域的交集来计算。以类A作为Complete示例(如图11所示),类A包含一个交叉转换域和6个下行转换域。因而,每个vftable的上面的转换域表中包含7个表项。首先以子对象A::A1::A3::C::C1::C2为例,记该对象为ptr(C2),共享其vftable的所有对象是{ptr(C2),ptr(Cl),ptr(C),ptr(A3),ptr(Al),ptr(A)},这些子对象的公有派生链集合是{C2—Cl—C,A3—Al—A}。该公有派生链集合只与转换域O以及转换域I有交集,故相应的转换域表中dom2.....dom6的表项minTgtCid为无穷大INF(实际取值255,因为实际程序中不可能出现长度超过255的首基类链),对应的srcOffset不存在N/A(实际取值O即可)。而对于与公有派生链集合交集非空的转换域O和I来说。其中转换域O是交叉转换域,故minTgtCid为交叉转换域中所有类(即A、A1、A2、A3、A4)的最小标识符4,而srcOffset则为共享该vftable的子对象到A类的偏移,即O。对于转换域I来说,共享该vftable的子对象的公有派生链与该转换域的交集是{C2,C1,C},这几个类都可以从共享该vftable的子对象中某个对象转换而得,故minTgtCid为C2、Cl和C中的最小标识符,即I;而该转换域不为交叉转换域,故srcOffset则为O。再以子对象A::C::C1::C2为例(这个C对象是A的一个虚继承的子对象),记该子对象为obj(C2),共享其vftable的所有子对象是{obj(C2),obj(Cl),obj(C)},这些子对象的公有派生链集合是{C2—Cl—C},它只与转换域I有交集。故对于其余的I个交叉转换域和5个下行转换域,其minTgtCid=INF,而srcOffset=N/A。对于转换域I来说,共享该vftable的子对象的公有派生链与该转换域的交集是{C2,Cl,C},这几个类都可以从共享该vftable的子对象中某个对象转换而得,故minTgtCid为C2、Cl和C中的最小标识符,即I;而该转换域不为交叉转换域,故srcOffset则为O。c)privatePtr指针在类Complete的每个vftable中还增加了一个privatePtr指针。如果共享该vftable的所有子对象之间不存在非公有继承,则该指针为空NULL。否则,该指针指向一个private表。该privateTable表为Complete类的所有转换域domid保留一个表项,记录共享该vftable的所有对象中,能够成功转换到domid转换域中某个类的对象的标识符的最小值minSrcCid。如果domid转换域的minTgtCid=INF,则相应的minSrcCid=INF。否贝1J,考察共享该vftable的所有子对象,计算其中能成功转换到domid域中某个类的对象的最新标识符。以类A作为Complete示例(如图11所示),类A包含一个交叉转换域和6个下行转换域。因而,每个非空privatePtr所指向的privateTable包含7个表项。首先以子对象A::A1::A3::C::C1::C2为例,记该对象为ptr(C2),共享其vftable的所有对象是{ptr(C2),ptr(Cl),ptr(C),ptr(A3),ptr(Al),ptr(A)},这几个子对象之间存在非公有继承A3—C,因而privatePtr非空。其指向的privateTable中,由于编号为2,3,4,5,6的转换域中minTgtCid=INF,故相应的minSrcCid=INF。而对于转换域O,子对象ptr(C2),ptr(Cl),ptr(C),ptr(A3),ptr(Al),ptr(A)中有且仅有{ptr(A3),ptr(Al),ptr(A)}能够转换到该转换域中某个类上,因而minSrcCid为A3、Al和A中最小标识符4。而对于转换域I来说,子对象ptr(C2),ptr(Cl),ptr(C),ptr(A3),ptr(Al),ptr(A)中有且仅有{ptr(C2),ptr(Cl),ptr(C)}能够转换到该转换域中某个类上,因而minSrcCid为C2、Cl和C中最小标识符I。再以子对象A::C::C1::C2为例(这个C对象是A的一个虚继承的子对象),记该子对象为obj(C2),共享其vftable的所有子对象是{obj(C2),obj(Cl),obj(C)},其中不存在非公有继承,故privatePtr=NULL。图11就是本发明针对示例代码的具体实施结果,其展示了两个子对象A::A1::A3::C::C1::C2和A::C::C1::C2所对应的vftable被扩充的结果,以及A类的thread表信息。相关的构建步骤在对图10描述时已经进行作出了说明,这里不再赘述。8)通过编译器为目标程序提供一段运行时代码,以实现dynamiC_Cast功能,完成运行时查找thread表、转换域表等,进而完成动态类型转换。该代码被封装为一个函数,最终链接到编译后的二进制程序中。该函数接受三个参数源对象src指针,源对象类型Source的编码(srcCid,srcTid),以及目标类型Target的编码(tgtCid,tgtTid)。该函数中代码的逻辑如下a)如果src指针为控制针NULL,返回NULL。b)根据src指针,得到其vfptr,进而得到其vtable。c)如果Target是void*,则从vfptr-8位置取出offset,并返回void*指针tgt,其中tgt=(char*)src+offseto否则d)在vfptr-12位置读取threadPtr,找到其指向的thread表。e)在该thread表中查找Target类型的对应的tgtTid。如果该表中没有找到tgtTid,则返回NULL。否则f)从该thread表中读取tgtTid表项中存储的maxTgtCid,如果tgtCid>maxTgtCid,则返回NULL。否则g)从该thread表中读取tgtTid表项中存储的domid和tgtOffset。h)从vfptr-20位置找到转换域表,读取其domid表项,得到minTgtCid和srcOffset。如果tgtCid<minTgtCid,则返回NULL。否则i)从vfptr-16位置取出privatePtr,i.如果privatePtr为NULL,则转换成功,并返回Target*指针tgt,其中tgt=(char*)src+src0ffset+tgt0ffseto否则ii找到privatePtr指向的privateTable表,读取其第domid表项,得到minSrcCid。如果srcCid<minSrcCid,则返回NULL。否则iii.转换成功,并返回Target*指针tgt,其中tgt=(char*)src+srcOffset+tgtOffsetο本发明通过采用一种新颖的编码方案,压缩缓存所有可能成功的动态类型转换信息。在运行时通过查找一个短小的数组,进而快速完成动态类型转换。本发明相比现有编译器的实现方案性能上有极大提升,经过试验测试,通常能够提升十几倍的性能。图12是测试所用示例代码的类的布局图,图13是三个常用编译器GCC、VC、IntelCC(由于相关商标限制,图中分别以编译器1、2、3代替这三个,其相对顺序无关)以及本发明的方案,针对图12所示代码,进行动态类型转换dynamic_cast〈Bl*>(src)所需要消耗的时间的度量,其中src类型为Ai,src的最大派生对象类型为Ci,而i则是图中横轴0,1,2,3,4,代表了Complete类(即CO、Cl.....C4)的继承层次。可以看到,本发明的方案的运行时性能是传统编译器的十几倍,而在继承层次较为复杂时(比如四层继承时),本发明提升的性能更多。最后应说明的是以上实施例仅用以说明本发明的技术方案而非对其进行限制,本领域的普通技术人员可以对本发明的技术方案进行修改或者等同替换,而不脱离本发明技术方案的精神和范围,本发明的保护范围应以权利要求所述为准。权利要求1.一种高效动态类型转换的实现方法,其步骤包括1)编译器对源代码进行解析,根据解析结果在编译过程中构建类继承图和首基类图;井根据所述首基类图,构建首基类链;2)对于每个类,编译器根据其类继承图构建交叉转换域;并根据首基类链,将该类的父类中不属于其交叉转换域的类划分为若干下行转换域;3)对于每个类,编译器为其创建一thread表,并将其存储到编译后的ニ进制程序中;对于与该类的父类集合的交集非空的首基类链,该thread表为其保留ー表项;4)编译器在每个类的所有虚函数表中分别增加一指向所述thread表的指针、一处理非公有继承的private表指针和ー转换域表;对姆个交叉/下行转换域,该转换域表为其保留ー表项;将该扩充后的虚函数表存储到编译后的ニ进制程序中;5)对于姆ー个dynamic_cast语句,编译器生成运行时代码,通过查找源对象的thread表、private表以及转换域表完成动态类型转换。2.如权利要求I所述的方法,其特征在于,所述动态类型转换的目标类型是类的指针或类的引用。3.如权利要求I所述的方法,其特征在于,步骤I)所述构建首基类链的步骤包括1)对于首基类图中的每条边,如果其是非公有继承则删除该边;2)对于首基类图中的每条边NI—N2,如果在原始程序中,存在某个类M同时继承自NI和N2,但是M中某个N2子对象的直接派生对象不是NI,则删除该边;3)在余下的图中,采用贪心算法,对于图中每棵树,逐个取出其中的最长链,得到若干首基类链。4.如权利要求I所述的方法,其特征在于对于类A及其父类节点V,当且仅当节点V在A的类继承图中具有可访问性和唯一性时,节点V属于类A的交叉转换域。5.如权利要求4所述的方法,其特征在于,所述可访问性通过如下方式判定如果存在某条边W—V是公有继承,且W是可访问的,则V也是可访问的。6.如权利要求4所述的方法,其特征在于,所述唯一性通过如下原则判定1)如果只存在一条指向V的边,并假设为w—V,且w是唯一的,则V是唯一的;2)如果存在多条指向V的边,但是所有这些边都是虚继承,则V是唯一的。7.如权利要求I所述的方法,其特征在于步骤3)所述表项包括所述首基类链的编号、所述首基类链中包含于父类集合的类的最大标识符、所述首基类链对应的类的转换域编号以及所述首基类链到该转换域的偏移。8.如权利要求7所述的方法,其特征在于所述表项分两段存储,其中正向的表存储首基类链的编号,反向的表存储其它表项。9.如权利要求I所述的方法,其特征在于对于类A的一虚函数表,如果共享该虚函数表的所有子对象之间不存在非公有继承,则所述private表指针为空;否则,该指针指向一个private表,该private表为类A的所有转换域保留一个表项,记录能够成功转换到转换域中某个类的对象的标识符的最小值。10.如权利要求I所述的方法,其特征在于将步骤5)所述代码封装为ー个函数,链接到编译后的ニ进制程序中;该函数接受三个參数源对象指针、源对象类型编码以及目标类型编码。全文摘要本发明提供一种高效的动态类型转换的实现方法,其步骤包括编译器对源代码进行解析并构建类继承图、首基类图和首基类链;根据类继承图构建交叉转换域;根据首基类链划分下行转换域;为每个类创建thread表,并存储到编译后的二进制程序中;在每个类的虚函数表中增加指向所述thread表的指针、处理非公有继承的private表指针和转换域表,将该扩充后的虚函数表存储到编译后的二进制程序中;编译器为每一个dynamic_cast语句生成运行时代码,通过查找thread表、private表及转换域表完成动态类型转换。本发明用于编译器等设备中,可产生高效的可执行程序,提升程序的运行时性能。文档编号G06F9/44GK102707947SQ201210124840公开日2012年10月3日申请日期2012年4月25日优先权日2012年4月25日发明者丁羽,张利华,张超,徐先栋,李坤,段镭,赵晓濛,陈兆丰,韦韬,黎斯达申请人:北京大学
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1