Вызов коллбэка-члена класса из другого класса
От: aalp  
Дата: 22.01.09 08:59
Оценка:
Здравствуйте,

Проблема: хочу вызвать член одного класса через указатель в другом классе, не прибегая к передаче в последний объекта первого.

Пытаюсь реализовать вызов члена контроллера из вьюхи, однако столкнулся с проблемой. Насколько я понял, для вызова из одного класса члена другого необходимо иметь инстанс класса, которому принадлежит вызываемый метод. Можно ли как-то этого избежать? В данном случае, хотелось бы, что бы вьюха ничего не знала про контроллер (кроме типа хэндлера, без этого не типизировать коллбэк).

Или есть более грамотный способ реализовать данное поведение? Использовать в качестве коллбэка дружественную функцию или статический член — не вариант.

class Controller;
class IView;
class View;

typedef void(Controller::* OnEventHandler)();

struct IView
{
    virtual void Exec() = 0;
    virtual void SetHandler(OnEventHandler) = 0;
};

struct Controller
{
public:
    Controller(IView* pView)
    {
        pView->SetHandler(&Controller::Handler);
    }

    void Handler()
    {
        std::cout << "Handler from Controller called" << std::endl;
    }
};

struct View : public IView
{
    OnEventHandler  handler;

    virtual void SetHandler(OnEventHandler callback)
    {
        handler = callback;
    }

    virtual void Exec()
    {
        if(handler)
        {
            std::cout << "Handler exist, calling: ";
            handler();
        }
        else
        {
            std::cout << "Handler does not exist" << std::endl;
        }
    }
};

int main()
{
    IView* view = new View();
    Controller c(view);
    view->Exec();
}
Re: Вызов коллбэка-члена класса из другого класса
От: placement_new  
Дата: 22.01.09 10:10
Оценка:
Здравствуйте, aalp, Вы писали:

A>Здравствуйте,


A>Проблема: хочу вызвать член одного класса через указатель в другом классе, не прибегая к передаче в последний объекта первого.


A>Пытаюсь реализовать вызов члена контроллера из вьюхи, однако столкнулся с проблемой. Насколько я понял, для вызова из одного класса члена другого необходимо иметь инстанс класса, которому принадлежит вызываемый метод. Можно ли как-то этого избежать? В данном случае, хотелось бы, что бы вьюха ничего не знала про контроллер (кроме типа хэндлера, без этого не типизировать коллбэк).


A>Или есть более грамотный способ реализовать данное поведение? Использовать в качестве коллбэка дружественную функцию или статический член — не вариант.


A>
A>class Controller;
A>class IView;
A>class View;

A>typedef void(Controller::* OnEventHandler)();

A>struct IView
A>{
A>    virtual void Exec() = 0;
A>    virtual void SetHandler(OnEventHandler) = 0;
A>};

A>struct Controller
A>{
A>public:
A>    Controller(IView* pView)
A>    {
A>        pView->SetHandler(&Controller::Handler);
A>    }

A>    void Handler()
A>    {
A>        std::cout << "Handler from Controller called" << std::endl;
A>    }
A>};

A>struct View : public IView
A>{
A>    OnEventHandler  handler;

A>    virtual void SetHandler(OnEventHandler callback)
A>    {
A>        handler = callback;
A>    }

A>    virtual void Exec()
A>    {
A>        if(handler)
A>        {
A>            std::cout << "Handler exist, calling: ";
A>            handler();
A>        }
A>        else
A>        {
A>            std::cout << "Handler does not exist" << std::endl;
A>        }
A>    }
A>};

A>int main()
A>{
A>    IView* view = new View();
A>    Controller c(view);
    view->>Exec();
A>}

A>


Заведи в Controller поле boost::function<...>, в main установи его посредством View, и вызывай когда надо.
Или посмотри в строну boost::signal, тоже самое в принципе.
Re[2]: Вызов коллбэка-члена класса из другого класса
От: aalp  
Дата: 22.01.09 10:16
Оценка:
Здравствуйте, placement_new, Вы писали:

....

