C++ libcxxabi中dynamic_cast 实现

  摘要:最近在看一个崩溃的过程中详细看了一遍cxxabi的定义,就想着看一些llvm中cxxabi的一些实现。本文描述了cxxabi中dynamic_cast的实现以及原理。
  关键字cxxabi,dynamic_cast

1 简介

  C++中,dynamic_cast用于有虚函数的继承链中父类型到子类型的安全转换。比较常见的用法如下:

class A{
public:virtual ~A() = default;
};class B{
};A *p = new B;
B* pp = dynamic_cast<B*>(p);

  dynamic_cast如何识别当前类的类型,这依赖于RTTI。C++中包含虚函数的对象都有一个虚函数表,一般情况下都在首地址(多继承和虚继承会有多个)有一个指向该虚函数表的虚函数表指针。虚函数表中有以下内容:

  • 基类偏移;
  • typeinfo;
  • 如果有虚函数的话会有虚析构函数指针,一般情况下有两个;

The entries for virtual destructors are actually pairs of entries. The first destructor, called the complete object destructor, performs the destruction without calling delete() on the object. The second destructor, called the deleting destructor, calls delete() after destroying the object. Both destroy any virtual bases; a separate, non-virtual function, called the base object destructor, performs destruction of the object but not its virtual base subobjects, and does not call delete().

  • 虚函数指针,如果是虚继承对应的虚函数指针可能是一个thunk function。

A segment of code associated (in this ABI) with a target function, which is called instead of the target function for the purpose of modifying parameters (e.g. this) or other parts of the environment before transferring control to the target function, and possibly making further modifications after its return. A thunk may contain as little as an instruction to be executed prior to falling through to an immediately following target function, or it may be a full function with its own stack frame that does a full call to the target function.

  C++中就是通过虚函数表携带的typeinfo信息来确认当前类的类型,如果是该类型就就可以转换成功,否则的话就会转换失败。cxxabi的基本实现思路也是如此。

2 cxxabi实现

  先来看一下cxxabi中对应实现函数的声明。

  • static_ptr:期望将进行转换的类地址;
  • static_type:当前类的类型信息;
  • dst_type:目标转换类的类型信息;
  • src2dst_offset:由 Itanium ABI 所规定的 hint 值,辅助优化用:
    • 是一个非负整数值,说明From是To的唯一一个公开非虚基类,且From基类子对象在一个To对象中的偏移为src2dst_offset;
    • -1表示无hint;
    • -2表示From不是To的公开基类;
    • -3表示To存在多个公开From基类,但是这些公开From基类都不是To的虚基类。
extern "C" _LIBCXXABI_FUNC_VIS void *
__dynamic_cast(const void *static_ptr, const __class_type_info *static_type, const __class_type_info *dst_type, std::ptrdiff_t src2dst_offset) 

在这里插入图片描述

  首先是从虚函数表中提取出当前类对象相对于子类首地址的offset和typeinfo,这两项都是固定存储在虚函数表指针所指向位置的-1和-2位置处。

The offset to top holds the displacement to the top of the object from the location within the object of the virtual table pointer that addresses this virtual table, as a ptrdiff_t. It is always present. The offset provides a way to find the top of the object from any base subobject with a virtual table pointer. This is necessary for dynamic_cast<void*> in particular.

    void **vtable = *static_cast<void ** const *>(static_ptr);ptrdiff_t offset_to_derived = reinterpret_cast<ptrdiff_t>(vtable[-2]);const void* dynamic_ptr = static_cast<const char*>(static_ptr) + offset_to_derived;const __class_type_info* dynamic_type = static_cast<const __class_type_info*>(vtable[-1]);

  接下来便是根据期望转换的目标对象的typeinfo和当前获取的typeinfo作对比进而选择是否将进行更进一步的转换。需要注意的是比较两个对象是否相同的方式:

static inline bool is_equal(const std::type_info* x, const std::type_info* y, bool use_strcmp){// Use std::type_info's default comparison unless we've explicitly asked// for strcmp.if (!use_strcmp)return *x == *y;// Still allow pointer equality to short circut.return x == y || strcmp(x->name(), y->name()) == 0;
}

