Попробуйте это:
#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.