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

С++ указатель на метод

Страницы: 1 2 Следующая »
#0
20:23, 5 сен 2008

Тут я уже был

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

#1
20:32, 5 сен 2008

Я делал так

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;
    }
}
#2
20:40, 5 сен 2008

KyberAlex
но ведь если TimerProc сделать friend, то она будет как и static (не будет доступа к членам класса)

#3
20:44, 5 сен 2008

GLEB

А тебе и не нужно из TimerProc никакого доступа )) 

Она едмнственно что делает - получает из глобальной таблицы класс, соответсвующий ID сработавшего таймера,  и вызывает его виртуальную функцию Alarm, которую ты должен переопредлить в своем классе, унаследованном от SimpleAlarm.

Тоесть TimerProc - это имплементация... твой код про нее вообще знать не должен, с его точки зрения сообщение от сработавшего таймера оказыается сразу в Alarm

#4
21:26, 5 сен 2008

KyberAlex
>А тебе и не нужно из TimerProc никакого доступа ))

TimerProc  это для примера, мне нужен доступ

#5
21:30, 5 сен 2008

GLEB

"Штатным " способом ты его не получишь... 
Тебе по-любому надо протащить  в глобальную функцию  this-указатель твоего класса.  Если Callback ф-ция позволяет передать юзeрский параметр (как например для потоков), то удобней загнать его туда...  А иначе - извращаться с таблицами соответствия this <-> ID...  Или исползовать другую специфику момента ( напр SetWindowLong для окон )

#6
21:45, 5 сен 2008

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 тоже передать нельзя

#7
21:52, 5 сен 2008

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**

#8
22:05, 5 сен 2008

KyberAlex
Точно! Спасибо, только сделал немного по другому, NewWndProc как static,  а указатель на класс связвал с кнопкой (SetWindowLong(wnd, GWL_USERDATА, (LONG)this));

#9
22:09, 5 сен 2008

GLEB
Ну да... я общую идею скинул... а вариантов деталей реализации - туева хуча )))

Совтеую кстати, "программирование графики дла Windows"  под. ред. Фень Юань. 
Дельная книжка, где все эти штуки как раз раскуриваются... и примеры внятные...

#10
20:08, 6 сен 2008

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();
#11
20:48, 6 сен 2008

GLEB
Еще можешь использовать Boost.Function.
http://www.boost.org/doc/libs/1_36_0/doc/html/function/tutorial.html#id2903708

#12
8:25, 7 сен 2008

_vasa_
Ni3
Но в данном контексте это ведь не применить?? Или я чего то не понимаю.
Ну вот есть WIN API функция которой надо CALLBACK функцию определенного прототипа например SetWindowLong( ..., GWL_WNDPROC, ..).
Как туда передать функтор?

#13
8:53, 7 сен 2008

gexogen
В контексте примера, приведенного автором, не применить, да. Это так, на случай, если автору понадобится реализовывать подобные коллбэки в своем собственном коде.

GLEB
SetWindowLong не используй вообще, особенно для передачи указателей. Она deprecated. В частности, в Win64 ее нет. Сейчас вместо нее используется функция SetWindowLongPtr.

#14
9:49, 7 сен 2008

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()
Страницы: 1 2 Следующая »
ПрограммированиеФорумОбщее

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