Могу ли я упростить это? - PullRequest
2 голосов
/ 20 января 2009
typedef void (FunctionSet::* Function)();

class MyFunctionSet : public FunctionSet
{
protected:
    void addFunctions()
    {
        addFunction(Function(&MyFunctionSet::function1));
    }

    void function1()
    {
        // Do something.
    }
};

Метод addFunction добавляет функцию в список в базовом классе,
который затем можно перечислить для вызова всех функций.

Есть ли способ упростить (меньше печатать) добавление функций?

Ответы [ 5 ]

2 голосов
/ 20 января 2009

Итак, я склонен не согласиться с Ури, немного.

Если вы реализуете шаблон Observer, вам нужно иметь возможность сказать:

«Когда объект X выполняет Y, я хочу выполнить код Z».

Использование подхода, основанного исключительно на абстрактном классе, не лучший способ сделать это. Требование отдельного класса (особенно в C ++) для каждого обработчика событий является излишним. Если вы пришли с Java, вот как они все делают. Однако у Java есть 2 функции, которые делают это только немного раздражающим: анонимный класс и классы-члены «экземпляра». Это позволяет вам определить несколько обработчиков для нескольких событий в одном классе. Вы должны добавить к методам префикс "class {", но вы можете сделать это.

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

.NET использует делегаты для обработки событий, которые в основном являются указателями на безопасные функции типов. Они делают код для обработки событий очень простым. В отличие от указателей на функции в C ++, делегат объединяет указатели на функции-члены и статические указатели на функции. По сути, он может каррировать параметр «this» любой функции-члена, оставляя указатели на функции, которые выглядят как статические указатели на функции. Это означает, что тип объекта, обрабатывающего событие, не является частью события «интерфейс». Это делает его очень гибким.

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

Лучшее решение для C ++ - это гибрид между ними. Вам нужен универсальный интерфейс, подобный следующему:

class EventHandler
{
public:
    virtual void Handle() = 0;
};

А затем реализация функций-членов, подобных этой

template <class T>
class MemberFuncEventHandler : public EventHandler
{
public:


    MemberFuncEventHandler(T * pThis, void (T::*pFunc)()) : m_pThis(pThis), m_pFunc(pFunc)
    {
    }

    void Handle()
    {
        (m_pThis->*m_pFunc)();
    } 
private:
    T* m_pThis;
    void (T::*m_pFunc)();
};

template <class T>
EventHandler * Handler(T * pThis, void (T::*pFunc)())
{
    return new MemberFuncEventHandler<T>(pThis, pFunc);
}

Вы также можете аналогичным образом определить класс обработчика для статических методов. Тогда вы можете просто сделать такие вещи:

Handlers += Handler(obj, &(c1::foo));
Handlers += Handler(obj, &(c2::bar));
Handlers += Handler(blaa); // for static methods...
2 голосов
/ 20 января 2009

Можете ли вы объяснить, чего вы пытаетесь достичь, делая это?

Это выглядит как довольно плохой дизайн, разве вы не можете иметь абстрактный класс ("интерфейс c ++"), такой как "Computable", который имеет чисто виртуальную функцию1, подкласс Computable для каждой реализации, а затем MyFunctionSet поддерживает набор вычислимого?

Есть ли конкретная причина, по которой вы используете указатели функций?

2 голосов
/ 20 января 2009

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

Чтобы ответить на ваш актуальный вопрос - я бы сделал addFunction шаблон:

void addFunctions() {
    addFunction(&MyFunctionSet::function1);
}

Измените addFunction в базовом классе на:

template<typename Derived>
void addFunction(void(Derived::*f)()) {
    myFunctions.push_back(static_cast<Function>(f));
}

Лучше использовать static_cast, поскольку он сообщит вам, действительно ли Derived не получен из FunctionSet.

1 голос
/ 20 января 2009

Если вы пытаетесь создать систему обратного вызова или что-то подобное, я бы порекомендовал библиотеку Boost.Signals. В основном он объединяет функции и вызывает группу функций по команде (как и любая другая библиотека сигналов), но он также предназначен для работы с потрясающими Boost.Bind и Boost.Function.

Пример:

using boost::function
using boost::bind
using boost::signal

void some_other_func();    

Foo a;
Bar b;

signal<void()> sig;

sig.connect(bind(&Foo::foo,&a));
sig.connect(bind(&Bar::bar,&b));
sig.connect(some_other_func);

sig(); // calls -> a.foo(), b.bar(), some_other_func()

Также поддерживает блокировку, сложное управление соединениями и другие полезные вещи.

1 голос
/ 20 января 2009

Полагаю, вы могли бы перегрузить оператор сложения.

...