Является ли шаблонный полиморфный обратный вызов хорошей идеей? - PullRequest
2 голосов
/ 12 ноября 2010

Я делаю Gui Api для игр. Пользователь всегда может использовать наследование на виджете и переопределить, но я хочу обратные вызовы. Я хочу использовать шаблонную систему обратного вызова:

поэтому, если они хотят иметь один для мыши, они наследуют от версии базы шаблонного обратного вызова с mouseargs:

Так что база будет выглядеть так:

template <typename T>

class AguiEventCallback {

public:
virtual void callback(AguiWidget* sender, T arg) = 0;

};

Хорошо ли смешивать шаблоны с таким полиморфизмом? Будет ли мне лучше создавать обратные вызовы для каждого типа, который мне нужен (мышь, клавиатура, геймпад и т. Д.)?

Спасибо

Ответы [ 4 ]

3 голосов
/ 12 ноября 2010

Посмотрите на boost :: function и boost :: bind. Примите функциональный объект с определенным списком параметров для определенных событий, и вызывающие абоненты могут делать то, что они хотят.

Это дает реализациям обратного вызова большую гибкость, а объект, генерирующий события, требует еще меньше знаний о реализации обратного вызова.

Например:

 typedef boost::function<void (AguiWidget* sender)> CallbackFunc;
 void register_callback(CallbackFunc const& f);

И клиент:

class Caller {
    void do_register() { register_callback(bind(&Caller::event, this, 123, _1)); }

    void event(int arg, AguiWidget* sender) { ... }
};

Просто показывает функцию / привязку, многие другие проблемы игнорируются; например. управление памятью, время жизни объекта.

1 голос
/ 12 ноября 2010

Использование шаблона, как у вас, иногда хорошо. Существуют проблемы, связанные с тем, что вы должны предоставить вашему шаблону виртуальный деструктор и что

  • Если вы встроите свой виртуальный деструктор (как вы это делаете с большинством шаблонных функций), некоторым компиляторам будет сложно придерживаться правила единого определения, особенно если библиотека используется в разных библиотеках.

  • Если вы не указали свой виртуальный деструктор, вам нужно создать экземпляр каждого типа, который вы собираетесь использовать с этим шаблоном. Это мой предпочтительный подход.

Для обратного вызова у вас есть возможность использовать boost :: function. Это избавляет от необходимости извлекать классы из вашего шаблона, создавать их с новым и, вероятно, где-то вставлять в shared_ptr. Я обнаружил, что недостатком boost :: function в качестве обратного вызова является сложность отладки, если что-то пойдет не так. Остерегайтесь этого вопроса.

0 голосов
/ 12 ноября 2010

В дополнение к другим ответам здесь вы можете взглянуть на библиотеку boost :: signal. Он реализует механизм сигнал / слот, который действительно полезен для графических интерфейсов. Производительность не так хороша, как вы ожидаете (стоит больше, чем вызов виртуального метода), но для графического интерфейса это просто отлично.

Библиотека boost :: signal также может использоваться вместе с boost :: bind, и эта комбинация очень мощная.

Мне не очень нравится использовать наследование для обратных вызовов. Он порождает множество классов с одним методом в большинстве случаев. Это C ++, а не Java. у вас есть функции, используйте их:)

0 голосов
/ 12 ноября 2010

На мгновение принимая ваше виртуальное диспетчерское решение, ваш шаблонный подход гарантирует единообразие имени и аргументов функции обратного вызова.К сожалению, это заставит много другого кода устранять неоднозначность, какой обратный вызов вызывается / переопределяется, что, вероятно, вызывает больше проблем, чем пользы.

Тем не менее, как сказал Дженм, существуют другие варианты.Функторы являются более мощными (вы можете изменить их на существующем объекте во время выполнения, вы можете иметь списки наблюдателей), но их также нужно инициализировать в нужное время (чисто виртуальные функции эффективно напоминают программисту о необходимости предоставлять их во время компиляции)и представьте большее разнообразие состояний времени выполнения, чтобы рассуждать и понимать.

Вы также можете использовать шаблонную политику или шаблон любопытного повторяющегося шаблона для предоставления поведения во время компиляции, что позволяет встраивать мертвыеустранение кода, специфичные для типа поведения и другие оптимизации.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...