本文思路来源于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() = ;
}; 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() = ;
};

这里值得提的一点是,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() = ;
}; 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() = ;
}; 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,将原来的==判断改为多个判断,这里不进行赘述。

经验法则

懒得翻译了,自己看吧,很简单,重要的是继承树的先序遍历

  1. The Kind enum should have one entry per concrete class, ordered according to a preorder traversal of the inheritance tree.
  2. The argument to classof should be a const Base *, where Base is some ancestor in the inheritance hierarchy. The argument should never be a derived class or the class itself: the template machinery for isa<> already handles this case and optimizes it.
  3. For each class in the hierarchy that has no children, implement a classof that checks only against its Kind.
  4. 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.

最新文章

  1. 06. Web大前端时代之:HTML5+CSS3入门系列~HTML5 画布
  2. 【WPF】Combobox指定选中值用selectedValue不是很灵的时候,
  3. VMware 安装虚拟机安装MAC (OSX10_11)
  4. js中join和split的用法
  5. Linux 批量修改文件名
  6. QC学习一:Windows环境中Quality Center 9.0安装详解
  7. [CareerCup] 7.7 The Number with Only Prime Factors 只有质数因子的数字
  8. 【转】Git连接oschina管理代码版本
  9. Roy Li的学习和成长自传
  10. 关于Build Active Architecture Only属性
  11. 驱动09.nand flash
  12. emmet 教程 emmet快捷键大全
  13. Bootstrap入门(三十)JS插件7:警告框
  14. 【转载】使用sklearn优雅地进行数据挖掘
  15. filebeat_config
  16. UWP中MarkupExtension的使用
  17. 如何设置访问内网web项目
  18. C++ 栈和队列的使用
  19. 二、存储管理器--SDRAM
  20. nagiosQL访问时报错PHP message: PHP Deprecated: preg_replace(): The /e modifier is deprecated, use preg_replace_callback instead

热门文章

  1. TransitionDrawable
  2. android在点击EditText的时候始终不弹出软件键盘
  3. mysql left join对于索引不生效的问题
  4. QFramework 使用指南 2020 (一): 概述
  5. Tomcat 部署web 项目
  6. RESTful规范与常用状态码
  7. 《鸟哥的Linux私房菜:基础学习篇》读书笔记之第一部分
  8. [Agc029A]Irreversible operation_逆序对
  9. tabs 导航 及内容切换
  10. 【51nod】2027 期望问题