Войти
ПрограммированиеФорумОбщее

Как добыть указатель на виртуальный метод класса?

#0
20:58, 26 дек. 2009

Нужно получить указатель на _реализацию_ виртуального метода. Самый обычный, который можно будет скормить ассемблерной команде call.
Например, имеем вот такю иерархию:

class Animal {
public:
    virtual void sound() { printf( "Animal::sound : not implemented\n" ); }
};

class Dog : public Animal {
public:
    virtual void sound() { printf( "Dog::sound : woof!\n" ); }
};

При одиночном наследовании можно создать экземпляр класса и получить указатель вот таким корявеньким методом:

void* virtualMethod( byte *object, int index )
{
  void *vtable = (*( byte** )object);
  void *method = (( byte** )vtable)[index];

  return method;
}

При большом кол-ве методов подход очень гемороен, т.к. надо вычислять index вручную, с виртуальным наследованием вобще ужас... да и не хочется создавать экземпляры класса.
Можно ещё вот так, но это не сработает для виртуальных методов:

void ( Dog::*pointer )( void ) = &Dog::sound; // Дебагер сообщит, что в pointer [thunk]:Dog::`vcall'{0,{flat}}'}' :((

Есть идеи?

#1
21:07, 26 дек. 2009

Спец
Нельзя.
Дело в том, что указатели на виртуальные методы (в частности в МСВЦ) могут иметь размер от 4 до 12 байт (в зависимости от виртуальности и множественности наследования) и простому коллу ты их так просто не скормишь. Ты лучше напиши, что ты хочешь сделать.

#2
21:13, 26 дек. 2009
template<class T>
void CallSound (void *ptr)
{
 ((T *)ptr)->T::sound ();
}

void (*profit) (void *) = CallSound<Dog>;
#3
21:19, 26 дек. 2009

Wraith
> Нельзя.
Печально, а я так надеялся, что прийдет добрый волшебних и скажет чо-то типа "засунь указатель в ecx и сделай jmp" =(((((
Как же тогда биндятся виртуальные методы в других скрипт-движках? LUA например... Классы адаптеры? Так не хочется этого делать =(

> Ты лучше напиши, что ты хочешь сделать.
Та у меня давно валялся велосипед в виде скрипт-движка(классы, наследование, виртуальные ф-ции, всякие примочки), решил вот допилить его и сделать биндинг удобный. Сделал регистрацию методов типа RegisterClassMethod( "Animal", "void", "sound", classMethod( Animal, sound ) ) (короче аля AngelScript)
А с виртуальными методами вот такая загвоздка вышла =(((

Sbtrn. Devil
О-о, кавайненько, щас подумаю как это можно заюзать :)

#4
21:25, 26 дек. 2009

ну наверное callback

вроде как-то через это

union Uni
{
  void* p1;
  void (Dog::*p2)();
};
#5
21:25, 26 дек. 2009

Спец
А как тогда в AS это сделано? Или он не умеет виртуальные фигачить?

#6
22:12, 26 дек. 2009

Спец
> О-о, кавайненько, щас подумаю как это можно заюзать :)
Почитайте чего-нибудь про функторы ( например у Александреску ). Кавайнее некуда:)

#7
23:42, 26 дек. 2009

Wraith
> А как тогда в AS это сделано? Или он не умеет виртуальные фигачить?
Та думаю должен как-то, но я не нашел

Duplex
> ну наверное callback
Merrewend
> Почитайте чего-нибудь про функторы ( например у Александреску ). Кавайнее
> некуда:)
Та знаю я про функторы и колбэки, но щас они мне не помогут:)

Sbtrn. Devil пнул меня в нужном направлении, вот чо намозговал:

// ** Конвертирует в void*
template< typename method >
void* getPointer( method m )
{
  byte *pointer[ POINTER_SIZE ];
  memcpy( pointer, &m, POINTER_SIZE );
  return *( byte** )pointer;
}

// ** Ф-ция-переходник
template< class T, typename ret, typename arg0, ret ( T::*method )( arg0 ) >
ret thunk( void *object, arg0 _arg0 )
{
  return ((T*)object->*method)( _arg0 );
}

#define  virtualMethod1( c, r, m, arg0 ) getPointer( &GenThunk<c, r, arg0, &c::m> )

// ** Ну и на конец указатель, через который можно потом дернуть метод из асма
void *pointer = virtualMethod1( cSceneObject, void, SetTransform, const cMatrix4& );

Всем спасибо :)

#8
0:13, 27 дек. 2009

Спец
>// ** Ну и на конец указатель, через который можно потом дернуть метод из асма
Т.е. в баню виртуальность метода, тип объекта в момент биндинга, причем в компайл-тайм, будет жестко фиксирован?
В чем польза?

#9
1:00, 27 дек. 2009

Спец
Опять эта виртуальщина и низкоуровневое шаманство :)  Да-да, ещё и РТТИ свой...

Посмотри в Linderdaum, мы там забиндили всё, что только можно - статические методы, виртуальные методы и т.п. Всё, естественно, для скрипта - чтобы свои классы наследовать от с++-ных.  Биндинг ровно по такой же схеме, как у тебя в посте 7 - собственно, по-другому (без "фарша" темплейтного) никак.

#10
1:17, 27 дек. 2009

RPGman
> Т.е. в баню виртуальность метода, тип объекта в момент биндинга, причем в
> компайл-тайм, будет жестко фиксирован?
Указатели потом кладутся в виртуальные таблицы скриптовых типов и пошло поехало, так что виртуальность сохраняется :)

Vinil
Ога, меня пока все устраивает :)) Я теперь тоже забиндил все что можно... ну или почти всё =)
А в ваши сорцы гляну.

#11
9:06, 27 дек. 2009

Спец
Там для такого

template< class T, typename ret, typename arg0, ret ( T::*method )( arg0 ) >
и генерилка есть, если что. Легко допиливается под свой формат и названия методов. MethodGen она там называется, по-моему. На любое кол-во параметров, с убиранием модификаторов const и т.п.

#12
3:03, 5 янв. 2010

Спец
А зачем из асма метод-то дёргать?

Прошло более 4 лет
#13
17:40, 6 янв. 2014

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

ПрограммированиеФорумОбщее

Тема в архиве.