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