![]() |
Callback - Опять в раздумьях
Тема уже была, но хочется свежего вгляда. Если можно, технические аргументы (в смысле не так, что в такой-то библиотеке так-то, а почему так лучше).
Как лучше сделать callback? 1. через виртуальные классы. Экономит память, работает быстро, но много писанины. Код:
class callback_t {public virtual void fire(...) = 0;} Код:
typedef void (Object::*callback)(...); Код:
virtual void Object1::invoke(unsigned id, ...) |
Ничего не могу сказать, кроме того, что я однозначно против таблиц диспечерезации. Сам использовал только первый вариант, потому что понятие callback в первый раз встретил именно с подбной реализацией. Если раздражает много писать (и параметры всё равно у всех одинаковые), то можно написать например такой шаблон:
Код:
template <typename T> struct member_callback : callback Да и макросы не зря были придуманы... |
\me - Через виртуальные классы.
но вообще-то я в основном QT юзаю, а там проблема решена уже |
Ivank, это получается вариант №2, потому что использует указатель на метод. Я так пробовал. Оптимизация не помогает. А вот с макросами что-то не подумал. Это мысль!
Hasherfrog, я бы не заморачивался, имея бесплатный QT под виндами (хотя там сделано вариантом №3). Как всегда, ответили самые уважаемые мной люди. Спасибо. Попробую с define-ом. |
Вот жеж :] Столкнулся с очень похожей задачей (уже решил, но второпях, абы работало, "горю"; вот хочу поговорить "за красоту решения")
1) Есть класс А, сложно-навороченный. Можифицировать его невозможно. Он периодически генерирует некое событие, назовём event. 2) Есть класс В (который можно модифицировать, это "наш" класс), который должен отслеживать event и корректировать собственные переменные в соответствии с новыми данными от event. 3) Класс А не имеет внешних сношений с миром кроме как через класс С, который имеет конструктор С::С(А) и виртуальный метод onEvent(). Собственно, С::onEvent() вызывается классом А, сразу после event'а. Модифицировать С нельзя, но можно унаследовать. В общем, не сложно (очень надеюсь, что смог объяснить). Задача: Сделать так, чтобы В тоже получал управление сразу после event'а. Решение сейчас такое: 1) Делаем класс D : public C, в котором переписываем конструктор D::D(A, B*p) : C(A), m_p(p){}; и callback, т.е. D::onEvent() { if(m_p) m_p->onEvent() }; 2) Добавляем callback в класс В (т.е. дописываем B::onEvent(){...}) и заводим в нём переменную D* m_pD = new D(C *pC, this). Теперь скользкий момент. Возникает потребность дописать ещё несколько классов E,F,G... (неизвестно, сколько их будет, в принципе два, но предположим, что их число неограниченно). Они, эти классы, тоже хотят получать event(). Значит, им тоже будет нужен "переписанный" D. Как же оформить D? Сейчас у меня сделано "тупо". Класс D имеет два конструктора D::D(A, B*p) и D::D(A, E*p) и соответственно два члена B*m_pB и E*m_pE. Callback вызывается для того того, который был проинициализирован в конструкторе. Но вот если количество классов будет расти, Вариант 1) написать шаблон tD и использовать {class E; tD<E>cbE; } перед объявлением E,F..., а внутри E использовать cbE (в общем, что-то вроде как у ivank'а) Вариант 2) придётся засовывать в класс D всё новые и новые указатели и fjrward-объявления классов E,F..., а внутри E использовать D Вариант 3) не знаю, ещё чего-нибудь... Как думаете? P.S. QT есть, но классы А и В не имеют слотов/сигналов, они не QObject'ы. Вообще хотелось бы без QT :]]]]]]]]]] P.P.S. pva QT сейчас 4-й грядёт, правда, мы пока не спешим переходить :] |
hasherfrog
Я чего-то не догоняю. Что мешает пронаследовать всех получателей (C, E, F итд) от общего предка и в конструкторе D принимать указатель на этот интерфейс, не заботясь о том, кто его реализует? |
ivank
В том-то всё и дело, что не хочется так делать. Некрасиво как-то. Совершенно разные классы, созданные для совершенно разных целей - и вдруг общий предок только для решения такой маленькой фишечки, как поддержка callback'а на event. Это, в принципе, вопрос "психологии и личных вкусов" :-/ >> всех получателей (C, E, F итд) С - нельзя. "Модифицировать С нельзя, ..." Получается, что мы все наши классы B, D, E... наследуем от С. Сторонний программист (который когда-нибудь возможно будет модифицировать и наш код) увидит конструкцию class B : public C, ещё, и ещё... где B - огромный класс, а С - малюсенький и спросит: а на фига? Но проблема не только в этом, не только в "личных пристрастиях" и "заботе" о других разработчиков. Класс С (который не смодифицируешь) обязательно требует в конструкторе ссылку на А. Но что, если мне нужно привязать класс В (унаследованный теперь уже от С, а значит, требующий А в памяти) "на ходу"? Т.е. иметь возможность то "прицепится" к event, то "отцепится"? Или "на ходу" прицепится к одному экземпляру А в памяти, а то к другому? Понимаете? |
Я немного выше перепутал B и C. Латинские буквы жутко неудобны.
Раз у нас B, E, F, G итд получают одинаковое сообщение, то логично предположить, что в этом аспекте у них одинаковый интерфейс. Почему бы эту одинаковость не вынести в отдельный абстрактный класс? Т.е. схема такая: У нас есть класс I (от слова интерфейс), с функцией onEvent(A &sender). От него наследуются B и компания. D наследуется от C, в конструкторе получает указатель на A и I. Кто этот I реализует - B, E, F или кто ещё ему всё равно. В своём onEvent он вызывает onEvent из I. Всё вроде просто. Если бы можно было менять A я бы вообще просто убрал класс C (и D, соответственно), т.к. они только плодят сущности с моей точки зрения. Другое дело, если события можно привязывать к разным методам. Тогда я не вижу альтернативы написанию нескольких классов вручную/шаблонно/темплейтно, типа того, который я привёл выше. pva Жаль параметром шаблона нельзя сделать отдельный метод, всё могло бы быть значительно красивше. |
>> Всё вроде просто.
Да, надо подумать; может, так и сделаю. Thanks. Выглядеть только будет своеобразно, по jav'овски как-то :] |
вот так у меня выглядит редактор табличек:
Код:
class ListModel |
Время: 02:46. |
Время: 02:46.
© OSzone.net 2001-