Определить индекс / порядковый номер vtable конкретной виртуальной функции (с использованием Visual C ++) - PullRequest
3 голосов
/ 12 апреля 2011

В дополнение к моему вопросу: Определение смещения виртуальной таблицы определенной виртуальной функции (с использованием Visual C ++) :

Дано:

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

Как я могу написать функцию в Visual C ++ (возможно, непереносимую), такую ​​что:

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

Причины, по которым я хотел бы сделать это, в связанном вопросе.

Ответы [ 3 ]

7 голосов
/ 18 апреля 2011

Вдохновлен ответом imre, если вы просто хотите узнать общий размер виртуальной таблицы;то есть, сколько виртуальных функций, это может работать.Этот метод не создает экземпляр класса.

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();
}

Примечание. Я не использовал Boost.PP, потому что он ограничен 256, как я могу сказать.Вы должны иметь возможность использовать макросы vim или что-то еще, чтобы получить произвольное количество виртуальных машин.Однако при использовании множественного или виртуального наследования он, вероятно, выдаст неверные значения.

7 голосов
/ 18 апреля 2011

Вдохновленный ответом Имре и Ивана, я думаю, что это можно полностью решить:

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
4 голосов
/ 16 апреля 2011

Попробуйте это:

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

Недостатком является то, что для получения vtable-индекса функции C :: F вам необходим фактический экземпляр C (в этой версии он должен быть неэкземпляр const, но я думаю, что можно создать версию const), поэтому она не будет работать с абстрактными классами.

Кроме того, я только что провел несколько быстрых тестов, и похоже, что это работает, но я не уверен, что так будет всегда (может зависеть от того, какой тип наследования используется с C, если он имеетлюбые унаследованные виртуальные функции или все виртуальные функции объявлены в C, возможно, даже если вы включаете инкрементное связывание или нет), так что будьте осторожны с этим.

Что это делает:

  • Создайте вспомогательный класс с именем VtableIndexCalculator, который имеет несколько функций GetIndex_n (), каждая из которых возвращает соответствующий n.
  • Возьмите экземпляр, если C (object), и указатель на функцию-член (называемую function).
  • Патч object vptr, чтобы он указывал на vtable VtableIndexCalculator.
  • Приведите function в другой указатель на функцию, который соответствует сигнатуре функций GetIndex () в VtableIndexCalculator.
  • Вызвать получившийся указатель на член на object.
  • Что происходит сейчас (по крайней мере, теоретически), так это то, что функция-член вызывается так же (вычисления смещения, виртуальные преобразователи и т. Д.), Как и обычный вызов C :: F (), за исключениемвозвращаемое значение и аргументы (они получены из приведенного типа указатель на функцию-член), плюс то, что запись vtable #n, которая первоначально указывала на C :: F, теперь указывает на VtableIndexCalculator :: GetIndex_n.
  • Таким образом, результатом этого вызова является индекс vtable C :: F.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...