Тут я уже был
http://www.gamedev.ru/faq/?id=34
но мне нужно сделать следующее.
есть например функция SetTimer, у которой последним параметром передается указатель на функцию.
Мне нужно передать указатель на функцию класса.
Я понимаю что вызвать метод без самого класса нельзя, но если экземпляр класса существует, то ведь теоретически можно получить адрес метода этого экземпляра и вызвать его?
а как это реализовать?
class test { public: ... VOID CALLBACK TimerProc(HWND hwnd, UINT uMsg, UINT idEvent, DWORD dwTime) { ... } void set( ) { SetTimer( hWnd, 0, 1000, TimerProc); //вот как тут передать указатель на TimerProc } };
P. S.
только не предлагайте объявить TimerProc как static
Я делал так
SimpleAlarm.h
#ifndef __SIMPLE_ALARM_H__ #define __SIMPLE_ALARM_H__ #include "defines.h" class SimpleAlarm { friend void CALLBACK TimerCallback( HWND, UINT, UINT_PTR, DWORD ); UINT_PTR id; public: SimpleAlarm() : id( 0 ) {}; ~SimpleAlarm() { CancelAlarm(); } DWORD SetAlarm( UINT interval ); void CancelAlarm(); virtual void Alarm() = 0; }; #endif // __SIMPLE_ALARM_H__
SimpleAlarm.срр
#include <windows.h> #include <list> #include "CriticalSection.h" #include "SimpleAlarm.h" using namespace std; list<SimpleAlarm*> alarms; CriticalSection lock; void CALLBACK TimerCallback( HWND, UINT, UINT_PTR id, DWORD ) { SimpleAlarm *alarm = NULL; lock.Enter(); for( list<SimpleAlarm*>::iterator i = alarms.begin(); i != alarms.end(); ++i ) if( (*i)->id == id ) { alarm = *i; alarms.erase( i ); alarm->id = 0; break; } lock.Leave(); if( alarm ) alarm->Alarm(); } DWORD SimpleAlarm::SetAlarm( UINT interval ) { if( id = SetTimer( NULL, 0, interval, (TIMERPROC)TimerCallback )) { lock.Enter(); alarms.push_back( this ); lock.Leave(); return 0; } return GetLastError(); } void SimpleAlarm::CancelAlarm() { if( id ) { KillTimer( NULL, id ); lock.Enter(); alarms.remove( this ); lock.Leave(); id = 0; } }
KyberAlex
но ведь если TimerProc сделать friend, то она будет как и static (не будет доступа к членам класса)
GLEB
А тебе и не нужно из TimerProc никакого доступа ))
Она едмнственно что делает - получает из глобальной таблицы класс, соответсвующий ID сработавшего таймера, и вызывает его виртуальную функцию Alarm, которую ты должен переопредлить в своем классе, унаследованном от SimpleAlarm.
Тоесть TimerProc - это имплементация... твой код про нее вообще знать не должен, с его точки зрения сообщение от сработавшего таймера оказыается сразу в Alarm
KyberAlex
>А тебе и не нужно из TimerProc никакого доступа ))
TimerProc это для примера, мне нужен доступ
GLEB
"Штатным " способом ты его не получишь...
Тебе по-любому надо протащить в глобальную функцию this-указатель твоего класса. Если Callback ф-ция позволяет передать юзeрский параметр (как например для потоков), то удобней загнать его туда... А иначе - извращаться с таблицами соответствия this <-> ID... Или исползовать другую специфику момента ( напр SetWindowLong для окон )
KyberAlex
Вот как раз SetWindowLong
допустим есть класс
CMyButton { public: void *prev_proc; HWND wnd; // новая WndProc для кнопки /*static*/ LRESULT CALLBACK NewWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { return CallWindowProc( ( WNDPROC)prev_proc, hWnd, message, wParam, lParam); } CMyButton( HWND parent) // созадем кнопку { wnd = CreateWindow( "Button", "Button1", ... // теперь мне нужно мненить оконную процедуру prev_proc = ( void *)SetWindowLong( wnd, GWL_WNDPROC,( LONG)NewWndProc); } };
вот как тут быть?
если NewWndProc как член класса, то в SetWindowLong ее не передать
если как static, то CallWindowProc не имеет доступа к prev_proc
user pointer тоже передать нельзя
GLEB
prev_proc = (void *)SetWindowLong(wnd, GWL_WNDPROC,(LONG)this);
а в глобальной ф-ции окна
CMyButton *but_this = (CMyButton *)GetWindowLong(wnd, GWL_WNDPROC);
but_this->NewWndProc( hWnd, message, wParam, lParam);
----------------------------
**правка Set - Get**
KyberAlex
Точно! Спасибо, только сделал немного по другому, NewWndProc как static, а указатель на класс связвал с кнопкой (SetWindowLong(wnd, GWL_USERDATА, (LONG)this));
GLEB
Ну да... я общую идею скинул... а вариантов деталей реализации - туева хуча )))
Совтеую кстати, "программирование графики дла Windows" под. ред. Фень Юань.
Дельная книжка, где все эти штуки как раз раскуриваются... и примеры внятные...
GLEB
Еще вариант использовать функторы. Реализация в библиотеке Loki. Темплейт обобщенного функтора позволит инкапсулировать и экземляр и адрес метода этого экземпляра и вызывать в любом месте проги.
Пример
#include <Loki/Functor.h> using namespace Loki; typedef pair<USHORT, Functor<>> pairFunc; typedef map<USHORT, Functor<>> mapFunc; class c3DSFormatLoader { private: mapFunc mapFunсtions; .... }; c3DSFormatLoader::c3DSFormatLoader() { mapFunсtions.insert( pairFunc( C3DS_CHUNK_AMBIENT /*ключ*/, Functor<> ( this, &c3DSFormatLoader::ReadMaterialParts) /*значение*/ ) ); } // вызов функтора iterFunc = mapFunctions.find( nChunkID); if( iterFunc != iterFuncEnd) iterFunc->second( );
GLEB
Еще можешь использовать Boost.Function.
http://www.boost.org/doc/libs/1_36_0/doc/html/function/tutorial.html#id2903708
_vasa_
Ni3
Но в данном контексте это ведь не применить?? Или я чего то не понимаю.
Ну вот есть WIN API функция которой надо CALLBACK функцию определенного прототипа например SetWindowLong( ..., GWL_WNDPROC, ..).
Как туда передать функтор?
gexogen
В контексте примера, приведенного автором, не применить, да. Это так, на случай, если автору понадобится реализовывать подобные коллбэки в своем собственном коде.
GLEB
SetWindowLong не используй вообще, особенно для передачи указателей. Она deprecated. В частности, в Win64 ее нет. Сейчас вместо нее используется функция SetWindowLongPtr.
gexogen
>есть WIN API функция которой надо CALLBACK функцию определенного прототипа например SetWindowLong( ..., GWL_WNDPROC, ..). Как туда передать функтор?
К LONG придется привести указатель на функтор, а потом восстановить.
class cBase3DApplication // класс приложения { public: void CreateDisplay(); // создаем окно static LRESULT CALLBACK StaticWndProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam); // глобальная функция, this ей не передается void MyFunc(); ... }; // где-то иниц. функтор Functor<> fn (this, &cBase3DApplication::MyFunc); void cBase3DApplication :: CreateDisplay() { WNDCLASSEX wc; wc.style = CS_BYTEALIGNCLIENT | ...; wc.lpfnWndProc = StaticWndProc; RegisterClassEx(&wc); ... hAppWnd = CreateWindow(..., (LPVOID) &fn); } LRESULT CALLBACK cBase3DApplication :: StaticWndProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam) { if(Message == WM_CREATE) SetWindowLong(hWnd, GWL_USERDATA, (LONG)((CREATESTRUCT FAR*) lParam)->lpCreateParams) ); // можно создать функтор, который будет вмещать функцию с параметрами и возвр. значением не void Functor<> *pFunc = (Functor<> *) GetWindowLong(hWnd, GWL_USERDATA); (*pFunc)(); // вызов cBase3DApplication :: MyFunc(); return DefWindowProc(hWnd, Message, wParam, lParam); }// LRESULT CALLBACK cBase3DApplication :: StaticWndProc()
Тема в архиве.