C ++ обратные вызовы? Должен ли я использовать указатели / делегаты / события членов Func? - PullRequest
2 голосов
/ 24 ноября 2010

Я вхожу в новую для меня область, но в основном мне нужно реализовать обратные вызовы в C ++. Я создаю набор инструментов для себя, чтобы упростить мою жизнь. По сути, это плагин .dll, который будет предоставлять множество функций другим моим плагинам .dll.

Одной из этих функций является HookEvent (const char * event_name, void * callback), который позволит мне перехватывать различные события, которые запускаются. Вот пример ...

Example_Plugin1.dll делает HookEvent ("player_spawn", & Plugin1 :: Event_PlayerSpawn); Example_Plugin2.dll делает HookEvent ("player_spawn", & Plugin2 :: Event_PlayerSpawn);

Мне нужно выяснить лучший (и желательно самый простой) метод настройки системы обратного вызова, которая будет хорошо работать для этого. Я уже несколько часов читаю обратные вызовы C ++ и нашел довольно много разных подходов.

Полагаю, проще всего было бы создать шаблон и использовать typedef bool (ClassName :: * EventHookCallback) (IGameEvent, bool); После этого я немного туманно.

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

Вот ссылка на систему событий в стиле C ++ .NET, о которой я читал. http://cratonica.wordpress.com/2010/02/19/implementing-c-net-events-in-c/

Так что вы, ребята, предлагаете? Любые советы, касающиеся его реализации, были бы весьма полезны.

Ответы [ 6 ]

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

Если вы хотите, чтобы генерализованное событие запускалось Boost.Signals2 может быть применимо.

Библиотека Boost.Signals2 является реализация управляемых сигналов и система слотов. Сигналы представляют обратные вызовы с несколькими целями, и также называются издателями или событиями в аналогичных системах. Сигналы подключен к некоторому набору слотов, которые являются приемниками обратного вызова (также называемые цели или подписчики), которые вызываются, когда сигнал "Излучается."

Даже если вам не нужен этот уровень гибкости, вы сможете упростить привязку функции в своем коде, используя Boost.Bind или эквиваленты C ++ 0x.

EDIT: Херб Саттер отлично обсуждает проблемы, с которыми вы можете столкнуться здесь . Вы можете использовать это в качестве руководства, если решите, что вам не нужен полный набор функций Boost, и прокрутите свой собственный.

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

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

Библиотека, PluginIfc.h:

class PluginIfc {
public:
  virtual ~PluginIfc() = 0;
  virtual bool EventCallback(const char* event_name, IGameEvent, bool) = 0;     
};

// For Windows, add dllexport/dllimport magic to this declaration.
// This is the only symbol you will look up from the plugin and invoke.
extern "C" PluginIfc* GetPlugin();

Плагин:

#include <PluginIfc.h>
class Plugin1 : public PluginIfc {
public:
  virtual bool EventCallback(const char* event_name, IGameEvent, bool);
  Plugin1& get() { return the_plugin_obj; }

  bool Event_PlayerSpawn(IGameEvent, bool);
  // ...
private:
  std::vector<std::string> _some_member;

  static Plugin1 the_plugin_obj; // constructed when plugin loaded
};

Plugin1 Plugin1::the_plugin_obj;
PluginIfc* GetPlugin() { return &Plugin1::get(); }

ЭтоКстати, ваши классы плагинов могут легко иметь членов, а механизм виртуальных вызовов C ++ позаботится о том, чтобы дать вам хороший указатель this в EventCallback.

Может быть соблазнительно создать виртуальный метод для каждого типа событияскажем просто сделайте Event_PlayerSpawn и подобные методы виртуальными.Но тогда, когда вы захотите добавить тип события, если это означает изменение класса PluginIfc, ваши старые скомпилированные плагины больше не будут совместимы.Поэтому безопаснее использовать строковый идентификатор события (для расширяемости) и отключить основные события сортировки обратного вызова для более конкретных методов.

Основной недостаток (по сравнению с реализацией типа сигнального слота) состоит в том, что всеобратные вызовы должны принимать тот же набор аргументов.Но твой вопрос звучал так, что этого было бы достаточно.И часто можно работать в пределах этого ограничения, убедившись, что набор аргументов очень гибок, используя анализируемые строки или Any объекты стиля.

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

Boost.Signals будет моим выбором в сочетании с такими вещами, как boost::bind и Boost.Function.

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

Как насчет использования Qt Signal и Slot?Он делает то, что делают обратные вызовы, но без необходимости делать что-то, что не является частью ваших параметров обратного вызова, глобальным.

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

Внедрение вашей собственной системы обратного вызова нетривиально.
Насколько я понимаю, ваша цель состоит в том, чтобы сопоставить типы событий с конкретными функциями обратного вызова.
Например, если событие «player_spawn» поднято, то & Plugin1 :: Event_PlayerSpawn будетзвонил.
Так что вы должны сделать следующее:
1) Определите все события, представляющие интерес.Сделайте их как можно более общими.Они могут инкапсулировать любую необходимую вам информацию
2) Создать регистратора.Т.е. класс, в котором все модули регистрируют свой интерес для конкретных методов.Например, Registrar.register(player_spawn,this,Event_PlayerSpawn);
3) Регистратор имеет очередь всех подписчиков.
4) Вы также можете иметь единый интерфейс для модулей.Т.е. все модули реализуют определенную функцию, но на основе данных события могут делать разные вещи
5) Когда происходит событие, все подписчики, заинтересованные в конкретном событии, получают уведомление, вызывая соответствующую функцию
6) Абонент может отменить регистрацию при необходимости
Надеюсь, это поможет.

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

Звучит так, как будто вы можете быть заинтересованы в том, как создать свой собственный плагин. Проблемы, с которыми вы столкнетесь, скорее всего, те же. Взгляните на эту замечательную статью доктора Доббса Создание собственного плагина Framework .

Надеюсь, это поможет!

...