Сейчас я пишу небольшой редактор графических форм.
Мне нужно добавлять к элементам формы callbacks, причем что в callback приходит всегда ссылка на объект типа widget (базовая структура данных, которая для разных типов виджетов только обрабатывается по-разному). Мне нужно предоставить пользователю возможность к определенной группе объектов добавлять свои поля данных.
Но загвоздка в том, как в callback определить тип для user data текущего объекта, если во всех widget поле user имеет тип void *, а в один и тот же callback могут приходить разные widget (например, в callback по нажатию мыши)?
Здравствуйте, Hard_Club, Вы писали:
H_C>Мне нужно добавлять к элементам формы callbacks, причем что в callback приходит всегда ссылка на объект типа widget (базовая структура данных, которая для разных типов виджетов только обрабатывается по-разному). Мне нужно предоставить пользователю возможность к определенной группе объектов добавлять свои поля данных.
H_C>Но загвоздка в том, как в callback определить тип для user data текущего объекта, если во всех widget поле user имеет тип void *, а в один и тот же callback могут приходить разные widget (например, в callback по нажатию мыши)?
То есть,
— с одной стороны, у колбека должна быть какая-то связанная переменная, определяемая пользователем (например, адрес объекта-приёмника)
— с другой стороны, этот колбек должен принимать полиморфные данные
Есть несколько решений.
Первое — в духе ООП.
Определяем интерфейс ICallback, у которого есть все нужные сигнатуры
struct ICallback
{
virtual void on_mouse(Point where, int buttons, int flags) = 0;
virtual void on_keyboard(int keycode, char letter, int shifts, int flags, int repeat) = 0;
virtual void on_timer(int identifier) = 0;
.....
};
Тогда связанной переменной является собственно адрес объекта, реализующего этот интерфейс.
Второе — в духе Plain Old C.
Полиморфные данные — это вариантный тип.
Колбек — это пара (указатель на функцию, указатель на что угодно).
typedef struct tagEvent
{
int type;
union
{
MouseEvent mouse;
KeyboardEvent keyboard;
TimerEvent timer;
....
} details;
} Event;
typedef void (*callback_func)(void* param, Event* event);
typedef struct tagCallback
{
callback_func func;
void* param;
} Callback;
Сделать красивую С++ную обёртку для сишной — не составит труда.
Развитием идеи являются boost::function + boost::variant / boost::any
typedef variant<MouseEvent, KeyboardEvent, TimerEvent> Event;
typedef function<void(Event*)> Callback;
И, наконец, boost::signal.
В нём есть не только механизм вызова, но и механизм подписки и рассылки.