Хороший способ добавить обработчики событий? - PullRequest
1 голос
/ 10 октября 2010

Я делаю Gui API для игр.По сути, в моем классе есть обратные вызовы событий, которые являются указателями на функции.Я думал о том, чтобы прямо позволить пользователю = указатель на функцию ex:

widget->OnPaintCallback = myPaintFunc;

Но мне не нравится, как я не могу проверить NULL или сделать что-то еще.Это также заставляет мой класс чувствовать себя разоблаченным.

Я также думал о том, чтобы иметь установщик для каждого обратного вызова, но это будет грязно в классе (у меня более 50)

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

Есть ли лучшая, более чистая альтернатива?

Спасибо

Может ли решение Касабланки иметь несколько аргументов?

Ответы [ 4 ]

1 голос
/ 10 октября 2010

Я рекомендую взглянуть на библиотеку boost.signals или libsigc ++ .Это очень общие библиотеки для управления такими вещами, как обработчики событий.Они делают намного больше, чем то, что вы пытаетесь сделать, но они дадут вам идеи о том, что вы можете захотеть от своего дизайна, о котором вы еще не думали.

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

1 голос
/ 10 октября 2010

Я бы предложил библиотеку Boost.Signals . Примерно так:

class Widget
{
public:
    boost::signal<void (Paint &)> onPaint;
    boost::signal<void (MouseMove &)> onMouseMove;
    // ... etc
};

// later...

Widget myWidget;
myWidget.onPaint.connect(myPaintFunc);

// and to fire the event:

void Widget::DoPaint()
{
    Paint data;
    data.whatever = foo;

    onPaint(data);
}

Это имеет несколько преимуществ:

  1. Вы можете комбинировать его с boost::bind (или с версией C ++ 0x bind, если ваш компилятор поддерживает это), чтобы позволить вам связывать функции-члены с обработчиками событий.
  2. Вы можете прикрепить несколько обработчиков к одному событию. Если вы просто используете указатели на функции, то одновременно может быть назначен только один указатель на функцию.
  3. Сигналы сильного типа и гибкие. У вас могут быть сигналы, которые принимают разные типы и числа параметров, и все они будут разрешены во время компиляции.
0 голосов
/ 10 октября 2010

Но мне не нравится, как я не могу проверить NULL или сделать что-то еще

Как насчет того, чтобы сделать обратный вызов (OnPaintCallback) объектом класса, который перегружает operator = таким образом, вы можете выполнить любую дополнительную проверку и выдать исключение, если что-то пойдет не так.Вы также можете перегрузить operator (), чтобы вызвать этот объект, как если бы он был простым указателем на функцию.

Обновление: Что касается переменного числа аргументов функции, то здесь нет общего способа.чтобы сделать это, но если ваш максимальный N ограничен и невелик, вы можете использовать шаблонные специализации, например: (я упустил конструкторы, operator = и другие подробности для ясности)

template<typename T, int N>
class Callback {
};

template<typename T>
class Callback<T, 1> {
  T func;

  template<typename A1>
  void operator ()(A1 arg1) {
    func(arg1);
  }
};

template<typename T>
class Callback<T, 2> {
  T func;

  template<typename A1, typename A2>
  void operator ()(A1 arg1, A2 arg2) {
    func(arg1, arg2);
  }
};

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

0 голосов
/ 10 октября 2010

Вы можете создать интерфейс для каждого типа обработчика событий, который вам нужен.В отличие от Java это делает.Например, у вас будет

class PaintCallback {
public:
  virtual void paint() = 0;
};

. Ваш обработчик событий будет наследовать от абстрактного класса и реализовывать метод рисования.В классе виджетов вы должны хранить указатель (или коллекцию) для каждого обработчика.

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