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

发布于 2024-11-27 06:16:52 字数 1369 浏览 0 评论 0原文

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

• 最初我尝试使用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:: function 或 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();
};

I'm trying to write an event system for my game. The callbacks that my event manager will store can be both plain functions as well as functors. I also need to be able to compare functions/functors so I know which one I need to disconnect from the event manager.

• Initially I tried using boost::function; it handles functions and functors perfectly well, except it has no operator==, so I can't remove callbacks if I want to.

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

• I also tried using boost::signal, but that also gives me a compilation problem related to operator==:

binary '==' : no operator found which takes a left-hand operand of type 'const Functor' (or there is no acceptable conversion)

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
}

Any other suggestions about how I might implement this? Or maybe how I can make either boost:: function or boost::signal work? (I'd rather use boost:: function though, since I've heard signal is rather slow for small collections of items.)


Edit: This is the interface of that I'd like EventManager to have.

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();
};

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

老娘不死你永远是小三 2024-12-04 06:16:52

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

这是为什么呢?好吧,只要看看你的函子:

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

这个函子没有运算符==,因此无法比较相等性。因此,当您将其传递给 boost::signal 按值时,会创建一个新实例;这将比较指针相等性为 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==,因为您直接引用信号注册。

You'll find that most generic function wrappers do not support function equality.

Why is this? Well, just look at your functor there:

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

This Functor has no operator==, and therefore cannot be compared for equality. So when you pass it to boost::signal by value, a new instance is created; this will compare false for pointer-equality, and has no operator to test for value-equality.

Most functors don't, in fact, have value-equality predicates. It's not useful very much. The usual way to deal with this is to have a handle to the callback instead; boost::signals does this with its connection object. For example, take a look at this example from 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

With this, HelloWorld doesn't need to have an operator==, as you're referring directly to the signal registration.

九局 2024-12-04 06:16:52

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

Have you ever tried libsigc and libsigc++? I started using them in linux and fell in love with them. I now use them in my Windows applications as well. I believe it is more extensible and flexible than boost. It is also a breeze to implement.

秋日私语 2024-12-04 06:16:52

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

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

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

此外,这篇文章已经有好几年了,所以如果您遇到任何问题,您可能需要通过谷歌搜索有关此委托实现的更新代码/讨论。

I highly recommend you consider Don Clugston's "Member Function Pointers and the Fastest Possible C++ Delegates". You can find the article and download the code from here:

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

Among many other benefits, his delegates provide comparison operators (==, !=, <) out of the box. I'm currently using them for a realtime system and find them excellent in every way. I do seem to recall we had to make a minor modification to fix a compiler portability issue; but, that experience will vary based on platform etc.

Also, the article is several years old so you may want to google around for updated code/discussion regarding this delegate implementation if you run into any problems.

凡间太子 2024-12-04 06:16:52

没关系,我找到了解决方案。一点模板魔法,事情就变得简单了(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,此代码就适用于所有类型的函子。我粗略地了解了 std::bind (C++0x),但这似乎没有可比性,因此它不适用于我上面发布的代码。

No matter, I found the solution. A little template magic and things become simple(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());
}

I need to handle Boost.Bind objects separately because operator== doesn't actually do comparison for Bind objects, but produce a new functor that compares the result of the other two (read more). To compare Boost.Bind you have to use the member function compare().

The type boost::_bi::bind_t seems to be an internal type of Boost (I guess that's what the underscore in namespace '_bi' means), however it should be safe to use it as all overloads of boost::function_equal also use this type (reference).

This code will work for all types of functors as long as there is an operator== defined that does comparison, or if you're using Boost.Bind. I had a superficial look into std::bind (C++0x), but that doesn't seem to be comparable, so it won't work with the code I posted above.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文