Есть пара классов.
class IObject { public : IObject (void ) {}; virtual ~IObject ( void ) {}; }; class TestCall : public IObject { private : int m_i; float m_f; public : TestCall ( void ) { m_i = 999; m_f = 1.7777f; } virtual ~TestCall ( void ) {} public : int __stdcall test_call_method ( int i, float f ) { this->m_i = i; this->m_f = f; return m_i; } }; /// int main ( int, char *[] ) { TestCall test_call; IObject *test_call_obj = &test_call; void *v_func = NULL; union { int ( __stdcall TestCall::*cFunc ) ( int, float ); void *voidFunc; }; cFunc = ( &TestCall::test_call_method ); v_func = voidFunc; for ( int i = 0; i < 10000; i++ ) { union { int ( __stdcall IObject::*Method )( int, float ); /// Смущает это преобразование. Всегда ли это будет работать? void *void_func; }; void_func = v_func; int ret = ( ( test_call_obj )->* ( Method ) ) ( i, 7.999f ); std::cout << " call method \"test_call_method\" ret = " << ret << std::endl; } return 0; }
Смущает это преобразование int ( CALLSPEC Core::IObject::*Method )( int, float ); . Всегда ли это будет работать?
Yotce
> Смущает это преобразование int ( CALLSPEC Core::IObject::*Method )( int, float ); . Всегда ли это будет работать?
По закону это вообще не работает.
Размер указателя на метод класса в общем случае не равен размеру указателя на void или функцию.
Нельзя приводить или алиасить в union.
RPGman
> По закону это вообще не работает.
По какому закону это не работает, если это рабочий пример?
Ух у тебя и "рабочий пример".
Для начала:
main.cpp:5:10: Constructor cannot be declared 'virtual'
И еще предупреждение:
Calling convention '__stdcall' ignored for this target это на
int __stdcall test_call_method (int i, float f )
PS: это в Xcode
Yotce
> По какому закону это не работает
Закон тут один - ISO/IEC 14882.
> если это рабочий пример?
Это НЕ рабочий пример. Если он у тебя не упал, то только по какому-то счастливому стечению обстоятельств.
Попробуй для проверки занулить юнион мемсетом перед тем, как делать "void_func = v_func" в теле цикла.
Ну и принтфни sizeof'ы для cFunc и voidFunc, чисто поржать.
Примерно: http://ideone.com/QdKjqD
Yotce
Тут полностью разжёвано: http://www.parashift.com/c++-faq/pointers-to-members.html
Yotce
Открой для себя Listeners
А что ты вообще хотел добиться? Передача функции член класса как указатель? Тогда тебе в направлении std::function если С++11, иначе поищи "С++ делегаты".
Ну а альтернативный вариант уже сказали Listeners.
Sergio
> Для начала:
>
> main.cpp:5:10: Constructor cannot be declared 'virtual'
Опечатка вышла должно быть
/// #include <map> #include <string> #include <fstream> #include <iostream> class IObject { public : IObject ( void ) {} virtual ~IObject ( void ) {} }; /// class TestCall : public IObject { private : int m_i; float m_f; public : TestCall ( void ) { m_i = 999; m_f = 1.7777f; } virtual ~TestCall ( void ) {} public : int __stdcall test_call_method ( int i, float f ) { this->m_i = i; this->m_f = f; return m_i; } int get_i ( void ) { return m_i ; } }; /// int main ( int argc, char *argv[] ) { TestCall test_call; IObject *test_call_obj = &test_call; void *v_func = 0; union { int ( __stdcall TestCall::*cFunc ) ( int, float ); void *voidFunc; }; memset( &cFunc, 0, sizeof ( cFunc ) ); cFunc = ( &TestCall::test_call_method ); v_func = voidFunc; for ( int i = 0; i < 10000; i++ ) { union { int ( __stdcall IObject::*Method )( int, float ); void *void_func; }; memset( &Method, 0, sizeof ( Method ) ); void_func = v_func; int ret = ( ( test_call_obj )->* ( Method ) ) ( i, 7.999f ); std::cout << " call method \"test_call_method\" ret = " << ret << " get_i = " << test_call.get_i() << std::endl; } return 0; }
RPGman
> Тут полностью разжёвано:
> http://www.parashift.com/c++-faq/pointers-to-members.html
int f(char x, float y);
int g(char x, float y);
typedef int(*FunctPtr)(char,float);
int callit(FunctPtr p, char x, float y)
{
return p(x, y);
}
int main()
{
FunctPtr p = f;
void* p2 = (void*)p; // ← illegal!!
callit(p, 'x', 3.14f); // okay
callit(FunctPtr(p2), 'x', 3.14f); // might fail!!
...
}
а так действительно делать нельзя потому, что здесь указатель на функцию приводится к void *
в этом случаи берётся адрес указателя
union
{
int ( __stdcall IObject::*Method )( int, float );
void *void_func;
};
memset( &Method, 0, sizeof ( Method ) );
void_func = v_func;
это равносильно записи вида
{
char *test_str = "TEST\0";
union{
char **buf;
void *addr;
}; buf = &test_str;
void *p = addr;
union{
char **bf;
void *adr;
}; adr = p;
std::cout << *bf << std::endl;
}
Можно ещё с jitasm или asmjit повеселиться.
Я юзаю jitasm.
Yotce
> typedef int(*FunctPtr)(char,float);
> int callit(FunctPtr p, char x, float y)
Это не в тему, это указатели на функции, а не на методы класса.
> в этом случаи берётся адрес указателя
...
> это равносильно записи вида
Твой синтетический случай слишком тривиальный, поэтому некорректное поведение замаскировано стечением обстоятельств.
Получай демонстрацию бага в более реалистичном случае:
http://ideone.com/wUDisI
Этого достаточно, чтобы больше не кастовать указатели на методы в указатели на void?
Если мой пример тебя не удовлетворяет, замени 0 в своём memset на i (нам же "всё-равно", что там было в указателе на метод, да?),
получишь не то что левые данные, а вообще крашнешься.
Методы stdcall не бывают же.
RPGman
> Получай демонстрацию бага в более реалистичном случае:
Баги я тоже писать могу, И править их...
struct Base1 { int val; Base1(): val( 1) {} virtual ~Base1( ) {} /// добавим virtual virtual int method1( ) /// на всякий случай virtual { // обязано всегда выводить 1 printf( "Base1 method, %d\n", val); return val; } }; struct Base2 { int val; Base2( ): val( 2) {} virtual ~Base2( ) {} /// добавим virtual virtual int method2( ) /// на всякий случай virtual { // обязано всегда выводить 2 printf( "Base2 method, %d\n", val); return val; } }; struct Foo: public Base1, Base2 { int val; Foo( ) : val( 3) { } virtual ~Foo ( ) {} }; int main ( int argc, char *argv[] ) { // класс Foo a; Base1 *base_1 = &a; Base2 *base_2 = &a; { union { int ( Base2::*cFunc ) ( ); /// Вить у нас множественное наследование!!! void *voidFunc; }; // берём метод 2 cFunc = &Base2::method2; // читаем "как бы" указатель void* void_func = voidFunc; // чистим юнион memset( &cFunc, 0, sizeof cFunc); voidFunc = void_func; // зовем a.method2() // должно вывести "base2 method, 2" ( base_2->*cFunc)( ); // по факту выводит "base2 method, 2" } { // делаем по твоему union { int ( Base1::*cFunc ) ( ); /// Вить у нас множественное наследование!!! void *voidFunc; }; // берём метод 1 cFunc = &Base1::method1; // читаем "как бы" указатель void* void_func = voidFunc; // чистим юнион memset( &cFunc, 0, sizeof cFunc); voidFunc = void_func; // зовем a.method2() // должно вывести "base1 method, 1" ( base_1->*cFunc)( ); // по факту выводит "base1 method, 1" } return 0; }
Yotce
> Баги я тоже писать могу,
Ты и написал
>И править их...
Нет. Ты берёшь и подстраиваешь ситуацию под устраивающий тебя вариант UB.
> int (Base2::*cFunc ) (); /// Вить у нас множественное наследование!!!
Ну и что, что множественное?
Foo наследует эту базу, а значит этот метод можно звать для объекта.
Пример на ideone показывает, что это работает.
>/// на всякий случай virtual
Не надо мне виртульных методов, особенно "на всякий случай". Зачем платить за то, что я не использую?
> Base2 *base_2 = &a;
И не надо читить, вызывая методы не на самом объекте, а на кастах к конкретной базе.
Это всего-лишь маскировка некорректного поведения, рассчитанная на определённую реализацию указателя на метод, и работающая только при определённых условиях.
Ты в юнионе потерял инфу о классе, потом вручную её "восстановил", зная заранее у какой базы был взят метод.
Представь, что у Foo есть метод, принимающий на вход указатель на один из собственных методов. Типа void Foo::dispatch(int (Foo::*cb)()) { (this->*cb)(); }
Как тут будешь химичить?
> memset(&cFunc, 0, sizeof cFunc);
Я же уже тебе предложил для теста вписать там не ноль. Почему игнорируешь неудобные вопросы?
RPGman
> Представь, что у Foo есть метод, принимающий на вход указатель на один из
> собственных методов. Типа void Foo::dispatch(int (Foo::*cb)()) { (this->*cb)();
> }
> Как тут будешь химичить?
Что тут химичить? берем и пишем
RPGman
> > memset(&cFunc, 0, sizeof cFunc);
> Я же уже тебе предложил для теста вписать там не ноль. Почему игнорируешь
> неудобные вопросы?
/// По поводу memcpy { char *test_str = "TEST\0"; union{ char **buf; void *addr; }; buf = &test_str; memset (buf, 0, sizeof( buf ) ); void *p = addr; union{ char **bf; void *adr; }; adr = p; printf ( " This = NULL %p \n", *bf ); if ( *bf != NULL ) { printf ( " This != NULL %s \n", *bf ); } /// Можно memset ( buf, 10, sizeof( buf ) ); тогда поменяем адрес test_str = 0x0a0a0a0a0a }
Не совсем понятно почему оно не падает когда memset вызывается
Тема в архиве.