Events
quetzalcoatl
Ktoś pracuje nad tą stroną, jej zawartość może się wkrótce zmienić. Prosimy o cierpliwość!
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);
}