Events

quetzalcoatl

budowa.gif

Strona w budowie
Ktoś pracuje nad tą stroną, jej zawartość może się wkrótce zmienić. Prosimy o cierpliwość!
Byłbym bardzo wdziędzny o nie-edytowanie tego artykułu, tylko zgłaszanie uwag w wątku: http://4programmers.net/Forum/viewtopic.php?id=121925

Natchnięty wiosennym promykiem słońca, postanowiłem sprawdzić, czy dało by się uzyskać coś na kształt eventów i eventhandlerów (wykraczających poza funkcje niezwiązane i metody statyczne, przyjmujacych metody od roznych klas, z parametrami lub bez)

Ponizej aktualna postac mojego radosnego wymyslu. Obsluguje eventhandlery z klas zwyklych i polimorficznych, oraz do trzech argumentow eventa.

Wybaczcie forme.. jesli nie wykryje w tym zadnego razacego uchybienia, wrzuce to na jakies repozytorium pozniej.

Najprostszy przyklad uzycia to plik basic_test.cpp. Wydaje mi sie w miare intuicyjny. Jedyna uwaga: cyferka w konkretyzacji szablonu oznacza ilosc argumentow eveta. Od strony uzytkowej, wszystko sprowadza sie do uzywania klasy Event oraz funkcji makehandler.

W przykladzie (bedacym jednoczenie testem:) ) klasa RAT definiuje dwa Eventy, jeden bez, drugi z prostym parametrem. Klasa Observer zawiera kilka metod, ktorych mozna uzyc jako handlerow reagujacych na wystapienie (Event::fire()) zdarzenia. W funkcji basic_test tworzone sa przykladowe obiekty i pokazane jest jak spinac (i odpinac) handlery do(od) eventow.

Klasa ktorej metody mialy by byc uzyte jako handlery, musi miec dolaczonego mix-in'a MEventHandlerOwner
..innymi slowy, musi dziedziczyc (publicznie!) po MEventHandlerOwner<nazwa_mojej_klasy>.
Wymog ten trudno mi jest w tej chwili wytlumaczyc.. dosc powiedziec, ze ze wzgledu na problemy z funkcjami virtualnymi (zwlaszcza pure virtual, patrz virtual_test) oraz skladowanem i rzutowaniem wskaznikow-na-te-metody, okazalo sie konieczne, aby wszystkie klasy dostarczajace handlerow posiadaly vtable i znajdowaly sie w tym samym drzewie klas.

PS. kod sprawdzany pod VisualStudio2005 i g++ version 3.3.6. Na g++ jest czysto w trybach -Wall i -ansi, niestety na -pedantic nie mialem juz nerwow :)

MEventHandlerOwner.h

#ifndef _MEVENTHANDLEROWNER_H
#define _MEVENTHANDLEROWNER_H

class MEventHandlerOwnerBase
{public:
        virtual ~MEventHandlerOwnerBase(){}             //ensure all MEHOwners have a vtable
};

template<typename T>
class MEventHandlerOwner : public MEventHandlerOwnerBase
{
public:
        template<typename T2>
        void invoke(void (MEventHandlerOwnerBase::*const member)()){(dynamic_cast<T2*>(static_cast<T*>(this))->*member)();}

        template<typename T2, typename A1>
        void invoke(void (MEventHandlerOwnerBase::*const member)(A1), A1 a1){(dynamic_cast<T2*>(static_cast<T*>(this))->*member)(a1);}

        template<typename T2, typename A1, typename A2>
        void invoke(void (MEventHandlerOwnerBase::*const member)(A1, A2), A1 a1, A2 a2){(dynamic_cast<T2*>(static_cast<T*>(this))->*member)(a1, a2);}

        template<typename T2, typename A1, typename A2, typename A3>
        void invoke(void (MEventHandlerOwnerBase::*const member)(A1, A2, A3), A1 a1, A2 a2, A3 a3){(dynamic_cast<T2*>(static_cast<T*>(this))->*member)(a1, a2, a3);}
};

#endif //_MEVENTHANDLEROWNER_H

EventHandlerBase.h

#ifndef _EVENTHANDLERBASE_H
#define _EVENTHANDLERBASE_H

#include "MEventHandlerOwner.h"