_>Заведи в Controller поле boost::function<...>, в main установи его посредством View, и вызывай когда надо.

_>Или посмотри в строну boost::signal, тоже самое в принципе.

Хотелось бы обойтись без буста. Код должен собираться на маке, помимо вин32, тащить буст туда по определенным причинам нельзя.
Re: Вызов коллбэка-члена класса из другого класса
От: Tilir Россия http://tilir.livejournal.com
Дата: 22.01.09 10:19
Оценка: +1
Здравствуйте, aalp, Вы писали:

A>Проблема: хочу вызвать член одного класса через указатель в другом классе, не прибегая к передаче в последний объекта первого.


Не получится по следующей причине: любая функция-член класса неявно первым аргументом принимает указатель на объект класса. То есть вызов MyClass.MyFunc() с точки зрения C-программиста это MyFunc(&MyClass); с некоторыми дополнительными ограничениями. Простой указатель на MyClass::MyFunc не сохраняет информацию об объекте класса, поэтому вы должны её каким-то образом предоставить.

Я предлагаю указатель на интерфейс контроллера во вьюхе и вызов по этому указателю. То есть нечто вроде:


#include <iostream>

struct IController;
struct Controller;
struct IView;
struct View;


struct IController
{
    virtual void Handler() = 0;    
};

struct IView
{
    virtual void Exec() = 0;
    virtual void SetHandler(IController*) = 0;
};

struct Controller : public IController
{
public:
    Controller(IView* pView)
    {
        pView->SetHandler(this);
    }

    void Handler()
    {
        std::cout << "Handler from Controller called" << std::endl;
    }
};

struct View : public IView
{
    IController *handler;

    virtual void SetHandler(IController *callback)
    {
        handler = callback;
    }

    virtual void Exec()
    {
        if(handler)
        {
            std::cout << "Handler exist, calling: ";
            handler->Handler();
        }
        else
        {
            std::cout << "Handler does not exist" << std::endl;
        }
    }
};

int main()
{
    IView* view = new View();
    Controller c(view);
    view->Exec();
}
Re[3]: Вызов коллбэка-члена класса из другого класса
От: placement_new  
Дата: 22.01.09 10:22
Оценка:
Здравствуйте, aalp, Вы писали:

A>Здравствуйте, placement_new, Вы писали:


A>....


_>>Заведи в Controller поле boost::function<...>, в main установи его посредством View, и вызывай когда надо.

_>>Или посмотри в строну boost::signal, тоже самое в принципе.

A>Хотелось бы обойтись без буста. Код должен собираться на маке, помимо вин32, тащить буст туда по определенным причинам нельзя.

Ну тогда можно обоитись указателем на член-ф-ию, что и есть по сути boost::function.
В принципе так же как у тебя, с тем лишь отличием что сделать метод SetHandler(<указатель на член IView>), а не в конструктор вьюшку передавать. А "менее зависимо" мне кажется не получится.
Re: Вызов коллбэка-члена класса из другого класса
От: Аноним  
Дата: 22.01.09 10:34
Оценка:
без буста я делал так:
class ICallback
{
public:
    virtual void OnCallback(int par) = 0;
};
 

template <class IT>  class CallbackImpl : public ICallback
{
public:
    typedef void (IT::*OnCallbackType)(int par);
    CallbackImpl(IT *it, OnCallbackType ptr) 
        :_it(it), _ptr(ptr)
    {
    }
    
protected:
    virtual void OnCallback(int par)
    {
        ((*_it).*(_ptr)) (par);
    }
    
private:
    OnCallbackType _ptr;
    IT *_it;
};

#define CALLBACK_IMPL(SelfClassName, CallbackMethod) (&CallbackImpl<SelfClassName> (this, &SelfClassName::CallbackMethod))

 class A
 {
     public:
         void CallIt(ICallback *it)
         {
             it->OnCallback(0);
         }
 };

 class B
 {
     void FooCallback1(int par)
     {
         printf("FooCallback1(%u)\n", par);
     }
     void FooCallback2(int par)
     {
         printf("FooCallback2(%u)\n", par);
     }
    public:
        
        void CallFooViaA()
        {
            A a;
            a.CallIt(CALLBACK_IMPL(B, FooCallback1));
            a.CallIt(CALLBACK_IMPL(B, FooCallback2));
        }
 };
 
 
 void _tmain(int argc, wchar_t **argv)
 {
     B b;
     b.CallFooViaA();
 }
