c – 在MSVC ABI中,如何只给出一个(void *)可靠地找到vtable?
这个问题具体是关于非便携式MSVC ABI的东西.
我试图在明显不可移植但不是魔法的C中写出相当于C的typeid.对于Itanium ABI(在Linux / Mac上使用),它非常简单: const std::type_info& dynamicast_typeid(void *mdo) { std::type_info **vptr = *reinterpret_cast<std::type_info ***>(mdo); std::type_info *typeinfo_ptr = vptr[-1]; return *typeinfo_ptr; } 所以现在我正在看64位的MSVC ABI,然后对它说,我很难过.对于非常简单的类,以偏移0处的vfptr开头的类,它几乎和Itanium一样简单: const std::type_info& dynamicast_typeid(void *mdo) { int *rtti_complete_object_locator = ((int ***)mdo)[0][-1]; char *result = (char *) rtti_complete_object_locator; result -= rtti_complete_object_locator[5]; result += rtti_complete_object_locator[3]; return *(std::type_info*)result; } (此代码基于the Wine project’s 问题是某些C类不是以偏移0处的vfptr开头的!有时他们从vbptr开始. struct Class1 { virtual ~Class1() {} }; struct Class2 : virtual Class1 {}; Class1以vfptr开头; Class2以vbptr开头. 当一个类以vbptr开头时,我相信它的第一个虚拟基础子对象(它在布局顺序中的第一个,因此它的“最活泼的”)总是会在其偏移0处有一个vfptr.所以如果我知道我’在处理以vbptr开头的类时,我想这样做: const std::type_info& dynamicast_typeid_for_vbptr_class(void *mdo) { int first_vbase_offset = ((int**)mdo)[0][1]; mdo = (char*)mdo + first_vbase_offset; int *rtti_complete_object_locator = ((int ***)mdo)[0][-1]; char *result = (char *) rtti_complete_object_locator; result -= rtti_complete_object_locator[5]; result += rtti_complete_object_locator[3]; return *(std::type_info*)result; } 我已经确定了MSVC does actually generate相当于 if constexpr(IS_VBPTR_CLASS) { int first_vbase_offset = ((int**)mdo)[0][1]; mdo = (char*)mdo + first_vbase_offset; } return __RTtypeid(mdo); 当编译C表达式typeid(x)时 – 其中IS_VBPTR_CLASS是伪代码,“编译器根据x的静态类型以及编译器知道每种类型的布局这一事实神奇地知道x是否具有vbptr.” 但是,在我的情况下,我不知道x的静态类型,即使我这样做,我也不知道如何找出(从C中,使用模板元编程)x是否以vbptr开头. 最后,我继续前进并稍微捏了一下 const std::type_info& dynamicast_typeid(void *mdo) { while (((int**)mdo)[0][0] == 0) { mdo = (char *)mdo + ((int**)mdo)[0][1]; } int *rtti_complete_object_locator = ((int ***)mdo)[0][-1]; char *result = (char *)complete_object_locator; result -= rtti_complete_object_locator[5]; result += rtti_complete_object_locator[3]; return *(std::type_info*)result; } 只发现存储在vftable中的“Class1中的Class1”的typeinfo保存了子对象类型Class1的typeinfo,而不是最派生类型的Class2!所以还有另一块拼图丢失了. 所以我的问题简而言之:给定(void *)& object_of_type_class2,如何检索typeid(Class2)? 解决方法
我有一些现在有用的东西!
函数dynamicast_to_mdo相当于dynamic_cast< void *>(p) – 它调整p以指向其最派生的对象. 函数dynamicast_typeid相当于typeid(p) – 它从p的vtable中获取typeinfo.我只是在使用我在问题中给出的软糖/黑客,我实际上不确定为什么我几个小时前得到了错误的答案;我想当我看到错误的typeinfo时,可能是因为我不小心试图将悬挂引用的typeid取为已销毁的堆栈变量. // 64-bit MSVC ABI void *dynamicast_to_mdo(void *p) { if (((int**)p)[0][0] == 0) { p = (char *)p + ((int**)p)[0][1]; } int *complete_object_locator = ((int ***)p)[0][-1]; int mdoffset = complete_object_locator[1]; void *adjusted_this = static_cast<char *>(p) - mdoffset; return adjusted_this; } // 64-bit MSVC ABI const std::type_info& dynamicast_typeid(void *p) { if (((int**)p)[0][0] == 0) { p = (char *)p + ((int**)p)[0][1]; } int *complete_object_locator = ((int ***)p)[0][-1]; char *result = (char *)complete_object_locator; result -= complete_object_locator[5]; result += complete_object_locator[3]; return *(const std::type_info*)result; } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |