Система обмена сообщениями: обратные вызовы могут быть любыми - PullRequest
3 голосов
/ 30 июля 2011

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

• Изначально я пытался использовать boost :: function; он отлично обрабатывает функции и функторы, за исключением того, что у него нет оператора ==, поэтому я не могу удалить обратные вызовы, если захочу.

class EventManager
{
    typedef boost::function<void (boost::weak_ptr<Event>)> Callback;
    std::map<Event::Type, std::vector<Callback>> eventHandlerMap_;
};

• Я также пытался использовать boost :: signal, но это также дает мне проблему с компиляцией, связанную с operator ==:

бинарный '==': не найден оператор, который принимает левый операнд типа 'const Functor' (или нет приемлемого преобразования)

void test(int c) {
    std::cout << "test(" << c << ")";
}

struct Functor
{
    void operator()(int g) {
        std::cout << "Functor::operator(" << g << ")";
    }
};

int main()
{
    boost::signal<void (int)> sig;

    Functor f;

    sig.connect(test);
    sig.connect(f);

    sig(7);

    sig.disconnect(f); // Error
}

Любые другие предложения о том, как я мог бы реализовать это? Или, может быть, как я могу заставить Boost :: Function или Boost :: Signal работать? (Я бы предпочел использовать boost :: function, поскольку я слышал, что для небольших коллекций элементов сигнал довольно медленный.)


Редактировать: Это интерфейс, который я бы хотел иметь в EventManager.

class EventManager
{
  public:
    void addEventHandler(Event::Type evType, Callback func);
    void removeEventHandler(Event::Type evType, Callback func);

    void queueEvent(boost::shared_ptr<Event> ev);
    void dispatchNextEvent();
};

Ответы [ 4 ]

1 голос
/ 31 июля 2011

Неважно, я нашел решение.Немного магии шаблонов, и все становится просто (r):

template<typename F>
void EventManager::removeEventHandler(Event::Type evType, F func)
{
    auto compare = [func](const Callback& other) -> bool {
        F const* f = other.target<F>();
        if (f == nullptr) return false;
        return *f == func;
    };

    std::vector<Callback>& callbacks = ...;
    auto pend = std::remove_if(callbacks.begin(), callbacks.end(), compare);
    callbacks.erase(pend, callbacks.end());
}


template<typename R, typename F, typename L>
void EventManager::removeEventHandler(
    Event::Type evType, const boost::_bi::bind_t<R, F, L>& func)
{
    auto compare = [&func](const Callback& other) -> bool {
        auto const* f = other.target<boost::_bi::bind_t<R, F, L>>();
        if (f == nullptr) return false;
        return func.compare(*f);
    };

    std::vector<Callback>& callbacks = ...;
    auto pend = std::remove_if(callbacks.begin(), callbacks.end(), compare);
    callbacks.erase(pend, callbacks.end());
}

Мне нужно обрабатывать объекты Boost.Bind отдельно, потому что operator== на самом деле не выполняет сравнение для объектов Bind, но создает новый функтор, которыйсравнивает результат двух других ( читать дальше ).Чтобы сравнить Boost.Bind, вы должны использовать функцию-член compare().

. Тип boost::_bi::bind_t представляется внутренним типом Boost (я думаю, это то, что означает подчеркивание в пространстве имен '_bi'),однако его можно безопасно использовать, поскольку все перегрузки boost::function_equal также используют этот тип ( reference ).

Этот код будет работать для всех типов функторов, пока существуетoperator== определено , что делает сравнение , или если вы используете Boost.Bind.Я поверхностно изучил std::bind (C ++ 0x), но это, похоже, не сравнимо, поэтому оно не будет работать с кодом, который я выложил выше.

1 голос
/ 30 июля 2011

Вы когда-нибудь пробовали libsigc и libsigc ++? Я начал использовать их в Linux и влюбился в них. Теперь я использую их и в своих приложениях Windows. Я считаю, что он более расширяемый и гибкий, чем буст. Это также легко реализовать.

1 голос
/ 31 июля 2011

Я настоятельно рекомендую вам рассмотреть «Указатели на функции-члены Дона Клагстона и максимально быстрых делегатов C ++». Вы можете найти статью и скачать код здесь:

http://www.codeproject.com/KB/cpp/FastDelegate.aspx

Среди многих других преимуществ его делегаты предоставляют операторы сравнения (==,! =, <) Из коробки. В настоящее время я использую их для системы реального времени и считаю их превосходными во всех отношениях. Кажется, я помню, что нам пришлось внести небольшую модификацию, чтобы исправить проблему переносимости компилятора; но этот опыт будет зависеть от платформы и т. д. </p>

Кроме того, этой статье уже несколько лет, поэтому вы можете поискать обновленный код / ​​обсуждение этой реализации делегата, если столкнетесь с какими-либо проблемами.

1 голос
/ 30 июля 2011

Вы обнаружите, что большинство оболочек универсальных функций не поддерживают равенство функций.

Почему это? Ну, просто посмотрите на ваш функтор:

struct Functor
{
    void operator()(int g) {
        std::cout << "Functor::operator(" << g << ")";
    }
};

У этого Functor нет operator==, и, следовательно, его нельзя сравнить на равенство. Поэтому, когда вы передаете его в boost::signal по значению , создается новый экземпляр; он сравнивает false для равенства указателей и не имеет оператора для проверки на равенство значений.

Большинство функторов фактически не имеют предикатов равенства значений. Это не очень полезно. Обычный способ справиться с этим - вместо этого иметь дескриптор обратного вызова; boost :: signal делает это с помощью объекта connection. Например, посмотрите на этот пример из документации :

boost::signals::connection c = sig.connect(HelloWorld());
if (c.connected()) {
// c is still connected to the signal
  sig(); // Prints "Hello, World!"
}

c.disconnect(); // Disconnect the HelloWorld object
assert(!c.connected()); c isn't connected any more

sig(); // Does nothing: there are no connected slots

При этом HelloWorld не нужно иметь operator==, так как вы имеете в виду непосредственно регистрацию сигнала.

...