Re: Вызов коллбэка-члена класса из другого класса
От: Flamer Кипр http://users.livejournal.com/_flamer_/
Дата: 22.01.09 10:35
Оценка:
Здравствуйте, aalp, Вы писали:

A>Проблема: хочу вызвать член одного класса через указатель в другом классе, не прибегая к передаче в последний объекта первого.


Singleton с reference counting?


class C1 : public Singleton<C1>
{
public:
 void OnEvent();
};

class C2
{
void CallOnEvent()
{
 C1* c = C1::Instance();
    c->OnEvent();
 c->FreeInstance();
}
};


Примерно так, как вариант. Но не подходит для задач, где экземпляров C1 может быть больше 1.
Re: Вызов коллбэка-члена класса из другого класса
От: alexey_ma Израиль  
Дата: 22.01.09 10:41
Оценка:
Здравствуйте, aalp, Вы писали:

Ну если буст не хотите то можете попробовать это
http://rsdn.ru/article/cpp/cxx_events.xml
Автор(ы): Алексндр Клюев
Дата: 29.01.2003

Техника в приципе известная, с помощью классов переходников.
Re[3]: Вызов коллбэка-члена класса из другого класса
От: Аноним  
Дата: 22.01.09 13:23
Оценка:
Здравствуйте, aalp, Вы писали:

A>Здравствуйте, placement_new, Вы писали:


A>....


_>>Заведи в Controller поле boost::function<...>, в main установи его посредством View, и вызывай когда надо.

_>>Или посмотри в строну boost::signal, тоже самое в принципе.

A>Хотелось бы обойтись без буста. Код должен собираться на маке, помимо вин32, тащить буст туда по определенным причинам нельзя.


на мой взгляд, есть 2 варианта:

1) сделать колбек не указателем а структурой (возможно с разделенными интерфейсом и реализацией) и в поле структуры хранить указатель на создавший колбек объект.

2) использовать компиляторы поддерживающие tr1/functional (std::tr1::function копия boost::function), менее велосипедно.
под винду это умеет 2008я студия, пол линукс последние версии gcc, под мак наверняка тоже что-то есть.
Re: Вызов коллбэка-члена класса из другого класса
От: Кодёнок  
Дата: 22.01.09 13:37
Оценка: +1
Здравствуйте, aalp, Вы писали:

A>Или есть более грамотный способ реализовать данное поведение? Использовать в качестве коллбэка дружественную функцию или статический член — не вариант.


По-моему у тебя ответ под носом.

class Controller;
class IView;
class View;

struct IEventHandler 
{ 
    virtual void Handler() = 0;
};

struct IView
{
    virtual void Exec() = 0;
    virtual void SetHandler(IEventHandler*) = 0;
};

struct Controller
{
public:
    Controller(IView* pView)
    {
        pView->SetHandler(this);
    }

    /*override*/ void Handler()
    {
        std::cout << "Handler from Controller called" << std::endl;
    }
};

struct View : public IView
{
    IEventHandler* handler;

    /*override*/ void SetHandler(IEventHandler* h)
    {
        handler = h;
    }

    /*override*/ void Exec()
    {
        if (handler)
        {
            std::cout << "Handler exist, calling: ";
            handler->Handler();
        }
        else
        {
            std::cout << "Handler does not exist" << std::endl;
        }
    }
};

int main()
{
    IView* view = new View();
    Controller c(view);
    view->Exec();
}


P.S. На маке есть полноценный буст.
Re[3]: Вызов коллбэка-члена класса из другого класса
От: _cb_  
Дата: 22.01.09 14:18
Оценка:
без буста можно воспользоваться произведением местного автора

http://www.rsdn.ru/Forum/?mid=1661731
Автор: MaximE
Дата: 06.02.06


или написать что-то подобное самому...
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.