本文思路来源于http://llvm.org/docs/HowToSetUpLLVMStyleRTTI.HTML,叙述有不同,望谅解,希望能从其他方面帮助大家了解C++语言的底层实现。
背景
在LLVM中默认禁止了C++的RTTI特性(RTTI特性的开关-fno-rtti
),主要是为了性能考虑(C++默认的RTTI特别冗余,会使得编译生成的文件大小增大,如果不使用RTTI反射机制的话,建议关闭。如果你对性能有极致要求的话,还可以考虑-fno-exceptions 禁用异常机制,但是关闭这两个特性的话,需要重新编译每一个依赖的软件,比如最常用的libstdc++,这个工作量就比较大了
)。但是为了方便考虑,LLVM中又使用了自己手撸(hand-rolled,手卷,感觉翻译成手撸可能比较贴合语义)的RTTI,这种特有的RTTI特性更有效而且更加灵活。当然,方便性的同时,也带来了更多的工作量。
在这里所有的工作都在LLVM 的UserManual中有体现,在深入研究之前,要求类的书写者(类的使用者,根本不会遇到如何实现LLVM的RTTI特性问题)了解“is-a”与“is-like-a”的关系(B继承至A,覆盖A的方法,B is-a A,新增方法,B is-like-a A)。
基础
本节介绍如何设置最基本的LLVM风格的RTTI(这足以满足99.9%的情况)。比如,我们的类继承关系如下:
class Shape {public: Shape() {} virtual double computeArea() = 0;};class Square : public Shape { double SIDeLength;public: Square(double S) : SIDeLength(S) {} double computeArea() overrIDe;};class Circle : public Shape { double Radius;public: Circle(double R) : Radius(R) {} double computeArea() overrIDe;};
按照以下4步进行修改,你就可以得到一个llvm形式的RTTI:
1.添加头文件
#include "llvm/Support/Casting.h"
2.在基类中,添加一个枚举类型,这个枚举类型存储所有继承至该类的每个类的值(其实就是枚举编码)
实现代码如下:
class Shape { public:+ /// discriminator for LLVM-style RTTI (dyn_cast<> et al.)+ enum ShapeKind {+ SK_Square,+ SK_Circle+ };+private:+ const ShapeKind Kind;+public:+ ShapeKind getKind() const { return Kind; }+ Shape() {} virtual double computeArea() = 0; };
这里值得提的一点是,llvm风格的RTTI支持没有v-tables(虚函数表)的类,而C++默认的dynamic_cast<>运算符并不支持这种转换。
3.接下来,需要确保该类被初始化为与类的动态类型相对应的值。通常,您希望它是基类构造函数的参数,然后从子类构造函数传入各自的XXXkind。
class Shape { public: /// discriminator for LLVM-style RTTI (dyn_cast<> et al.) enum ShapeKind { SK_Square,SK_Circle }; private: const ShapeKind Kind; public: ShapeKind getKind() const { return Kind; }- Shape() {}+ Shape(ShapeKind K) : Kind(K) {} virtual double computeArea() = 0; }; class Square : public Shape { double SIDeLength; public:- Square(double S) : SIDeLength(S) {}+ Square(double S) : Shape(SK_Square),SIDeLength(S) {} double computeArea() overrIDe; }; class Circle : public Shape { double Radius; public:- Circle(double R) : Radius(R) {}+ Circle(double R) : Shape(SK_Circle),Radius(R) {} double computeArea() overrIDe; };
4.有了上边的这些代码,还不够,还需要一步:告诉类,自己的类型是什么,这里是通过classof方法实现的
class Shape { public: /// discriminator for LLVM-style RTTI (dyn_cast<> et al.) enum ShapeKind { SK_Square,SK_Circle }; private: const ShapeKind Kind; public: ShapeKind getKind() const { return Kind; } Shape(ShapeKind K) : Kind(K) {} virtual double computeArea() = 0; }; class Square : public Shape { double SIDeLength; public: Square(double S) : Shape(SK_Square),SIDeLength(S) {} double computeArea() overrIDe;++ static bool classof(const Shape *S) {+ return S->getKind() == SK_Square;+ } }; class Circle : public Shape { double Radius; public: Circle(double R) : Shape(SK_Circle),Radius(R) {} double computeArea() overrIDe;++ static bool classof(const Shape *S) {+ return S->getKind() == SK_Circle;+ } };
这里已经完成了LLVM风格的RTTI(其实C++的RTTI实现也是同样的方法,这里用法有点不准确,LLVM和MSVC的RTTI实现略有不同,大概流程是typeID,调用___RTtypeID(),判断是否有vfptr,然后根据type_info来进行实现的,具体可以看下struct RTtixXX那几个结构体,感兴趣的反汇编一下看看)
如何实现层次继承的RTTI特性,大家可以关注下原文,主要是修改对应的classof,将原来的==判断改为多个判断,这里不进行赘述。
经验法则
懒得翻译了,自己看吧,很简单,重要的是继承树的先序遍历
The
Kind
enum should have one entry per concrete class,ordered according to a preorder traversal of the inheritance tree. The argument to classof
should be a const Base *
,where Base
is some ancestor in the inheritance hIErarchy. The argument should never@H_301_493@ be a derived class or the class itself: the template machinery for isa<>
already handles this case and optimizes it. For each class in the hIErarchy that has no children,implement a classof
that checks only against its Kind
. For each class in the hIErarchy that has children,implement a classof
that checks a range of the first child’s Kind
and the last child’s Kind
. 总结 以上是内存溢出为你收集整理的LLVM的RTTI特性全部内容,希望文章能够帮你解决LLVM的RTTI特性所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)