开发者

Detect the the vtable index/ordinal of a specific virtual function (using Visual C++)

Further to my question: Detect the the vtable offset of a specific virtual function (using Visual C++):

Given:

struct A 
{
   virtual void a() {}
   virtual void b() {}
   virtual void c() {} 
};

How can I write a function in Visua开发者_JAVA百科l C++ (probably non-portable), such that:

int x = GetVtableIndex(&A::a); //returns 0
int x = GetVtableIndex(&A::b); //returns 1
int x = GetVtableIndex(&A::c); //returns 2

Reasons why I'd want to do this are in the linked question.


Inspired by imre's answer, if you just want to know the total size of the vtable; ie, how many virtual functions there are, this may work. This method doesn't instantiate the class.

template <typename T>
int VTableSize()
{
    class VTableCounter
    {
    public:
        virtual int Get1() { return 0; }
        virtual int Get2() { return 1; }
        virtual int Get3() { return 2; }
        // ... 994 more ...
        virtual int Get998() { return 997; }
        virtual int Get999() { return 998; }
        virtual int Get1000() { return 999; }
    };

    class A : public T
    {
    public:
        virtual int LastVirtual() {return -1;}
    };

    VTableCounter vt;
    return reinterpret_cast<A*>(&vt)->LastVirtual();
}

Note I didn't use Boost.PP because it is limited to 256 from what I can tell. You should be able to use vim macros or something to get an arbitrary number of virtuals. It will probably give incorrect values if multiple or virtual inheritance is used, however.


Inspired by imre's and Ivan's answer, I think this can be completely solved:

template <class T, typename F>
int VTableIndex(F f)
{
    struct VTableCounter
    {
        virtual int Get1() { return 1; }
        virtual int Get2() { return 2; }
        virtual int Get3() { return 3; }
        virtual int Get4() { return 4; }
        // ... more ...
    } vt;

    T* t = reinterpret_cast<T*>(&vt);

    typedef int (T::*GetIndex)(); 
    GetIndex getIndex = (GetIndex)f;
    return (t->*getIndex)();
}

int n = VTableIndex<A>(&A::c); //returns 3


Try this:

#include <boost/preprocessor/repeat.hpp>

struct VtableIndexCalculator
{
    #define VTIC_GET_INDEX(z, i, d) virtual int GetIndex_ ## i() { return i; }
    BOOST_PP_REPEAT(128, VTIC_GET_INDEX, unused); 
    #undef VTIC_GET_INDEX
};

template <class C, typename F> int GetVtableIndex(C& object, F function)
{
    static VtableIndexCalculator calculator; 
    static void** vtable_new = *(void***)&calculator; 

    void*** pvptr = (void***)&object; 
    void** vtable_old = *pvptr; 
    *pvptr = vtable_new; 

    typedef int (C::*GetIndex)(); 
    GetIndex getIndex = (GetIndex)function;
    int index = (object.*getIndex)();

    *pvptr = vtable_old; 
    return index; 
}

A drawback is that to get the vtable index of a C::F function, you need an actual instance of C (with this version it needs to be a non-const instance, but I think a const version could be created), so it won't work with abstract classes.

Also, I just did a few quick tests, and it seems to work so far, but I'm not sure if it always does (might depend on what kind of inheritance is used with C, if it has any inherited virtual functions or all virtuals are declared in C, maybe even whether you enable incremental linking or not), so be careful with it.

What this does is:

  • Create a helper class called VtableIndexCalculator that has a number of GetIndex_n() functions, each returning the corresponding n.
  • Take an instance if C (object), and a pointer to a member function (called function).
  • Patch object's vptr so that it point at the vtable of VtableIndexCalculator.
  • Cast function into a different function pointer that matches the signature of GetIndex() functions in VtableIndexCalculator.
  • Call the resulting pointer-to-member on object.
  • What happens now (at least in theory) is that a member function gets called the same way (offset calculations, virtual thunks, whatever) as a normal C::F() call would be done, except for return value and arguments (these come from the casted pointer-to-member-function type), plus that the vtable entry #n that originally pointed to C::F now points to VtableIndexCalculator::GetIndex_n.
  • So the result of that call is the vtable index of C::F.
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