本文思路来源于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.

LLVM的RTTI特性的更多相关文章

  1. RTTI(Runtime Type Information )

    RTTI 是“Runtime Type Information”的缩写,意思是:运行时类型信息.它提供了运行时确定对象类型的方法.本文将简略介绍 RTTI 的一些背景知识.描述 RTTI 的概念,并通 ...

  2. Delphi: RTTI与ini配置文件

    项目以Rtti特性做文件参数配置,简化每项读写ini操作,记录以做备忘,代码如下: unit uGlobal; interface uses Windows, Messages, SysUtils, ...

  3. C++中模板单例的跨SO(DLL)问题:RTTI,typeid,static,单例

    (转载请注明原创于潘多拉盒子) C++的模板可以帮助我们编写适合不同类型的模板类,给代码的复用性提供了极大的方便.近来写了一个涉及单例的C++模板类,简化下来可以归结为以下的代码: template ...

  4. 《Gradle权威指南》--Android Gradle NDK支持

    No1: 在根项目下的local.properties文件中配置 sdk.dir=/home/frame/android/android-sdk ndk.dir=/home/frame/android ...

  5. VC++ MFC类库基础(55讲全)

    视频保存在播音员 网盘中内容简介: 本部分是您成为VC++软件工程师必备的阶段,如果您没有任何基础,学习C++能快速让您进入编程领域,建议配合书籍<C++入门经典> 关键词: VC++.V ...

  6. 《C++ Primer Plus》学习笔记9

    <C++ Primer Plus>学习笔记9 第15章 友元.异常和其他 <<<<<<<<<<<<<<& ...

  7. [转帖]软件的变革与 AOT

    软件的变革与 AOT https://www.colabug.com/851475.html 文章写的很牛B .. 前言 AOT 即 Ahead of Time Compilation,即运行前编,与 ...

  8. Application.mk语法解释(转)

    转自:http://blog.csdn.net/roland_sun/article/details/46318893 Application.mk是用来描述你的应用程序需要哪些模块,以及这些模块所要 ...

  9. LLVM 编码规范 - 中文翻译

    LLVM 编码规范 导论 语言.库和标准 C++ 标准版本 C++ 标准库 Go 代码准则 机械的代码问题 代码格式化 注释 头文件 类概述 method information 注释格式化 使用Do ...

随机推荐

  1. MySQL连接错误:Can't connect to MySQL server on'localhost' (10055)

    在Windows服务器上确认服务器和mysql都是正常运行,但就是连接不上.搜了一下别人的解决方案, 参考这篇https://blog.csdn.net/langren697/article/deta ...

  2. LODOP纸张高度不定的纯文本累计高度

    小票由于纸张没有确定的高度,根据内容多少,小票打印机出多少纸,在设置纸张的时候,需要把纸张设置成不定高的纸张.简短问答:小票打印 ,参考样例18 http://www.c-lodop.com/demo ...

  3. jQuery.fn.extend与jQuery.extend的区别

    jquery 本身并不提供 jQuery.color() 这个方法,如果我们需要对jQuery本身提供的方法进行扩展,则我们就需要是用jQuery.fn.extend: jQuery.fn.exten ...

  4. 【ARTS】01_24_左耳听风-201900422~2019004028

    ARTS: Algrothm: leetcode算法题目 Review: 阅读并且点评一篇英文技术文章 Tip/Techni: 学习一个技术技巧 Share: 分享一篇有观点和思考的技术文章 Algo ...

  5. iOS开发之——keychain使用

    iOS的keychain服务提供了一种安全的保存私密信息(密码,序列号,证书等)的方式.每个ios程序都有一个独立的keychain存储.从ios 3.0开始,跨程序分享keychain变得可行. 使 ...

  6. 使用mybatis的resultMap进行复杂查询

        记录下mybatis的集合查询中碰到的问题 https://jaychang.iteye.com/blog/2357143   MyBatis ofType和javaType区别 https: ...

  7. 知识点整理-mysql怎么查看优化器优化后的sql

    背景 1.新建两张表 CREATE TABLE t1 (m1 )); CREATE TABLE t2 (m2 )); 2.插入些数据 INSERT INTO t1 VALUES(, , , 'c'); ...

  8. OPC 数据交互环境配置

    本文源自ioufev先生的博客<OPC和DCOM配置>(https://www.cnblogs.com/ioufev/p/9365919.html)及「geekc」先生的<OPC工作 ...

  9. Java进程监控

    目录 1.引言 2. 程序启停, 为进程自定义项目名称 3. 操作系统判断 4. 获取进程信息 5. 内存,CPU信息 6. 堆内存信息 7. 端口信息 8. 线程信息 9. MXBean使用样例 9 ...

  10. 【VS开发】C/C++预编译命令

    C/C++中宏总结C程序的源代码中可包括各种编译指令,这些指令称为预处理命令或预处理器.虽然它们实际上不是C语言的一部分,但却扩展了C程 序设计的环境. 预处理指令的主要作用就是把通过预处理的内建功能 ...