C++ libcxxabi中dynamic_cast 实现

news2024/11/29 21:38:17

  摘要:最近在看一个崩溃的过程中详细看了一遍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.coloradmin.cn/o/1256141.html

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

相关文章

Linux系统管理:WinSCP 安装与使用

目录 一、实验 1.下载WinSCP 2.安装WinSCP 3.使用WinSCP 一、实验 1.下载WinSCP &#xff08;1&#xff09;地址 Downloading WinSCP-6.1.2-Setup.exe :: WinSCP 2.安装WinSCP &#xff08;1&#xff09;选择安装程序模式 &#xff08;2&#xff09;点击 &#xff08;3…

vite-性能优化-构建优化-cnd加速优化

CDN 加速优化 - 感觉用不大到 主要作用 &#xff1a; 将引入的依赖&#xff0c;打包部署后&#xff0c;在用户访问的时候&#xff0c; 通过网络CDN的方式进行加载&#xff0c;而非直接从你自己的服务器上加载。优点 &#xff1a; 1、直接降低了你自己的打包的体积&#xff0c…

基于helm的方式在k8s集群中部署gitlab - 部署(一)

文章目录 1. 背景说明2. 你可以学到什么&#xff1f;3. 前置条件4. 安装docker服务&#xff08;所有节点&#xff09;5. 部署k8s集群5.1 系统配置&#xff08;所有节点&#xff09;5.2 安装kubelet组件(所有节点)5.2.1 编写kubelet源5.2.2 安装kubelet5.2.3 启动kubelet 5.3 集…

2023nacos源码解读第4集——整体了解nacos源码模块

文章目录 1、类Linux tree的windows treee工具2、源码目录结构3、模块依赖关系 1、类Linux tree的windows treee工具 windows 自带的tree 不够用&#xff0c;使用node npm安装一个类Linux 的treee npm install -g cnpm --registryhttps://registry.npm.taobao.org npm config…

MySQL 8 手动安装后无法启动的问题解决

开头还是介绍一下群&#xff0c;如果感兴趣PolarDB ,MongoDB ,MySQL ,PostgreSQL ,Redis, Oceanbase, Sql Server等有问题&#xff0c;有需求都可以加群群内有各大数据库行业大咖&#xff0c;CTO&#xff0c;可以解决你的问题。加群请联系 liuaustin3 &#xff0c;&#xff08;…

springframe工程导入

