- Любовные романы
- Фантастика и фэнтези
- Ненаучная фантастика
- Ироническое фэнтези
- Научная Фантастика
- Фэнтези
- Ужасы и Мистика
- Боевая фантастика
- Альтернативная история
- Космическая фантастика
- Попаданцы
- Юмористическая фантастика
- Героическая фантастика
- Детективная фантастика
- Социально-психологическая
- Боевое фэнтези
- Русское фэнтези
- Киберпанк
- Романтическая фантастика
- Городская фантастика
- Технофэнтези
- Мистика
- Разная фантастика
- Иностранное фэнтези
- Историческое фэнтези
- LitRPG
- Эпическая фантастика
- Зарубежная фантастика
- Городское фентези
- Космоопера
- Разное фэнтези
- Книги магов
- Любовное фэнтези
- Постапокалипсис
- Бизнес
- Историческая фантастика
- Социально-философская фантастика
- Сказочная фантастика
- Стимпанк
- Романтическое фэнтези
- Ироническая фантастика
- Детективы и Триллеры
- Проза
- Юмор
- Феерия
- Новелла
- Русская классическая проза
- Современная проза
- Повести
- Контркультура
- Русская современная проза
- Историческая проза
- Проза
- Классическая проза
- Советская классическая проза
- О войне
- Зарубежная современная проза
- Рассказы
- Зарубежная классика
- Очерки
- Антисоветская литература
- Магический реализм
- Разное
- Сентиментальная проза
- Афоризмы
- Эссе
- Эпистолярная проза
- Семейный роман/Семейная сага
- Поэзия, Драматургия
- Приключения
- Детская литература
- Загадки
- Книга-игра
- Детская проза
- Детские приключения
- Сказка
- Прочая детская литература
- Детская фантастика
- Детские стихи
- Детская образовательная литература
- Детские остросюжетные
- Учебная литература
- Зарубежные детские книги
- Детский фольклор
- Буквари
- Книги для подростков
- Школьные учебники
- Внеклассное чтение
- Книги для дошкольников
- Детская познавательная и развивающая литература
- Детские детективы
- Домоводство, Дом и семья
- Юмор
- Документальные книги
- Бизнес
- Работа с клиентами
- Тайм-менеджмент
- Кадровый менеджмент
- Экономика
- Менеджмент и кадры
- Управление, подбор персонала
- О бизнесе популярно
- Интернет-бизнес
- Личные финансы
- Делопроизводство, офис
- Маркетинг, PR, реклама
- Поиск работы
- Бизнес
- Банковское дело
- Малый бизнес
- Ценные бумаги и инвестиции
- Краткое содержание
- Бухучет и аудит
- Ораторское искусство / риторика
- Корпоративная культура, бизнес
- Финансы
- Государственное и муниципальное управление
- Менеджмент
- Зарубежная деловая литература
- Продажи
- Переговоры
- Личная эффективность
- Торговля
- Научные и научно-популярные книги
- Биофизика
- География
- Экология
- Биохимия
- Рефераты
- Культурология
- Техническая литература
- История
- Психология
- Медицина
- Прочая научная литература
- Юриспруденция
- Биология
- Политика
- Литературоведение
- Религиоведение
- Научпоп
- Психология, личное
- Математика
- Психотерапия
- Социология
- Воспитание детей, педагогика
- Языкознание
- Беременность, ожидание детей
- Транспорт, военная техника
- Детская психология
- Науки: разное
- Педагогика
- Зарубежная психология
- Иностранные языки
- Филология
- Радиотехника
- Деловая литература
- Физика
- Альтернативная медицина
- Химия
- Государство и право
- Обществознание
- Образовательная литература
- Учебники
- Зоология
- Архитектура
- Науки о космосе
- Ботаника
- Астрология
- Ветеринария
- История Европы
- География
- Зарубежная публицистика
- О животных
- Шпаргалки
- Разная литература
- Зарубежная литература о культуре и искусстве
- Пословицы, поговорки
- Боевые искусства
- Прочее
- Периодические издания
- Фанфик
- Военное
- Цитаты из афоризмов
- Гиды, путеводители
- Литература 19 века
- Зарубежная образовательная литература
- Военная история
- Кино
- Современная литература
- Военная техника, оружие
- Культура и искусство
- Музыка, музыканты
- Газеты и журналы
- Современная зарубежная литература
- Визуальные искусства
- Отраслевые издания
- Шахматы
- Недвижимость
- Великолепные истории
- Музыка, танцы
- Авто и ПДД
- Изобразительное искусство, фотография
- Истории из жизни
- Готические новеллы
- Начинающие авторы
- Спецслужбы
- Подростковая литература
- Зарубежная прикладная литература
- Религия и духовность
- Старинная литература
- Справочная литература
- Компьютеры и Интернет
- Блог
Обработка событий в С++ - Александр Клюев


- Жанр: Компьютеры и Интернет / Программирование
- Название: Обработка событий в С++
- Автор: Александр Клюев
- Возрастные ограничения: (18+) Внимание! Книга может содержать контент только для совершеннолетних. Для несовершеннолетних просмотр данного контента СТРОГО ЗАПРЕЩЕН! Если в книге присутствует наличие пропаганды ЛГБТ и другого, запрещенного контента - просьба написать на почту для удаления материала.
Шрифт:
Интервал:
Закладка:
Александр Клюев
Обработка событий в С++
Введение
Так уж исторически сложилось, что в языке С++ нет событий. Событием (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 eventn", 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 objectn", 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 [email protected]
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_
Комментарии:
Не всегда корректный кодВы приводите указатель на функцию-член класса клиента к указателю на функцию из конкрентного класса (slot::Thunk), это для некоторых классов может быть невозможно, ошибка компилятора, что-то типа "указатели имеют разную природу", наблюдатась для WTL проекта, я в свое время не стал углубляться, удалось обойтись.
Кстати эта проблема нашла отражение в FLTK (библиотека типа WTL/Qt, etc., http://www.fltk.org)/– там все события вызывают статические функции с параметром-указателем this:
static void static_cb(void* v) {
handler* h=(handler*)v;
h->member();
}
В C++ указатели на функцию-член не всегда просто адрес функции, нельзя приводить указатель на функцию одного класса к указателю на функцию другого. Однако возможно есть один способ:
template<class TyClass::*f)()>
void call(TyClass* p_this) {(
p_this->*f)();
}
т.е. сделать обычную функцию с параметром this, параметризованную функцией-членом, а на эту обычную функцию уже хранить указатель.
class foo {
public: void f() {}
};
typedef void (*call_f_type)(void*);
call_f_type call_f=(call_f_type)(call<&foo::f>);
а теперь
foo obj;
call_f(&obj);
Проблема здесь в том, что VC++ может не понять, что (call<&foo::f>) означает, что надо сгенерировать функцию и взять указатель на нее, ну и конечно как изменить Ваш пакет – как известно удобство важнее всего.
