消息系统:回调可以是任何东西

时间:2011-07-30 15:14:51

标签: c++ event-handling boost-bind

我正在尝试为我的游戏编写一个事件系统。我的事件管理器将存储的回调既可以是普通函数,也可以是函子。我还需要能够比较函数/函子,以便知道我需要从事件管理器中断开哪一个。

•最初我尝试使用boost :: function;它完美地处理函数和函子,除了它没有运算符==,所以如果我愿意,我不能删除回调。

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

•我也试过使用boost :: signal,但这也给了我一个与operator ==相关的编译问题:

  

binary'==':找不到哪个运算符采用'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 个答案:

答案 0 :(得分:1)

您会发现大多数通用函数包装器不支持函数相等。

这是为什么?那么,看看那里的仿函数:

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

Functor没有operator==,因此无法进行相等性比较。因此,当您按值将其传递给boost::signal 时,会创建一个新实例;这将比较指针相等的false,并且没有运算符来测试值相等。

事实上,大多数仿函数都没有值相等的谓词。它没什么用处。解决这个问题的常用方法是改为使用回调句柄; boost :: signals通过其connection对象执行此操作。例如,从the documentation

查看此示例
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==,因为你直接指的是信号注册。

答案 1 :(得分:1)

你有没有尝试过libsigc和libsigc ++?我开始在linux中使用它们并爱上了它们。我现在也在我的Windows应用程序中使用它们。我相信它比增强更具可扩展性和灵活性。实施起来也是一件轻而易举的事。

答案 2 :(得分:1)

我强烈建议您考虑Don Clugston&#34;会员功能指针和最快可能的C ++代表&#34;。您可以在这里找到该文章并下载代码:

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

在众多其他好处中,他的代表提供开箱即用的比较运算符(==,!=,&lt;)。我目前正在将它们用于实时系统,并且发现它们在各个方面都非常出色。我似乎记得我们必须做一个小修改来修复编译器可移植性问题;但是,这种体验会因平台等而异。

此外,这篇文章已有几年的历史了,所以如果您遇到任何问题,可能需要谷歌了解有关此委托实施的更新代码/讨论。

答案 3 :(得分:1)

无论如何,我找到了解决方案。一个小模板魔术和事情变得简单(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对象进行比较,而是生成一个新的函子来比较另外两个(read more)的结果。要比较Boost.Bind,您必须使用成员函数compare()

类型boost::_bi::bind_t似乎是一种内部类型的Boost(我猜这就是命名空间'_bi'中的下划线的意思),但是将它用作boost::function_equal的所有重载应该是安全的。也使用此类型(reference)。

此代码适用于所有类型的仿函数,只要有operator==定义进行比较,或者您正在使用Boost.Bind。我对std::bind(C ++ 0x)有一个肤浅的看法,但这似乎没有可比性,所以它不适用于我上面发布的代码。