Events
Strona w budowie
Ktoś pracuje nad tą stroną, jej zawartość może się wkrótce zmienić. Prosimy o cierpliwość!
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://forum.4programmers.net/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); }
