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



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

•最初我尝试使用boost::function;它能很好地处理函数和函数,只是没有操作符==,所以即使我想删除回调,也不能删除。

class EventManager
{
    typedef boost::function<void (boost::weak_ptr<Event>)> Callback;
    std::map<Event::Type, std::vector<Callback>> eventHandlerMap_;
};
•我也尝试使用boost::信号,但这也给了我一个与操作符==: 相关的编译问题

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::函数或boost::信号工作?(我宁愿使用boost::函数,因为我听说信号是相当慢的小集合的项目)


编辑:这是我想要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();
};

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

为什么会这样?看看这里的函子:

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

这个Functor没有operator==,因此不能比较是否相等。所以当你通过值传递给boost::signal 时,一个新的实例被创建;这将比较指针相等的false,并且没有操作符来测试值是否相等。

大多数函子实际上没有值相等谓词。它不是很有用。处理这种情况的通常方法是使用回调的句柄;boost::信号通过它的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==,因为你直接引用信号注册。

你试过libsigc和libsigc++吗?我开始在linux中使用它们,并爱上了它们。我现在也在我的Windows应用程序中使用它们。我相信它比boost更具可扩展性和灵活性。

我强烈建议你考虑Don Clugston的《成员函数指针和最快的c++委托》。您可以从这里找到文章并下载代码:

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

在许多其他好处中,他的委托提供了开箱即用的比较操作符(==,!=,<)。我目前正在一个实时系统中使用它们,并发现它们在各个方面都很出色。我似乎记得我们不得不做一个小修改来修复编译器可移植性问题;但是,这种体验会因平台等而有所不同。

另外,这篇文章是好几年前的了,所以如果你遇到任何问题,你可能想要谷歌一下关于这个委托实现的更新代码/讨论。

不管怎样,我找到了解决办法。一点模板魔法,事情就变得简单了(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。单独绑定对象,因为operator==实际上并没有对绑定对象进行比较,而是生成一个新的函子来比较其他两个对象的结果(了解更多)。来比较一下Boost。你必须使用成员函数compare()

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

这段代码将适用于所有类型的函子,只要有一个operator==定义的来进行比较,或者如果你使用Boost.Bind。我对std::bind (c++ 0x)有一个肤浅的了解,但这似乎没有可比性,所以它不会与我上面发布的代码一起工作。

相关内容

最新更新