Сообщений 25    Оценка 161 [+1/-0]         Оценить  
Система Orphus

Обработка событий в С++

Автор: Алексндр Клюев
Pragmaworks group
Опубликовано: 29.01.2003
Исправлено: 13.03.2005
Версия текста: 1.0.1

Введение
Пример
Краткое описание классов
Исходный код

Демонстрационный проект - 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:


Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав.
    Сообщений 25    Оценка 161 [+1/-0]         Оценить