Имя пользователя:
Пароль:
 | Правила  

Компьютерный форум OSzone.net » Программирование, базы данных и автоматизация действий » Программирование и базы данных » Callback - Опять в раздумьях

Ответить
Настройки темы
Callback - Опять в раздумьях
pva pva вне форума

Аватара для pva

Ветеран


Сообщения: 1180
Благодарности: 279

Профиль | Отправить PM | Цитировать


Тема уже была, но хочется свежего вгляда. Если можно, технические аргументы (в смысле не так, что в такой-то библиотеке так-то, а почему так лучше).
Как лучше сделать callback?
1. через виртуальные классы. Экономит память, работает быстро, но много писанины.
Код: Выделить весь код
class callback_t {public virtual void fire(...) = 0;}
class A
{
    struct callback1 : callback {
       A* a;
       callback1(A* a1) : a(a1) {}
       void fire(...) {a->doSomething(...);}
    }; // и так на каждый callback
   ...
};
2. через указатель на метод. Медленней, плохо приводится.
Код: Выделить весь код
typedef void (Object::*callback)(...);
class Object1 : public Object {...}
// трудно привести void (Object1::*callback)(...) к void (Object::*callback)(...), нужен хак.
3. через таблицу ID. Постоянно нужно переписывать таблицу (invoke)
Код: Выделить весь код
virtual void Object1::invoke(unsigned id, ...)
{
    switch (id)
    {
        case 10 : foo(...) break;
        ...
        default: Object::invoke(id, ...);
    }
}
Промойте мне мозги, please

Отправлено: 21:28, 25-05-2006

 

редкий гость


Сообщения: 1696
Благодарности: 44

Профиль | Сайт | Отправить PM | Цитировать


Ничего не могу сказать, кроме того, что я однозначно против таблиц диспечерезации. Сам использовал только первый вариант, потому что понятие callback в первый раз встретил именно с подбной реализацией. Если раздражает много писать (и параметры всё равно у всех одинаковые), то можно написать например такой шаблон:
Код: Выделить весь код
template <typename T> struct member_callback : callback
{
    typedef void (T::*ptr) (/*whatever*/);
    T *obj;
    ptr method;
    member_callback(T *o, ptr m) :
        obj(o),
        method(m)
    {}
    void fire(/*whatever*/)
    {
        (obj->*method)(/*whatever*/);
    }
};
Можно несколько таких шаблонов набросать, под различные параметры и возращаемые значения (тоже как параметры шаблона, разумеется).

Да и макросы не зря были придуманы...

-------
http://ivank.ru

Это сообщение посчитали полезным следующие участники:

Отправлено: 22:13, 25-05-2006 | #2



Для отключения данного рекламного блока вам необходимо зарегистрироваться или войти с учетной записью социальной сети.

Если же вы забыли свой пароль на форуме, то воспользуйтесь данной ссылкой для восстановления пароля.


Аватара для hasherfrog

Старый параноик


Сообщения: 2423
Благодарности: 85

Профиль | Отправить PM | Цитировать


\me - Через виртуальные классы.
но вообще-то я в основном QT юзаю, а там проблема решена уже
Это сообщение посчитали полезным следующие участники:

Отправлено: 11:09, 26-05-2006 | #3

pva pva вне форума Автор темы

Аватара для pva

Ветеран


Сообщения: 1180
Благодарности: 279

Профиль | Отправить PM | Цитировать


Ivank, это получается вариант №2, потому что использует указатель на метод. Я так пробовал. Оптимизация не помогает. А вот с макросами что-то не подумал. Это мысль!
Hasherfrog, я бы не заморачивался, имея бесплатный QT под виндами (хотя там сделано вариантом №3).
Как всегда, ответили самые уважаемые мной люди. Спасибо. Попробую с define-ом.

Отправлено: 22:53, 26-05-2006 | #4


Аватара для hasherfrog

Старый параноик


Сообщения: 2423
Благодарности: 85

Профиль | Отправить PM | Цитировать


Вот жеж :] Столкнулся с очень похожей задачей (уже решил, но второпях, абы работало, "горю"; вот хочу поговорить "за красоту решения")

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-й грядёт, правда, мы пока не спешим переходить :]

Отправлено: 13:22, 27-05-2006 | #5


редкий гость


Сообщения: 1696
Благодарности: 44

Профиль | Сайт | Отправить PM | Цитировать


hasherfrog
Я чего-то не догоняю. Что мешает пронаследовать всех получателей (C, E, F итд) от общего предка и в конструкторе D принимать указатель на этот интерфейс, не заботясь о том, кто его реализует?

-------
http://ivank.ru


Отправлено: 14:17, 27-05-2006 | #6


