Демонстрационный проект - events.zip
Так уж исторически сложилось, что в языке С++ нет событий. Событием (event) является исходящий вызов (программисты на VB хорошо знакомы с ними) и в С++ их действительно нет. Иногда события путают с сообщениями (message), но это не верно. Сообщение это прямой вызов: например windows вызывает оконную процедуру для передачи сообщения окну. Объект (система) вызывает функцию объекта (окна). Вызов происходит от объекта к объекту. В отличии от сообщения событие имеет другую механику. Объект инициирует событие и вызываются все объекты-обработчики. Т.е. от одного объекта к нескольким. Причем объект инициатор события может ничего не «знать» об его обработчиках, поэтому событие называют исходящим вызовом.
Раз уж в С++ события на уровне языка не поддерживаются, значит стоит организовать их на уровне библиотеки. Здесь приведена реализация такой библиотеки. В ней есть два класса signal и slot.
Итак, чтобы сделать какой нибудь класс источником события поместите в него переменную типа signal:
struct EventRaiser { // источник события signal<void> someEvent; // void - тип аргумента события }; |
А чтобы сделать класс обработчиком поместите в него переменную типа slot, функцию обработчик и свяжите slot с обработчиком:
struct EventHandler { // обработчик события slot someHandler; // переходник void onEvent( void ) { // функция обработчик события printf( "event handled" ); } void connect ( EventRaiser &er ) { someHandler.init( er.someEvent, onEvent, this ); // установим связь события с обработчиком } }; |
Так как эти объекты являются частью своих хозяев, не нужно заботится о времени жизни связи. Ее разрыв произойдет во время разрушения одного из них. Событие же инициируется вызвовом метода signal::raise:
struct EventRaiser { // источник события signal<void> someEvent; // void - тип аргумента события void someFunc() { someEvent.raise(); // инициация события } }; |
В примере создаются два класса обработчик и инициатор события, устанавливается связь между ними и иллюстрируется обработка события в нескольких объектах одновременно:
#include "stdafx.h" #include "sigslot.h" struct EventRaiser { // источник события signal<const char*> event; // const char* - тип аргумента. может быть void void raise( const char *eventName ) { printf( "raising %s event\n", eventName ); event.raise( eventName ); } } g_Raiser; // глобальный объект struct EventHandler { // обработчик события const char *color; slot handler; // переходник void onEvent( const char *eventName ) { // обработчик события printf( "\t%s event handled in %s object\n", eventName, color ); } EventHandler( const char *clr ) : color(clr) { handler.init( g_Raiser.event, onEvent, this ); // установим связь } }; int main(int argc, _TCHAR* argv[]) { EventHandler red("Red"); g_Raiser.raise( "Small" ); // событие обработается в red { { EventHandler blue("Blue"); g_Raiser.raise( "Big" ); // событие обработается в red и blue } EventHandler green("Green"); g_Raiser.raise( "Medium" ); // событие обработается в red и green. // объект blue уничтожен, связь разорвана } return 0; } |
signal – cобытие (детали реализации опущены)
template <class Arg> // Arg - тип аргумента функции обработчика class signal { public: // Инициировать событие void raise( Arg arg // Арумент arg будет передан в обработчики события ); }; |
slot - переходник для обработки события в классе-обработчике (детали реализации опущены)
class slot { public: // установить связь с событием и обработчиком template < class Owner, // класс-обработчик class Arg // Тип аргумента события. > void init( signal<Arg> &sig, // событие void (Owner::*mpfn)(Arg), // функция обработчик Owner *This // обьект обработчик ); // установить связь с событием и обработчиком для случая signal<void> template < class Owner // класс-обработчик > void init( signal<void> &sig, // событие void (Owner::*mpfn)(), // функция обработчик Owner *This // обьект обработчик ); // разорвать связь void clear(); }; |
Весь код находится в файле
sigslot.h:#ifndef _SIGSLOT_h_ #define _SIGSLOT_h_ // sigslot.h - autor Kluev Alexander <kluev@pragmaworks.com> template <class Arg> class signal; class slot { friend class signal_base; slot *_prev; slot *_next; struct Thunk {}; typedef void (Thunk::*Func)(); Thunk *_trg; Func _mfn; public: slot() : _trg(0), _mfn(0), _prev(0), _next(0) {} ~slot() { clear(); } public: void clear() { if ( _next ) _next->_prev = _prev; if ( _prev ) _prev->_next = _next; _prev = _next = 0; } template <class Owner, class Arg> void init( signal<Arg> &sig, void (Owner::*mpfn)(Arg), Owner *This ) { clear(); _trg = (Thunk*)This; _mfn = (Func)mpfn; sig._add( *this ); } template <class Owner> void init( signal<void> &sig, void (Owner::*mpfn)(), Owner *This ) { clear(); _trg = (Thunk*)This; _mfn = (Func)mpfn; sig._add( *this ); } private: template <class Arg> void _call( Arg a ) { typedef void (Thunk::*XFunc)(Arg); XFunc f = (XFunc)_mfn; (_trg->*f)(a); } void _call() { (_trg->*_mfn)(); } }; class signal_base { protected: friend class slot; slot _head; void _add( slot &s ) { s._prev = &_head; s._next = _head._next; if ( _head._next ) _head._next->_prev = &s; _head._next = &s; } template <class Arg> void _raise( Arg a ) { slot *p = _head._next; while ( p ) { p->_call( a ); p = p->_next; } } void _raise() { slot *p = _head._next; while ( p ) { p->_call(); p = p->_next; } } public: ~signal_base() { clear(); } public: void clear() { while ( _head._next ) _head._next->clear(); } }; template <class Arg> class signal : public signal_base { public: void raise( Arg ); }; typedef void VOID; template <> void signal<VOID>::raise() { signal_base::_raise(); } template <class Arg> void signal<Arg>::raise( Arg a ) { signal_base::_raise( a ); } #endif // _SIGSLOT_h_ |