template<int N, typename A1 = void, typename A2 = void, typename A3 = void, typename LAST_AND_UNUSED = void>
class EventHandlerBase;

template<>
class EventHandlerBase<0>
{
protected:
        MEventHandlerOwnerBase* const target;
        void (MEventHandlerOwnerBase::* const method)();
public:
        EventHandlerBase(MEventHandlerOwnerBase* const object, void (MEventHandlerOwnerBase::* const member)()):target(object),method(member){}
    virtual void invoke()=0;
};

template<typename A1>
class EventHandlerBase<1,A1>
{
protected:
        MEventHandlerOwnerBase* const target;
        void (MEventHandlerOwnerBase::* const method)(A1);
public:
        EventHandlerBase(MEventHandlerOwnerBase* const object, void (MEventHandlerOwnerBase::* const member)(A1)):target(object),method(member){}
    virtual void invoke(A1 a1)=0;
};

template<typename A1, typename A2>
class EventHandlerBase<2,A1,A2>
{
protected:
        MEventHandlerOwnerBase* const target;
        void (MEventHandlerOwnerBase::* const method)(A1, A2);
public:
        EventHandlerBase(MEventHandlerOwnerBase* const object, void (MEventHandlerOwnerBase::* const member)(A1, A2)):target(object),method(member){}
    virtual void invoke(A1 a1, A2 a2)=0;
};

template<typename A1, typename A2, typename A3>
class EventHandlerBase<3,A1,A2,A3>
{
protected:
        MEventHandlerOwnerBase* const target;
        void (MEventHandlerOwnerBase::* const method)(A1, A2, A3);
public:
        EventHandlerBase(MEventHandlerOwnerBase* const object, void (MEventHandlerOwnerBase::* const member)(A1, A2, A3)):target(object),method(member){}
    virtual void invoke(A1 a1, A2 a2, A3 a3)=0;
};

#endif //_EVENTHANDLERBASE_H

EventHandler.h

#ifndef _EVENTHANDLER_H
#define _EVENTHANDLER_H

#include "EventHandlerBase.h"

template<typename T, typename A1 = void, typename A2 = void, typename A3 = void, typename LAST_AND_UNUSED = void>
struct EventHandler;


template<typename T>
struct EventHandler<T> : EventHandlerBase<0>
{   EventHandler(T* const object, void(MEventHandlerOwnerBase::* const member)() ):EventHandlerBase<0>(object,member){}
        void invoke(){static_cast<T*>(target)->template invoke<T>(method);}
};

template<typename T>
boost::shared_ptr<EventHandlerBase<0> > make_handler(T& object, void (T::* const member)() )
{   return boost::shared_ptr<EventHandlerBase<0> >(new EventHandler<T>(&object, static_cast<void(MEventHandlerOwnerBase::*const)()>(member)));
}


template<typename T, typename A1>
struct EventHandler<T, A1> : EventHandlerBase<1, A1>
{   EventHandler(T* const object, void(MEventHandlerOwnerBase::* const member)(A1) ):EventHandlerBase<1, A1>(object,member){}
        void invoke(A1 a1){static_cast<T*>(target)->template invoke<T, A1>(method, a1);}
};

template<typename T, typename A1>
boost::shared_ptr<EventHandlerBase<1, A1> > make_handler(T& object, void (T::* const member)(A1) )
{   return boost::shared_ptr<EventHandlerBase<1, A1> >(new EventHandler<T, A1>(&object, static_cast<void(MEventHandlerOwnerBase::*const)(A1)>(member)));
}


template<typename T, typename A1, typename A2>
struct EventHandler<T, A1, A2> : EventHandlerBase<2, A1, A2>
{   EventHandler(T* const object, void(MEventHandlerOwnerBase::* const member)(A1, A2) ):EventHandlerBase<2, A1, A2>(object,member){}
        void invoke(A1 a1, A2 a2){static_cast<T*>(target)->template invoke<T, A1, A2>(method, a1, a2);}
};

template<typename T, typename A1, typename A2>
boost::shared_ptr<EventHandlerBase<2, A1, A2> > make_handler(T& object, void (T::* const member)(A1, A2) )
{   return boost::shared_ptr<EventHandlerBase<2, A1, A2> >(new EventHandler<T, A1, A2>(&object, static_cast<void(MEventHandlerOwnerBase::*const)(A1, A2)>(member)));
}


