从 type_info 判断子类(下)

那么在有多继承的情况下,我们需要更为复杂的 is_base_of,这时需要计算偏移量:更为复杂的就是如何更改指针的值。如果你不清楚为什么我们需要这个,那说明你对 C++ 的 object model 不是十分了解。这个版本不支持 virtual base

bool is_base_of(const std::type_info* a, const std::type_info* b, ptrdiff_t* offset) {
 if (*a == *b) return true;
 const auto* bb = dynamic_cast<const __cxxabiv1::__class_type_info*>(b);
 if (bb == nullptr) return false;
 const auto* si_bb = dynamic_cast<const __cxxabiv1::__si_class_type_info*>(bb);
 if (si_bb) {
   const auto* b_base = si_bb->__base_type;
   return is_base_of(a, b_base, offset);
 }
 const auto* vmi_bb = dynamic_cast<const __cxxabiv1::__vmi_class_type_info*>(bb);
 if (vmi_bb) {
   for (int i = 0; i < vmi_bb->__base_count; ++i) {
     const auto& b_base = vmi_class_tinfo->__base_info[i];
     *offset += b_base.__offset();
     auto result = is_base_of(a, b_base.__base_type, offset);
     if (!result) {
       *offset -= b_base.__offset();
       continue;
     }
     return result;
   }
 }
 return false;
}

有了这个我们可以修改 any_ptr,这里有几个地方值得斟酌:

  • 这里的 is_base_of 同时计算了指针的偏移量,我们可以把 void* reinterpret 成为 char* 偏移之后再 reinterpret 成为 void*,最后 static_cast 成为需要的类型
  • make_any_ptr 传递类型信息究竟应该选择 typeid(T) 还是 typeid(t),这其实是一个很 delicate 的事情
  • 对于之前的 boost.any 版本我们应该选择 typeid(T),即传递编译时类型(比如使用 parent ref 到一个 child 对象,对这个 parent ref 使用 make_any_ptr 对应的 T 为 parent 类型)。这是因为这个版本只能转换回编译时类型,这意味着如果不这样做 any_cast 到 parent 类型反而会 fail,这就让人觉得不能接受
  • 但是我们现在的 any_ptr 支持任意一个 base class pointer 的 casting,这意味着如果我们传递 typeid(t),即运行时类型,对于编译时类型以及所有其父类型都应该是可以成功的转换的。所以不存在之前的问题
  • 存在 virtual base 的时候 __offset() 返回的并不是 base 指针的偏移,而是这个 virtual base 对应的 virtual base offset 在 vtable 中的 offset,这个听起来很诡异:一个类可以存在多个 virtual base,每一个 virtual base 的访问都需要根据对应的 virtual base offset(加在对象的指针上);而 __offset() 返回的 offset 就是用来获得这个 virtual base offset 的,然而我们需要将这个 offset 与 vtable 指针相加,然后 deref 获得实际的偏移量。

从这个意义上来说,这个 type erasure 的实现完全借助了 std::type_info 本身。通过 fake_type_id 我们能实现一般性的 boost.any 但是如果需要这种程度的类型转换,我们需要依赖更底层的 cxxabi.h,这已经不是 C++ 标准的范畴,而是每个实现标准的 vendor 自己定义的一套东西了。这个东西里面其实也涉及了诸如异常处理、mangling 等实现的细节。

Advertisements
从 type_info 判断子类(下)

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s