この質問は、特に移植性のない MSVC ABI に関するものです。
私は、C++ に相当するものをtypeid
、明らかに非移植性でありながら魔法ではない C++ で書こうとしています。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;
}
(このコードは、Wine プロジェクトの__RTtypeid
に基づいています。)
問題は、一部の 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が実際に同等のものを生成すると判断しました
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)
— これは、「コンパイラは、静的な型と、コンパイラがすべての型のレイアウトを知っているという事実に基づいて、vbptr があるIS_VBPTR_CLASS
かどうかを魔法のように知る」ための擬似コードです。x
x
ただし、私の場合、 の静的型がわかりません。わかったとしても、(C++ 内から、テンプレート メタプログラミングを使用して) vbptr で始まるx
かどうかを調べる方法がわかりません。x
最後に、私は先に進み、少しごまかしました
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;
}
"Class1 in Class2" の vftable に格納されている typeinfo がClass1
、最も派生した型ではなく、サブオブジェクト type の typeinfo を保持していることを発見するだけClass2
です。つまり、パズルのピースがもう 1 つ欠けています。
私の質問を一言で言えば(void*)&object_of_type_class2
:typeid(Class2)