template<typename T, typename A1, typename A2, typename A3>
struct EventHandler<T, A1, A2, A3> : EventHandlerBase<3, A1, A2, A3>
{   EventHandler(T* const object, void(MEventHandlerOwnerBase::* const member)(A1, A2, A3) ):EventHandlerBase<3, A1, A2, A3>(object,member){}
        void invoke(A1 a1, A2 a2, A3 a3){static_cast<T*>(target)->template invoke<T, A1, A2, A3>(method, a1, a2, a3);}
};

template<typename T, typename A1, typename A2, typename A3>
boost::shared_ptr<EventHandlerBase<3, A1, A2, A3> > make_handler(T& object, void (T::* const member)(A1, A2, A3) )
{   return boost::shared_ptr<EventHandlerBase<3, A1, A2, A3> >(new EventHandler<T, A1, A2, A3>(&object, static_cast<void(MEventHandlerOwnerBase::*const)(A1, A2, A3)>(member)));
}


#endif //_EVENTHANDLER_H

EventBase.h

#ifndef _EVENTBASE_H
#define _EVENTBASE_H

#include <list>
#include <boost/smart_ptr.hpp>

#include "EventHandler.h"

template<int N, typename A1 = void, typename A2 = void, typename A3 = void>
struct EventBase : protected std::list<boost::shared_ptr<EventHandlerBase<N, A1, A2, A3> > >
{
public:
        typedef EventHandlerBase<N, A1, A2, A3> element;
        typedef boost::shared_ptr<element> safepointer;
        typedef std::list<safepointer> innerlist;

        using innerlist::push_back;
        using innerlist::push_front;
        using innerlist::remove;

        safepointer pop_back()
        {       safepointer tmp = back();
                innerlist::pop_back();
                return tmp;
        }
        safepointer pop_front()
        {       safepointer tmp = front();
                innerlist::pop_front();
                return tmp;
        }
};

#endif //_EVENTBASE_H

Event.h

#ifndef _EVENT_H
#define _EVENT_H

#include "EventBase.h"

template<int N, typename A1 = void, typename A2 = void, typename A3 = void, typename LAST_AND_UNUSED = void>
struct Event;

template<>
struct Event<0> : EventBase<0>
{
        void fire() const
        {       innerlist::const_iterator _it = begin(), _end = end();
                while(_it!=_end)
                        (*_it++)->invoke();
        }
};

template<typename A1>
struct Event<1, A1> : EventBase<1, A1>
{
        void fire(A1 a1) const
        {   typename innerlist::const_iterator _it = begin(), _end = end();
                while(_it!=_end)
                        (*_it++)->invoke(a1);
        }
};

template<typename A1, typename A2>
struct Event<2, A1, A2> : EventBase<2, A1, A2>
{
        void fire(A1 a1, A2 a2) const
        {       typename innerlist::const_iterator _it = begin(), _end = end();
                while(_it!=_end)
                        (*_it++)->invoke(a1, a2);
        }
};

template<typename A1, typename A2, typename A3>
struct Event<3, A1, A2, A3> : EventBase<3, A1, A2, A3>
{
        void fire(A1 a1, A2 a2, A3 a3) const
        {       typename innerlist::const_iterator _it = begin(), _end = end();
                while(_it!=_end)
                        (*_it++)->invoke(a1, a2, a3);
        }
};

#endif //_EVENT_H

main.cpp

#include <iostream>
#include <string>

#include "tests.h"

int main()
{       basic_test();
        virtual_test();
        copyargs_test();

#ifdef VERBOSE
        std::cin.get();
#endif
}

tests.h

#ifndef _TESTS_H
#define _TESTS_H

#define VERBOSE

void basic_test();
void virtual_test();
void copyargs_test();

#endif  //_TESTS_H

basic_test.cpp

#include <string>

#include "Event.h"

#include "tests.h"
#ifdef VERBOSE
#include <iostream>
#endif

class Rat
{
public:
        Event<1, std::string const&> before_method;
        Event<0> after_method;

