1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/mirrors_Tencent-flare

Клонировать/Скачать
rtti.md 6.4 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
Luo Bo Отправлено 26.05.2021 09:11 9cc00d3

RTTI

对于处于正常运行流程中的RTTI需求,C++内置的RTTI可能引入不必要的开销,因此我们参考LLVM的实现,实现了一套RTTI机制。

约定

为使现有的类支持我们的RTTI接口,其需要通过如下几种方式之一(但不应同时)向框架提供必要的信息:

  • 如果向下转换时始终转换到精确匹配的运行时类型(即向下转换时从不转换到某个对象的父类),那么可以让TExactMatchCastable继承,并在构造时调用SetRuntimeType(kRuntimeType<T>)即可。SetRuntimeType通过ADL找到,不应当通过flare::指定。

    这种情况下如果需要向下转换至实际运行时类型的基类,依然可以通过dynamic_cast,或者对转换目标(运行时类型的基类)特化CastingTraits<T>实现。但是这种用法目前不受支持,如果有这种任意转换的需求,我们建议使用更加通用的其他方法

  • 每个子类均提供原型如bool T::classof(const Base& val)静态方法。这一方法应当使用类型自身所提供(框架不感知)的途径,来识别val实际类型是否为T或其子类。对于某些场景,可能可以通过继承Castable基类,通过其提供的SetRuntimeTypeGetRuntimeTypekRuntimeType<T>来简化实现。

  • 特化CastingTraits<T>,并实现bool RuntimeTypeCheck<U>(const U& val)。其行为同T::classof

第二种方式侵入型较强但编码简单,第三种方式优缺点相反。

对于编译期即可确定可行的向上转换(即子类转换为基类的情况),框架会进行优化,这种情况classof无需处理(因此基类不需要提供classof)。

示例

这儿我们通过给基类增加type字段来实现运行时识别类型,取决于具体的场景,用户也可以通过其他方式(如虚函数等)来识别运行时类型。

相对来说,增加字段来识别运行时类型提供了更多的优化空间,通过被内联的accessor(如这儿示例)访问type可以被优化为单条mov指令;而虚函数通常可能会引入难以优化(devirtualization)的虚函数调用。

通过ExactMatchCastable实现

struct C1 : ExactMatchCastable {};
struct C2 : C1 {
  C2() { SetRuntimeType(this, kRuntimeType<C2>); }
};
struct C3 : C1 {
  C3() { SetRuntimeType(this, kRuntimeType<C3>); }
};

void CastTypes() {
  auto pc2 = std::make_unique<C2>();
  C1* p = pc2.get();
  ASSERT_NE(nullptr, dyn_cast<C1>(p));  // Success.
  ASSERT_NE(nullptr, dyn_cast<C2>(p));  // Success.
  ASSERT_EQ(nullptr, dyn_cast<C3>(p));  // Failure.
}

通过classof实现

struct Base {
  enum { kA, kB, kC } type;
};

struct A : Base {
  A() { type = kA; }

  static bool classof(const Base& val) {
    return val.type == kA || val.type == kB;
  }
};

struct B : A {
  B() { type = kB; }

  static bool classof(const Base& val) { return val.type == kB; }
};

struct C : Base {
  C() { type = kC; }

  static bool classof(const Base& val) { return val.type == kC; }
};

void CastTypes() {
  auto pb = std::make_unique<B>();
  ASSERT_NE(nullptr, dyn_cast<Base>(pb.get()));  // Success.
  ASSERT_NE(nullptr, dyn_cast<A>(pb.get()));     // Success.
  ASSERT_NE(nullptr, dyn_cast<B>(pb.get()));     // Success.
  ASSERT_EQ(nullptr, dyn_cast<C>(pb.get()));     // Failure.
}

接口

我们提供了如下一些方法供使用:

  • bool isa<T>(const U& val):判定val的运行时类型是否为T或其子类。
  • T* dyn_cast<T>(U& val) / T* dyn_cast<T>(U* val):如果val运行时类型是T或其子类,则将之转换为T*,否则返回nullptrdyn_cast不能接受nullptr
  • T* cast<T>(U& val) / T* cast<T>(U* val):将val转换为T*。如果val的运行时类型不是T或其子类,则会导致CHECK失败(即崩溃)。cast不能接受nullptr
  • T* (dyn_)cast_or_null(...):同对应的(dyn_)cast,但可以接受nullptr(并在这种情况下返回nullptr)。

接口设计考量

LLVM的接口设计与dynamic_cast有一些不同。我们经过分析,认为LLVM的选择是合理的,并采取了相同的设计:

  • (dyn_)cast默认不支持nullptr

    dynamic_cast显式允许传入nullptr,但是需要转换可能为nullptr的情况实际上较少,因此我们单独提供了..._or_null版本处理这种情况。

  • 模板参数只需要提供具体类型T,而非指针或引用类型。

    dynamic_cast接受T*T&并:

    • 由之决定表达式类型;
    • 根据类型决定转换失败返回nullptr(既失败符合预期)或抛出异常(即失败属于错误)。

    dynamic_cast的这一设计存在几方面问题:

    • C++标准在这儿难以保持一致,如模仿dynamic_castdynamic_pointer_cast接受的是T,而非xxx_pointer<T>(即,最终返回类型)。
    • 模板参数导致了对转换失败是否是“错误”的定义的不一致;
    • 增加编码量(T vs T* / T&)。

    另外,只接受参数T允许我们始终返回T*,这更符合我们的编码规范(不使用非常量引用,只使用非常量指针)。

  • 针对转换是否失败符合预期,分别提供dyn_castcastdynamic_cast通过T*T&区分,

    我们的分析见上。

性能对比

我们在Xeon Gold 6133上的测试代码得到如下数据:

Benchmark                                    Time           CPU Iterations
--------------------------------------------------------------------------
Benchmark_BuiltinDynamicCast                22 ns         22 ns   31094959
Benchmark_DynCast                            2 ns          2 ns  419860704
Benchmark_ExactMatchCastableDynCast          2 ns          2 ns  349577035

我们的benchmark框架空转开销约2ns,因此dyn_cast本身的开销应当小于1ns。

我们分析了编译产出,确认dyn_cast的调用没有被优化掉,并编译为如下代码:

... <+48>: mov    (%rax),%rax
... <+51>: cmpl   $0x2,0x8(%rax)
... <+55>: cmovae %r12,%rax
... <+59>: mov    %rax,(%rdx)

返回目录

Опубликовать ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://api.gitlife.ru/oschina-mirror/mirrors_Tencent-flare.git
git@api.gitlife.ru:oschina-mirror/mirrors_Tencent-flare.git
oschina-mirror
mirrors_Tencent-flare
mirrors_Tencent-flare
master