相同类型
  typeinfo是编译器生成的,是静态对象用户只能读写,这里提供了使用typeinfo的名字和指针来比较的方式,是为了避免一些场景下typeinfo不一致导致转换失败(虽然ABI中默认是关闭的)。
  针对typeinfo相同的情况下则需要根据src2dst_offset来辅助优化:

  • src2dst_offset为非负整数时,fromt是to的唯一公开非虚基类。但是to类型是有可能有其他非公开基类的,因此需要比较vtptr中的偏移和src2dst_offset,能够匹配则直接返回偏移后的地址,否则返回空指针;
  • src2dst_offset为-2表示,from不是to的公开基类,直接返回空指针;
        if (src2dst_offset >= 0){// The static type is a unique public non-virtual base type of//   dst_type at offset `src2dst_offset` from the origin of dst.// Note that there might be other non-public static_type bases. The//   hint only guarantees that the public base is non-virtual and//   unique. So we have to check whether static_ptr points to that//   unique public base sub-object.if (offset_to_derived == -src2dst_offset)dst_ptr = dynamic_ptr;}else if (src2dst_offset == -2){// static_type is not a public base of dst_type.dst_ptr = nullptr;}
  • src2dst_offset无法提供帮助时,只能所搜继承图来确认是否存在继承关系。此时只能搜索整张继承图,搜索出从最派生对象所有的from公开基类子对象。如果from所指向的对象是这些from基类子对象中的某一个,那么转换成功,转换结果是最派生对象指针;否则转换失败。在搜索继承图的过程中可以应用一些剪枝方法降低搜索开销。例如一旦搜索到from指向的对象就停止搜索、不搜索包含私有继承的路径等。
info.number_of_dst_type = 1;
// Do the  search
dynamic_type->search_above_dst(&info, dynamic_ptr, dynamic_ptr, public_path, false);

不相同类型
  typeinfo不相同时,表示源和目标只是在相同的继承图上,但是并不存在直接的公开继承关系,因此为了确认是否真的存在该关系只能搜索当前源和目标的继承图来确认。

libcxxabi的dynamic_cast实现似乎有性能问题https://reviews.llvm.org/D137315#3910662这个patch修复了该问题。修复完的benchmarkhttps://gist.github.com/Lancern/212a26a3144343f459428dffe202cde0

搜索策略
  对于不同继承类型的类的 type info 有不同的搜索策略。例如对于有虚多继承的类的 type info(__vmi_class_type_info)、单继承的类的 type info(__si_class_info)等。搜索的方式看起来就是广度优先搜索再加上一些剪枝的优化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/172535.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【go入门】表单

4.1 处理表单的输入 先来看一个表单递交的例子&#xff0c;我们有如下的表单内容&#xff0c;命名成文件login.gtpl(放入当前新建项目的目录里面) <html> <head> <title></title> </head> <body> <form action"/login" meth…

【Java】循环语句练习

文章目录 1. 计算5的阶乘2. 计算 1! 2! 3! 4! 5!3. 数字9 出现的次数4. 判定素数5. 求1-100之间的素数6. 求2个整数的最大公约数7. 计算分数的值8. 模拟登陆9. 输出乘法口诀表10. 求出0&#xff5e;999之间的所有“水仙花数”并输出11. 猜数字游戏&#x1f648; 1. 计算5的…

Linux系统编程 day05 进程控制

Linux系统编程 day05 进程控制 1. 进程相关概念2. 创建进程3. exec函数族4. 进程回收 1. 进程相关概念 程序就是编译好的二进制文件&#xff0c;在磁盘上&#xff0c;占用磁盘空间。程序是一个静态的概念。进程即使启动了的程序&#xff0c;进程会占用系统资源&#xff0c;如内…

FO-like Transformation

参考文献&#xff1a; [RS91] Rackoff C, Simon D R. Non-interactive zero-knowledge proof of knowledge and chosen ciphertext attack[C]//Annual international cryptology conference. Berlin, Heidelberg: Springer Berlin Heidelberg, 1991: 433-444.[BR93] Bellare M…

软件介绍01- koodo Reader支持所有电脑平台!

1 软件简介 Koodo Reader软件是一款阅读器&#xff0c;可以阅读各种格式的文档。用来代替kindle。界面简洁&#xff0c;好看&#xff0c;阅读功能强大&#xff0c;而且可以多设备同步。 因为开源&#xff0c;所以免费。而且支持所有电脑平台&#xff01; 支持格式&#xff1a…

Android修行手册-ViewPager定制页面切换以及实现原理剖析

Unity3D特效百例案例项目实战源码Android-Unity实战问题汇总游戏脚本-辅助自动化Android控件全解手册再战Android系列Scratch编程案例软考全系列Unity3D学习专栏蓝桥系列ChatGPT和AIGC &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分…

kafka的设计原理

文章目录 1 Kafka简介2 Kafka的架构2.1 Kafka 一些重要概念2.2 工作流程2.3 副本原理2.4 分区和主题的关系2.5 生产者2.5.1 分区可以水平扩展2.5.2 分区策略 2.6 消费者2.6.1 消费方式2.6.2 分区分配策略 2.7 数据可靠性保证2.7.1 副本数据同步策略2.7.2 ACK 应答机制2.7.3 可靠…

Java抽象类和接口(1)

&#x1f435;本篇文章将对抽象类和接口相关知识进行讲解 一、抽象类 先来看下面的代码&#xff1a; class Shape {public void draw() {System.out.println("画");} } class Cycle extends Shape {public void draw() {System.out.println("圆形");} } …

开发知识点-ArkTS-鸿蒙开发-Typescript

Typescript IED IED https://developer.harmonyos.com/cn/develop/deveco-studio/#download

打开CMD的六种方法,CMD快捷键,CMD命令大全及详解

目录 前言1. winR快捷键2、通过文本文档创建&#xff1b;3、通过C盘中的cmd.exe文件打开&#xff1b;4、创建快捷方式&#xff1b;5、通过PowerShell打开&#xff1b;6、通过文件夹导航栏打开&#xff1b; 前言 自己的电脑win键失灵了&#xff0c;想通过winR来调出cmd&#xff…

【Linux基础】Linux常见指令总结及周边小知识

前言 Linux系统编程的学习我们将要开始了&#xff0c;学习它我们不得不谈谈它的版本发布是怎样的&#xff0c;谈它的版本发布就不得不说说unix。下面是unix发展史是我在百度百科了解的 Unix发展史 UNIX系统是一个分时系统。最早的UNIX系统于1970年问世。此前&#xff0c;只有…

Doris单机部署——2.0.1.1版本

目录 一、前期准备工作 1.设置系统最大文件打开句柄数 2.时钟同步 3.关闭每台机器的交换分区 4.下载安装包 二、单节点部署安装Doris (一)安装fe 1.解压改名 2.修改配置文件 3.创建元数据目录 4.启动fe 5.访问fe的webUI (二)安装be 1.进入be目录下&#xff0c;修…

Leetcode—35.搜索插入位置【简单】

2023每日刷题&#xff08;四十&#xff09; Leetcode—35.搜索插入位置 实现代码 int lower_bound(int* arr, int numsSize, int tar) {int left 0, right numsSize;int mid;// 左闭右开[left, right)while(left < right) {mid left (right - left) / 2;if(arr[mid] &…

Cadence Vmanager vsif文件编写指南(持续更新...)

目录 1.NTF格式介绍 1.1.1 {属性&#xff1a;值}定义 1.1.2类别 1.1.3语法 2.vsif文件中有效的container 2.1 session {…} 1.NTF格式介绍 Cadence的Vmanager工具采用vsif类型的文件作为regression的输入文件&#xff0c;采用vplanx/csv类型的文件作为vplan的输入文件&am…

BC77 简单计算器(牛客)

#include <stdio.h> int main() {double a, b, d;//用来接收浮点数char c;//用来接受符号scanf("%lf %c %lf", &a, &c, &b);if (c || c - || c * || c /)//判断输入的运算符号不包括在&#xff08;、-、*、/&#xff09;范围内{switch (c)//根…

Kotlin应用——使用kt进行web开发 使用h2database进行初始化数据库 mybatis-plus使用

Kotlin 是一门现代但已成熟的编程语言&#xff0c;旨在让开发人员更幸福快乐。 它简洁、安全、可与 Java 及其他语言互操作&#xff0c;并提供了多种方式在多个平台间复用代码&#xff0c;以实现高效编程。 kt入门的合集文章如下&#xff1a; Kotlin学习——kt入门合集博客 &…

P16 C++构造函数

目录 前言 01 什么是构造函数呢&#xff1f; 02 非构造函数初始化变量 03 构造函数初始化变量 04 带参数的构造函数。 最后的话 前言 我们继续学习 C 的面向对象编程&#xff0c;本章主要是讲其中的 构造函数。 01 什么是构造函数呢&#xff1f; 构造函数基本上是一种特…

tinyViT论文笔记

论文&#xff1a;https://arxiv.org/abs/2207.10666 GitHub&#xff1a;https://github.com/microsoft/Cream/tree/main/TinyViT 摘要 在计算机视觉任务中&#xff0c;视觉ViT由于其优秀的模型能力已经引起了极大关注。但是&#xff0c;由于大多数ViT模型的参数量巨大&#x…

MetaObject-BeanWrapper-MetaClass-Reflector的关系

MetaObject、BeanWrapper、MetaClass、Reflector之间是通过装饰器模式逐层进行装饰的。其中MetaObject、BeanWrapper是操作对象&#xff1b;MetaClass、Reflector是操作Class ObjectWrapper类结构图 BaseWrapper是对BeanWrapper、MapWrapper公共方法的提取及类图的优化&#…

线程的创建方式

作者简介&#xff1a; zoro-1&#xff0c;目前大二&#xff0c;正在学习Java&#xff0c;数据结构&#xff0c;mysql&#xff0c;javaee等 作者主页&#xff1a; zoro-1的主页 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01;&#x1f496;&#x1f496; 线程的创建方…