Аннотация :
Так уж исторически сложилось, что в языке С++ нет событий. Событием (event) является исходящий вызов (программисты на VB хорошо знакомы с ними) и в С++ их действительно нет. Иногда события путают с сообщениями (message), но это не верно. Сообщение это прямой вызов: например windows вызывает оконную процедуру для передачи собщения окну. Объект (система) вызывает функцию обькта(окна). Вызов происходит от объекта к объекту. В отличии от сообщения событие имеет другую механику. Объект инициирует событие и вызываются все объекты-обработчики. Т.е. от одного объекта к нескольким. Причем объект инициатор события может ничего не «знать» об его обработчиках, поэтому событие называют исходящим вызовом.
Раз уж в С++ события на уровне языка не поддерживаются, значит стоит организовать их на уровне библиотеки. Здесь приведена реализация такой библиотеки. В ней есть два класса signal и slot.
Вы приводите указатель на функцию-член класса клиента
к уназателю на функцию из конкрентного класса (slot::Thunk),
это для некоторых классов может быть невозможно,
ошибка компилятора, что-то типа "указатели имеют разную природу",
наблюдатась для WTL проекта, я в свое время не стал
углубляться, удалось обойтись
Кстати эта проблема нашла отражение в FLTK (библиотека типа WTL/Qt, etc., http://www.fltk.org) — там все события вызывают статические функции
с пареметром-указателем this:
В C++ указатели на функцию-член не всегда просто
адрес функции, нельзя приводить указатель на функцию одного
класса к указателю на функцию другого.
Однако возможно есть один способ:
Проблема здесь в том, что VC++ может не понять, что
(call<&foo::f>) означает, что надо сгенерировать
функцию и взять указатель на нее, ну и конечно
как изменить Ваш пакет — как известно удобство
важнее всего.
Интересно как это сделано в boost.
Возможно в свете .Net на такие проблемы можно не смотреть. ;-)
в Boost есть реализация подобного
интересна тем, что:
также является шаблонным классом
слот может реагировать на несколько сигналов
сигнал вызывает объект с перегруженным оператором (), т.е. не обязателен отдельный объект типа слот...
можно передавать не только объект-слот, но и просто указатель на функцию и работать будет стем же успехом...
так что, конечно неплохо, но та реализация, IMHO, лучше...
Сильно !
От:
Аноним
Дата:
05.02.03 10:40
Оценка:
Не хуже, чем в QT ихние эвенты.И не надо макросов гопницких
Я в свое время пробовал использовать указатели на функцию в качестве аргументов шаблонов, но оказалось что в VC++ с этим совсем тухло. Получал internal compiler error и прочие странности.
Проблемму с разнородностью указателей можно решить если сделать класс slot шаблонным т.е:
не совсем корректный вопрос...
там несколько другой подход...
есть объект signal;
есть объект-обработчик, т.е. то, что мы коннектим к signal
в реализации(внутри) он, конечно, signal создает объект slot, но это происходит прозрачно для пользователя,
т.е. для Вас есть только signal и объект-обработчик (и никаких slot)
объекту signal передается объект-обработчик
но даже если передается адрес функции это соединение тоже можно отключить, а именно:
при создание связи "signal"-"slot" возвращает объект connection, при этом есть два варианта: соединение живет до уничтожения объекта connection или дольше, рекоммендую взглянуть в реализацию boost::signals:
{
scoped_connection c = sig.connect(hello);
}// соединение убъется при выходе из блока
{
connection c = sig.connect(hello);
}// соединение не убъется при выходе из блока
// присваивать к connection, конечно, не обязательно, но в этом случае
// разрыв произойдет только при уничтожениии sig
// при этом объект-обработчик может быть назначен различным сигналам
Очевидно что при таком подходе связь будет разорвана только при уничтожении источника события(сервера) если обработчик события (клиент) будет уничтожен раньше програма долго не проживет. В реальной жизни клиент как правило уничтожается раньше сервера. Пример документ(сервер) клиент (вид) сначала уничтожаются виды потом документ. Поэтому важно разрывать связь в любом случае, что здесь и сделано (см. код ;) )
А иначе прийдется ручками разрывать :)
Эта реализация тоже позволяет. Добавляется несколько несколько slot-ов в класс. Можно для каждого слота по функции обработчику или одну на всех (если тип аргумента сиглала совпадает)
class MyClass {
slot onSignal0, onSignaд1, .... // до бесконечности
}
но как раз дело в том, что для каждого сигнала требуется свой обработчик, т.е. получается
придется для варианта сдвумя сигналами и одним обработчиком писать след. конструкцию:
т.к. хендлеры выстраиваются в двусвязный список, и передачя одного и тогоже хендлера потянет за собой другие в цепочке, т.е. цепочки двух сигналов склеятся :-)
IMHO так, если есть другой способ поправте меня...
т.е. учитывая что оба сигнала не знают друг о друге, а хендлеры передаються им...
Здравствуйте, yaroslav_v, Вы писали:
_>Вы приводите указатель на функцию-член класса клиента _>к уназателю на функцию из конкрентного класса (slot::Thunk), _>это для некоторых классов может быть невозможно, _>ошибка компилятора, что-то типа "указатели имеют разную природу", _>наблюдатась для WTL проекта, я в свое время не стал _>углубляться, удалось обойтись
Ошибку на такое приведение может выдать только некорректный компилятор. Такое приведение всегда доложно прекрасно выполняться при помощи 'reinterpret_cast'.
_>В C++ указатели на функцию-член не всегда просто _>адрес функции, нельзя приводить указатель на функцию одного _>класса к указателю на функцию другого.
Указатель на функцию одного класса в С++ можно приводить к указателю на функцию любого другого класса при помощи 'reinterpret_cast'. Портабельное применение такого приведения типа ограничивается толкро тем, что рано или поздно придется сделать обратное преобразование и, согласно спецификации языка, при этом получится исходное значение. Именно на этом принципе построен данный код. Прямое преобразование делается в при инициализации слота, обратное — при вызове функции.