        void method()
        {       std::string nonemptystring = "rat";
                before_method.fire(nonemptystring);
#ifdef VERBOSE
                std::cout << "Rat squeaks" << std::endl;
#endif
                after_method.fire();
        }
};

class Observer : public MEventHandlerOwner<Observer>
{
    std::string name;
public:
        Observer(char const* str):name(str),sit_called(false),jump_called(false),param_passed(false){}
        void sit(std::string const & onwhat)
        {
#ifdef VERBOSE
                std::cout << name << " sits on " << onwhat << std::endl;
#endif
                param_passed = !onwhat.empty();
                sit_called = true;
        }
    void jump()
        {
#ifdef VERBOSE
                std::cout << name << " screams" << std::endl;
#endif
                jump_called=true;
        }
        bool sit_called;
        bool jump_called;
        bool param_passed;
};


void basic_test()
{
        Rat rat;
        Observer woman("Jane");

        //non-virtual member function test
        rat.before_method.push_back(make_handler(woman, &Observer::sit));
        rat.after_method.push_back(make_handler(woman, &Observer::jump));
    rat.method();

        assert(woman.sit_called);
        assert(woman.jump_called);
        assert(woman.param_passed);
}

virtual_test.cpp

#include <string>

#include "Event.h"

#include "tests.h"
#ifdef VERBOSE
#include <iostream>
#endif

class Rat
{
public:
        Event<1, std::string const&> before_method;
        Event<0> after_method;

        void method()
        {       std::string nonemptystring = "rat";
                before_method.fire(nonemptystring);
#ifdef VERBOSE
                std::cout << "Rat squeaks" << std::endl;
#endif
                after_method.fire();
        }
};

class Observer_base : public MEventHandlerOwner<Observer_base>
{
protected:
    std::string name;
public:
    Observer_base(char const* str):name(str),sit_called(false),jump_called(false),param_passed(false){}
    virtual void sit(std::string const & onwhat)=0;
    virtual void jump()=0;
        bool sit_called;
        bool jump_called;
        bool param_passed;
};

class Observer_derived : public Observer_base
{
public:
    Observer_derived(char const* str):Observer_base(str){}
        void sit(std::string const & onwhat)
        {
#ifdef VERBOSE
                std::cout << name << " sits on " << onwhat << std::endl;
#endif
                param_passed = !onwhat.empty();
                sit_called = true;
        }
    void jump()
        {
#ifdef VERBOSE
                std::cout << name << " screams" << std::endl;
#endif
                jump_called=true;
        }
};


void virtual_test()
{
        Rat rat;
        Observer_derived woman("Mary");

        //virtual member function test
        rat.before_method.push_back(make_handler(woman, &Observer_derived::sit));
        rat.after_method.push_back(make_handler(woman, &Observer_derived::jump));
    rat.method();
        rat.before_method.pop_back();
        rat.after_method.pop_back();

        assert(woman.sit_called);
        assert(woman.jump_called);
        assert(woman.param_passed);
}

copyargs_test.cpp

#include "Event.h"

#include "tests.h"

#ifdef VERBOSE
#include <iostream>
#endif

struct CopyWatch
{
        static unsigned long copycount;
        CopyWatch()
        {
#ifdef VERBOSE
                std::cout << "Created" << std::endl;
#endif
        }
        CopyWatch(CopyWatch const&)
        {       ++copycount;
#ifdef VERBOSE
                std::cout << "Copied" << std::endl;
#endif
        }
};

class CopyWatchActor
{public:
        Event<3, CopyWatch const&, CopyWatch const&, CopyWatch const&> copy_test_event;

        void copy_test()
        {       CopyWatch arg;
                copy_test_event.fire(arg, arg, arg);
        }
};

class CopyWatchListener : public MEventHandlerOwner<CopyWatchListener>
{public:
        void test(CopyWatch const&, CopyWatch const&, CopyWatch const&){};
};

unsigned long CopyWatch::copycount = 0;

void copyargs_test()
{
        //are the const-ref arguments passed by reference correctly?
        CopyWatchActor a;
        CopyWatchListener x;
        a.copy_test_event.push_back(make_handler(x, &CopyWatchListener::test));
        a.copy_test();

        assert( CopyWatch::copycount == 0);
}

0 komentarzy