2016-09-18 37 views
1

仮想メソッドテーブルの仮想関数のインデックスを取得することは可能ですか?C++仮想関数テーブルの取得インデックス

class A 
{ 
    virtual void foo(); 
} 

私はしかし、私はfooを持っていると0を得ることができfooは、仮想メソッドテーブル

で最初(0)アイテムです知っていますか?

+1

何を達成しようとしていますか? – DeiDei

+4

私は実装が定義されていると思うし、ABIに依存しているので、(理論上は)ポータブルな方法ではありません。 – skypjack

+1

[仮想関数のインデックスをコンパイラと同じように取得するにはどうすればよいですか?](http://stackoverflow.com/questions/3062647/how-to-get-every-virtual-function-index-just-as- the-compiler-does) – DeiDei

答えて

1

の実装がの実装で定義されていると思われます(@SteveJessopのおかげで答えにコメントが表示されています)、ABIに依存するので、(理論的には)ポータブルな方法では不可能です。
既知のABI定義の例として、here(Itanium C++ ABI)を参照してください。
これはC++標準の専門用語で、を意味します。実装では、それを文書化する必要があります
さらに、@ n.m。コメントに記載されている質問には、vtableのようなものは含まれていませんので、明示的には規定されていません。
実装はそれらを使用するか否かは自由であり、使用するかどうかは自由です。ユーザーコードにアクセスするためのサポートされた手段を自由に提供できます。

言い換えれば、それを行うための明示的で移植可能な方法はありません。

1

vtablesはほとんどの(たぶんすべての)C++実装が仮想関数ディスパッチを実装する方法であるにもかかわらず、それらが特定の順序であることははるかに少ない標準にも存在するという保証はありません。

つまり、特定のコンパイラを使用してvtableを整理し、クラスレイアウトに基づいて同じアルゴリズムを実行して、関心のある関数のインデックスを検索するだけです

そうでなければ、信じられないほどのプラットフォーム固有のハッキングを使用して、メンバー関数の実際のアドレスを取得し、メモリ内のvtableを見つけ、内部のアドレスを検索します。

いずれにしても、そのような情報は、特定のコンパイラのABI保証によっては、プラットフォームとコンパイラに固有のものであり、おそらくコンパイラバージョンでさえあります。

補足として、GCCとMSVC++の両方でvtableのレイアウトアルゴリズムが文書化されており、vptrがオブジェクト内でどこに座っているかについての文書化されたアルゴリズムがあります。 GCCの場合、ドキュメントはCommon C++ ABI(a.k.a. Itanium C++ ABI)です。 MSVC++では、ドキュメントがどこにあるのか、それとも直接存在するのかわかりませんが、コンパイラは少なくともデータメンバーのないクラスがCOM ABIと互換性があるようにレイアウトされていることを保証します。

-1

理想的には、関数のアドレスをループし、指定された関数のアドレスを比較することができます。仮想関数のインデックスを出力できる次のコードを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; 
} 

次picがテストのアドレス::グーとf2の値(テストの正しいアドレス::グーを格納している) enter image description here

+0

このコードはまったく意味がありません。これは* Pythonではありません。 'p-> pf1'を実行すると、メソッドの"束縛された "インスタンスに解決されず、もしそれがあったとしても' void(* func)() '型に代入することはできません。あなたは 'test * const this'の隠し引数をどこかに格納する必要があります!最良の場合、メンバ関数 'void(test :: *)()' _might_は 'void(*)(test *)'に代入可能ですが、 'this'ポインタを渡す方法が保証されていません(ABI)は、これらの2つの呼び出しの間で異なる場合があります。 –

+0

PS:メソッドに「バインドされた呼び出し」を保存する場合は、ファンクタまたはラムダを使用する必要があります: 'std :: bind(std :: mem_fn(&test :: foo)、p)'または ' [p](){p-> foo(); } 'は両方とも動作しますが、どちらもオブジェクトを何か(ポインタpのコピー)を格納するために' void(*)() 'に割り当てることはできません。 –

+0

@ Javier、skypjackのケースでは、彼はちょうど仮想関数のアドレスを知って、vtableの関数のアドレスを取得する必要がありますOKです。なぜテストは* constを必要としますか? 1つのタイプのオブジェクトはすべて同じvtableを指す必要があります。これらの関数の中に任意のメンバ変数を使用していれば、f1()、f2()、f3()のようなコードを試してみると、未定義の動作になります。あなたはコードを試して自分で分解をチェックすることができます。 –

0

あなたは仮想ディスパッチをやりたいように見えるです一般的なやり方で、関数シグネチャのみを知り、他の方法でその関数参照を取得します。
そのような追求はメリットがあるかもしれませんが、あなたの質問は実装の詳細(vtable)とそれを組み合わせています。これはC++標準では保証されていません。

幸いなことに、この標準では名前を知らなくてもメンバー関数を呼び出す方法が提供され、仮想ディスパッチを尊重することさえできます。メンバーへの簡単なポインタです(これは保存してコピーすることができます)。あなたはそれを使用するでしょうlike this

#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; 
}