我正在尝试为我的游戏编写一个事件系统。事件管理器将存储的回调函数既可以是普通函数,也可以是函数函数。我还需要能够比较函数/函子,所以我知道哪一个我需要从事件管理器断开。
•最初我尝试使用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)有一个肤浅的了解,但这似乎没有可比性,所以它不会与我上面发布的代码一起工作。