配置gradle工程 init.d 目录下新建init.gradle allprojects {repositories {mavenLocal()maven {allowInsecureProtocol trueurl https://maven.aliyun.com/nexus/content/repositories/central/}} } 报错Plugin [id: org.jetbrains.dokka, version: 0.10.1, apply: false] w…

Python集合类型

目录 目标 版本 官方文档 集合分类 实战 创建 循环 常用方法 目标 掌握set和frozenset两种集合的使用方法&#xff0c;包括&#xff1a;创建、交集、并集、差集等操作。 版本 Python 3.12.0 官方文档 Set Types — set, frozensethttps://docs.python.org/3/library/s…

常见指令的数据通路和执行过程

作此篇的原因是17年19题&#xff1a; 本题选A&#xff0c;做的时候总感觉不够通透&#xff0c;因此把这题涉及到的内容全部看了一遍&#xff0c;顿时没有那种朦胧感了 零、五段式流水线&#xff1a; 以下均为MIPS设定&#xff1a;指令长度为32位&#xff0c;主存按字节编址&a…

RC-MVSNet:无监督的多视角立体视觉与神经渲染--论文笔记(2022年)

RC-MVSNet&#xff1a;无监督的多视角立体视觉与神经渲染--论文笔记&#xff08;2022年&#xff09; 摘要1 引言2 相关工作2.1 基于监督的MVS2.2 无监督和自监督MVS2.3 多视图神经渲染 3 实现方法3.1 无监督的MVS网络 Chang, D. et al. (2022). RC-MVSNet: Unsupervised Multi-…

领域驱动设计总结——如何构造领域模型

领域驱动设计总结——如何构造领域模型 本文为领域驱动设计系列总结的第三篇&#xff0c;主要对领域驱动设计概念做个介绍&#xff0c;本系列领域驱动设计总结主要是在Eric Evans 所编写的《领域驱动设计》 一书的基础上进行归纳和总结。本文主要介绍在领域驱动设计中如何构造…

【数据中台】开源项目(2)-Dbus系统架构

大体来说&#xff0c;Dbus支持两类数据源&#xff1a; RDBMS数据源 日志类数据源 1 RMDBMS类数据源的实现 以mysql为例子. 分为三个部分&#xff1a; 日志抽取模块(最新版DBus已经废弃该模块&#xff0c;使用canal直接输出到kafka) 增量转换模块 全量拉取模块 1.1 日志抽…

紫光展锐 展讯芯片 展讯处理器解锁BL 各分区结构 ROM 分区列表代表什么 bin img 表示什么意思

是展锐 Android 10.0、Android 9.0 平台 ROM 空间划分情况以及分区格式、分区大小和分区功能的 初步描述。 prodnv 开机后系统中的 productinfo 分区&#xff0c;保 存 adc 校准参数、eng.db 数据库。 Miscdata 保存 ota、recovery 时的一些数据 recovery 存放 recovery.i…

【深度学习实验】图像处理(二):PIL 和 PyTorch(transforms)中的图像处理与随机图片增强

文章目录 一、实验介绍二、实验环境1. 配置虚拟环境2. 库版本介绍 三、实验内容0. 导入需要的工具包1. PIL图像处理a. 生成绿色和蓝色图像b. 缩放和合成图像c 在合成图像上添加文字d. 展示并保存图像 2. PIL随机图像增强a. 定义随机图像增强函数b. 实验结果展示 3. PyTorch&…

【Amazon】通过代理连接的方式导入 AWS EKS集群至KubeSphere主容器平台

文章目录 一、设置主集群方式一&#xff1a;使用 Web 控制台方式二&#xff1a;使用 Kubectl命令 二、在主集群中设置代理服务地址方式一&#xff1a;使用 Web 控制台方式二&#xff1a;使用 Kubectl命令 三、登录控制台验证四、准备成员集群方式一&#xff1a;使用 Web 控制台…

P17C++析构函数

目录 前言 01 什么是析构函数 1.1 举个栗子 02 为什么要写析构函数 前言 今天我们要讨论一下它的“孪生兄弟”&#xff0c;析构函数&#xff0c;它们在某些方面非常相似。 与构造函数相反&#xff0c;当对象结束其生命周期&#xff0c;如对象所在的函数已调用完毕时&…

[LaTex]arXiv投稿攻略——jpg/png转pdf

一、将图片复制进ppt&#xff0c;右键单击图片选择设置图片格式&#xff0c;获取图片高度和宽度 二、选择“设计-幻灯片大小-自定义幻灯片大小” 三、设置幻灯片大小为图片大小 四、 选择“最大化” 五、 检查幻灯片大小是否与图像大小一致 六、导出为PDF

web前端之vue和echarts的堆叠柱状图顶部显示总数、鼠标悬浮工具提示、设置图例的显示与隐藏、label、legend、tooltip

MENU 效果图htmlJavaScripstyle解析 效果图 html <template><div><div><div id"idStackedColumnChart" style"width: 100%; height: 680px"></div></div></div> </template>JavaScrip export default {…

单细胞seurat入门—— 从原始数据到表达矩阵

根据所使用的建库方法&#xff0c;单细胞的RNA序列&#xff08;也称为读取&#xff08;reads&#xff09;或标签&#xff08;tags&#xff09;&#xff09;将从转录本的3端&#xff08;或5端&#xff09;&#xff08;10X Genomics&#xff0c;CEL-seq2&#xff0c;Drop-seq&…

Django(十一、auth认证模块)

文章目录 一、auth介绍auth认证相关模块及操作扩展auth_user表 一、auth介绍 Django自带一个admin路由&#xff0c;但是需要我们提供管理员账户和密码&#xff0c;如果想要使用admin后台管理&#xff0c;需要先创建表&#xff0c;然后创建管理员账户。 直接执行数据类迁移命令…

Redis:持久化RDB和AOF

目录 概述RDB持久化流程指定备份文件的名称指定备份文件存放的目录触发RDB备份redis.conf 其他一些配置rdb的备份和恢复优缺点停止RDB AOF持久化流程AOF启动/修复/恢复AOF同步频率设置rewrite压缩原理触发机制重写流程no-appendfsync-on-rewrite 优缺点 如何选择 概述 Redis是…