3

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

• 最初我尝试使用 boost::function;它可以很好地处理函数和仿函数,只是它没有运算符==,所以如果我愿意,我不能删除回调。

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

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

二进制 '==' : 没有找到接受 '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::signal 工作?(我宁愿使用 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();
};
4

4 回答 4

1

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

为什么是这样?好吧,看看你的仿函数:

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

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

事实上,大多数函子没有价值相等的谓词。它不是很有用。处理这个问题的常用方法是使用回调句柄;boost::signals 用它的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==,因为你直接指的是信号注册。

于 2011-07-30T15:19:48.580 回答
1

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

于 2011-07-30T16:03:24.117 回答
1

我强烈建议您考虑 Don Clugston 的“成员函数指针和最快的 C++ 代表”。您可以从这里找到文章并下载代码:

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

除了许多其他好处之外,他的代表还提供了开箱即用的比较运算符(==、!=、<)。我目前正在将它们用于实时系统,并发现它们在各个方面都非常出色。我似乎确实记得我们必须进行一些小修改才能解决编译器可移植性问题。但是,这种体验会因平台等而异。

此外,这篇文章已有好几年的历史了,所以如果您遇到任何问题,您可能想四处搜索有关此委托实现的更新代码/讨论。

于 2011-07-31T02:53:06.273 回答
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 对象进行比较,而是生成一个新的仿函数来比较其他两个对象的结果(阅读更多)。要比较 Boost.Bind 您必须使用成员函数compare()

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

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

于 2011-07-31T16:02:07.330 回答