仮想メソッドテーブルで仮想関数のインデックスを取得することは可能ですか?
class A
{
virtual void foo();
}
foo
仮想メソッド テーブルの最初 (0) の項目であることはわかっています
しかし、私はfoo
0を持って得ることができますか?
ほとんどの (おそらくすべての) C++ 実装が仮想関数ディスパッチを実装する方法はたまたま vtables ですが、vtables が標準に存在するという保証はなく、ましてや特定の順序であるという保証はありません。
言い換えれば、知る唯一の方法は、特定のコンパイラを使用して vtable をどのように配置するかを調べ、クラス レイアウトに基づいて同じアルゴリズムに従って、関心のある関数のインデックスを見つけることです。
または、信じられないほどプラットフォーム固有のハックを使用して、メンバー関数の実際のアドレスを取得し、メモリ内の vtable を見つけて、内部のアドレスを検索します。
ただし、いずれにしても、そのような情報は、特定のコンパイラの ABI 保証に応じて、1 つのプラットフォームとコンパイラに固有であり、おそらくコンパイラのバージョンにも固有です。
補足として、GCC と MSVC++ の両方で、vtable のレイアウト アルゴリズムが文書化されており、vptr がオブジェクト内のどこにあるかのアルゴリズムが文書化されています。GCC の場合、ドキュメントは Common C++ ABI (別名 Itanium C++ ABI) です。MSVC++ の場合、ドキュメントがどこにあるのか、直接存在するのかどうかはわかりませんが、コンパイラは、少なくともデータ メンバーを持たないクラスが COM ABI と互換性を持つようにレイアウトされることを保証します。
理想的には、関数のアドレスをループして、指定された関数のアドレスを比較することができます。仮想関数のインデックスを出力できる次のコードを g++ で書きました。
#include <iostream>
#include <stdio.h>
using namespace std;
class test {
public:
virtual void foo() {
cout << "foo" << endl;
}
virtual void goo() {
cout << "goo" << endl;
}
virtual void hoo() {
cout << "hoo" << endl;
}
};
int find_function_index(test* p, void* f) {
for(int i = 0; i < 3; ++i) {
void* tmp = (void*)*((long*)*(int*)(p)+i);
if(tmp == f)
return i;
}
return -1;
}
int main() {
test* p = new test();
void* f1 = reinterpret_cast<void*>(&test::foo);
cout << "foo: " << find_function_index(p, f1) << endl;
void* f2 = reinterpret_cast<void*>(&test::goo);
cout << "goo: " << find_function_index(p, f2) << endl;
void* f3 = reinterpret_cast<void*>(&test::hoo);
cout << "hoo: " << find_function_index(p, f3) << endl;
}
次の図は、test::goo のアドレスと f2 の値です (これは、test::goo の正しいアドレスを格納しています)。
関数のシグネチャだけを知っていて、他の方法でその関数参照を取得して、一般的な方法で仮想ディスパッチを行いたいようです。
その追求にはメリットがあるかもしれませんが、あなたの質問はそれを実装の詳細 (vtable) と混同しています。これは C++ 標準では保証されていません。
幸いなことに、標準では名前を知らなくてもメンバー関数を呼び出す方法が提供されており、仮想ディスパッチも考慮されています。これは、メンバーへの単純なポインターです (保存およびコピーできます)。次のように使用します。
#include <iostream>
struct A
{
virtual void foo() { std::cout << "A::foo\n"; }
};
struct AA : A
{
virtual void foo() { std::cout << "AA::foo\n"; }
};
struct AAA : AA
{
virtual void foo() { std::cout << "AAA::foo\n"; }
};
void bar (A& a, void (A::* pMem)())
{
(a.*pMem)();
}
int main() {
A a;
AA aa;
AAA aaa;
bar (a, &A::foo);
bar (aa, &A::foo);
bar (aaa, &A::foo);
return 0;
}