Search code examples
c++event-handlingboost-bind

Messaging system: Callbacks can be anything


I'm trying to write an event system for my game. The callbacks that my event manager will store can be both plain functions as well as functors. I also need to be able to compare functions/functors so I know which one I need to disconnect from the event manager.

• Initially I tried using boost::function; it handles functions and functors perfectly well, except it has no operator==, so I can't remove callbacks if I want to.

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

• I also tried using boost::signal, but that also gives me a compilation problem related to operator==:

binary '==' : no operator found which takes a left-hand operand of type 'const Functor' (or there is no acceptable conversion)

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
}

Any other suggestions about how I might implement this? Or maybe how I can make either boost:: function or boost::signal work? (I'd rather use boost:: function though, since I've heard signal is rather slow for small collections of items.)


Edit: This is the interface of that I'd like EventManager to have.

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();
};

Solution

  • No matter, I found the solution. A little template magic and things become simple(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());
    }
    

    I need to handle Boost.Bind objects separately because operator== doesn't actually do comparison for Bind objects, but produce a new functor that compares the result of the other two (read more). To compare Boost.Bind you have to use the member function compare().

    The type boost::_bi::bind_t seems to be an internal type of Boost (I guess that's what the underscore in namespace '_bi' means), however it should be safe to use it as all overloads of boost::function_equal also use this type (reference).

    This code will work for all types of functors as long as there is an operator== defined that does comparison, or if you're using Boost.Bind. I had a superficial look into std::bind (C++0x), but that doesn't seem to be comparable, so it won't work with the code I posted above.