【问题标题】:Messaging system: Callbacks can be anything消息系统:回调可以是任何东西
【发布时间】:2011-07-30 15:14:51
【问题描述】:

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

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

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

【问题讨论】:

标签: c++ event-handling boost-bind


【解决方案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==,因为您直接指的是信号注册。

【讨论】:

  • 如果我要保存连接对象,我不妨改用 vector<:fucntion> 并保存迭代器,因为函数比信号快。难道没有办法让 boost::function 比较内存地址吗?
  • boost::function 也按值捕获,因此地址将永远相同。而且我怀疑信号不会比常规的函数向量差太多。
【解决方案2】:

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

【讨论】:

    【解决方案3】:

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

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

    在许多其他好处中,他的代表提供了开箱即用的比较运算符(==、!=、

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

    【讨论】:

      【解决方案4】:

      没关系,我找到了解决方案。一点模板魔法,事情就变得简单了(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) 有一个肤浅的了解,但这似乎没有可比性,因此它不适用于我上面发布的代码。

      【讨论】:

        猜你喜欢
        • 2019-09-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-08-23
        • 1970-01-01
        • 1970-01-01
        • 2016-09-10
        相关资源
        最近更新 更多