Аватара для hasherfrog

Старый параноик


Сообщения: 2423
Благодарности: 85

Профиль | Отправить PM | Цитировать


ivank
В том-то всё и дело, что не хочется так делать. Некрасиво как-то. Совершенно разные классы, созданные для совершенно разных целей - и вдруг общий предок только для решения такой маленькой фишечки, как поддержка callback'а на event. Это, в принципе, вопрос "психологии и личных вкусов" :-/

>> всех получателей (C, E, F итд)
С - нельзя. "Модифицировать С нельзя, ..."

Получается, что мы все наши классы B, D, E... наследуем от С. Сторонний программист (который когда-нибудь возможно будет модифицировать и наш код) увидит конструкцию
class B : public C, ещё, и ещё...
где B - огромный класс, а С - малюсенький

и спросит: а на фига?

Но проблема не только в этом, не только в "личных пристрастиях" и "заботе" о других разработчиков. Класс С (который не смодифицируешь) обязательно требует в конструкторе ссылку на А. Но что, если мне нужно привязать класс В (унаследованный теперь уже от С, а значит, требующий А в памяти) "на ходу"? Т.е. иметь возможность то "прицепится" к event, то "отцепится"? Или "на ходу" прицепится к одному экземпляру А в памяти, а то к другому? Понимаете?

Отправлено: 14:30, 27-05-2006 | #7


редкий гость


Сообщения: 1696
Благодарности: 44

Профиль | Сайт | Отправить PM | Цитировать


Я немного выше перепутал 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
Жаль параметром шаблона нельзя сделать отдельный метод, всё могло бы быть значительно красивше.

-------
http://ivank.ru


Отправлено: 16:35, 27-05-2006 | #8


Аватара для hasherfrog

Старый параноик


Сообщения: 2423
Благодарности: 85

Профиль | Отправить PM | Цитировать


>> Всё вроде просто.
Да, надо подумать; может, так и сделаю. Thanks. Выглядеть только будет своеобразно, по jav'овски как-то :]

Отправлено: 18:14, 27-05-2006 | #9

pva pva вне форума Автор темы

Аватара для pva

Ветеран


Сообщения: 1180
Благодарности: 279

Профиль | Отправить PM | Цитировать


вот так у меня выглядит редактор табличек:
Код: Выделить весь код
class ListModel
{
public:
    virtual void setFocused(item_id_t /*id*/) {};
    virtual void setSelected(item_id_t /*id*/, bool) {};
};

class TableEdit;

class TableEditModel
{
public:
    virtual void erase(const std::vector<item_id_t>&) = 0;
    virtual void insert() = 0;
    virtual void select(item_id_t) = 0;
    virtual void setFocused(item_id_t) = 0;
};

class TableEditView : public ListView, ListModel
{
    TableEdit* fpanel;
    unsigned fcount;

    void perform(Message&);
    void setFocused(item_id_t);
    void setSelected(item_id_t, bool);
public:
    TableEditView(TableEdit* panel);
};

class TableEdit : public Panel, ListModel
{
    TableEditModel* fmodel;
    friend class TableEditView;
    void focusItem(item_id_t);
    void setFocused(item_id_t);
    void eraseSelected();

public:
    enum {
        action_select,
        action_insert,
        action_erase
    };

    ImageList   images;
    LineLabel   label;
    TableEditView listview;

protected:
    ToolBar     toolbar;
    GridLayout  layout;

public:
    TableEdit(Panel&, TableEditModel* model);
    bool doubleClick(Window*);
};
от маленького ListModel (реагирует на нажатия кнопок toolbar) наследуется огромный Panel, который потом ещё пополнять можно. Единственное, что - не могу придумать как грамотно расшарить images, label и listview для заполнения. В идеале - любым классом (например формой, на которой три таких таблички).

Отправлено: 14:41, 31-05-2006 | #10



Компьютерный форум OSzone.net » Программирование, базы данных и автоматизация действий » Программирование и базы данных » Callback - Опять в раздумьях

Участник сейчас на форуме Участник сейчас на форуме Участник вне форума Участник вне форума Автор темы Автор темы Шапка темы Сообщение прикреплено

Похожие темы
Название темы Автор Информация о форуме Ответов Последнее сообщение
Callback New DiploBoy Хочу все знать 4 05-01-2009 00:07
Перенабор callback DeepProg Microsoft Windows 2000/XP 3 10-01-2006 14:53
Настройка callback'a Dimon Общий по Linux 12 28-02-2004 19:50
Опять ХР, опять вин98, опять сеть =( Arioch Сетевые технологии 7 01-04-2003 11:45
Help with Callback, please LoveGun Хочу все знать 2 11-12-2002 19:03




 
Переход