C++ 在MSVC ABI中,如何可靠地查找仅给定(void*)的vtable?

C++ 在MSVC ABI中,如何可靠地查找仅给定(void*)的vtable?,c++,visual-c++,rtti,msvcrt,vptr,C++,Visual C++,Rtti,Msvcrt,Vptr,这个问题是关于不可移植的MSVC ABI的 我试图写的是C++的代码< Type ID>代码>,显然是不可移植的,而不是神奇C++。对于安腾ABI(在Linux/Mac上使用),它非常简单: const std::type_info& dynamicast_typeid(void *mdo) { std::type_info **vptr = *reinterpret_cast<std::type_info ***>(mdo); std::type_info

这个问题是关于不可移植的MSVC ABI的

<>我试图写的是C++的代码< Type ID>代码>,显然是不可移植的,而不是神奇C++。对于安腾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;
}
(此代码基于。)

问题是一些C++类在偏移0处没有用VFPTR开始!有时,它们从vbptr开始

struct Class1 { virtual ~Class1() {} };
struct Class2 : virtual Class1 {};
Class1
以vfptr开头<代码>类别2以vbptr开头

struct Class1 { virtual ~Class1() {} };
struct Class2 : virtual Class1 {};
当一个类以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;
}
我已经确定

    if constexpr(IS_VBPTR_CLASS) {
        int first_vbase_offset = ((int**)mdo)[0][1];
        mdo = (char*)mdo + first_vbase_offset;
    }
    return __RTtypeid(mdo);
<编译> C++表达式> Type ID(x)< />代码>代码> ISHVBPTRIGHORE < /COD>是“编译器神奇地知道代码< > X/COD>是否具有VBPTR,基于静态代码类型<代码> x/COD>和编译器知道每种类型的布局的伪代码。

但是,在我的例子中,我不知道静态类型<代码> x/COD>,即使我做了,我也不知道如何从C++中用模板元编程来发现代码< > >代码>是否以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 in Class2”的typeinfo保存了子对象类型
Class1
的typeinfo,而不是最派生的类型
Class2
!所以还有一块拼图不见了


简而言之,我的问题是:给定
(void*)和对象类型为
,我如何检索
typeid(class2)

我现在有了可以工作的东西

函数
dynamiccast\u to\u mdo
相当于
dynamic\u cast(p)
——它调整
p
以指向其最派生的对象

函数
dynamicast\u 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;
}
//64位MSVC ABI
void*dynamicast_to_mdo(void*p)
{
如果(((int**)p)[0][0]==0){
p=(char*)p+((int**)p)[0][1];
}
int*完整对象定位器=((int***)p)[0][1];
int mdoffset=完整的对象定位器[1];
void*调整后的\u this=静态\u铸造(p)-mdoffset;
返回调整后的数据;
}
//64位MSVC ABI
const std::type_info&dynamicast_typeid(void*p)
{
如果(((int**)p)[0][0]==0){
p=(char*)p+((int**)p)[0][1];
}
int*完整对象定位器=((int***)p)[0][1];
char*result=(char*)完整的对象定位器;
结果-=完整的对象定位器[5];
结果+=完整的对象定位器[3];
返回*(const std::type_info*)结果;
}

A
void*
不包含任何类型信息。您至少需要一个
重新解释\u cast
@πάνταῥεῖ: 请注意,我的代码已经包含了很多重新解释的类型转换。在这一点上我比你领先很多还要注意,这个问题的语言律师/可移植性版本在这里:-但这不是我要问的。我想问一下MSVC ABI的细节。如果使用C++ C++样式表,代码会更容易理解。其中有很多,就像大多数C++ ABIS一样。ABI可以从主要版本更改为主要版本,这意味着类布局的细节也可以更改。这里唯一可以依赖的是,遵循与COM接口相同规则的类的vtable指针将始终位于同一个COM兼容位置。这适用于vtable本身的布局,您不能依赖于类型id信息位于您期望的位置,因为COM不使用它。