0

我创建了简单的基于回调的事件管理器并且它可以工作,但是我有一些零模板参数的错误。

class event_manager
{
public:
    template <typename... T>
    static void register_event(const unsigned long e, std::function<void(T...)> ec)
    {
        events.insert({ e, ec });
    }

    template <typename... T>
    static void fire_event(const unsigned long e, T... args)
    {
        for (auto it : events)
        {
            if (it.first == e)
            {
                boost::any_cast<std::function<void(T...)>>(it.second)(args...);
            }
        }
    }
private:
    static std::unordered_multimap<unsigned int, boost::any> events;
};

我正在使用此代码添加回调:

event_manager::register_event<unsigned int>(DVU_EVENT_KEY_PRESSED, [](unsigned int key)
{
    //Works!
});

event_manager::register_event(DVU_EVENT_IDLE, []()
{
    //Could not deduce template argument
});

第二个问题:是否可以更改代码以删除<unsigned int>类似模板规范?

例子:

event_manager::register_event(DVU_EVENT_KEY_PRESSED, [](unsigned int key){}));
4

3 回答 3

1

由于 lambda 只是一个带有 的函子operator(),因此您可能只需要一个重载来推导它:

template <typename F>
static void register_event(const unsigned long e, F f) 
{
    register_event(e, f, &F::operator());
}

template <typename F, typename R, typename... T>
static void register_event(const unsigned long e, F& f, 
    R (F::*method)(T...) const) 
{
    std::function<R(T...)> func = f;
    events.insert({ e, func });
}

也许要么需要它,要么需要R == voidstatic_assert或其他东西。

于 2014-10-24T13:44:46.903 回答
1

即使是第一个也不会像你一样在这里编译。

std::function与 lambda 不完全匹配,并且当您使用可变参数模板时,您不能以这种方式指定所有类型(当您指定第一种类型时,编译器可能会推断出其余的类型)。

一种可能的解决方法是只传递 func

template <typename Func>
static void register_event(const unsigned long e, Func ec);

并重建 std::functionFunc::operator()

于 2014-10-24T13:48:11.577 回答
0

您的设计是不安全的,因为依靠类型推导来产生完全匹配的类型是脆弱的,并且您的转换需要完全匹配的类型。

这是一个稍微不同的设计:

class event_manager {
public:
  template <typename Signature>
  static void register_event(const unsigned long e, std::function<Signature> ec) {
    events.emplace( e, std::move(ec) );
  }

  template <typename Signature, typename...Ts>
  static void fire_event(const unsigned long e, Ts...&& args) {
    auto r = events.equal_range( e );
    for (auto it = r.first; it != r.second; ++it)
    {
      auto&& f = boost::any_cast<std::function<Signature> const&>(it.second);
      f(std::forward<Ts>(args)...);
    }
  }
private:
  static std::unordered_multimap<unsigned int, boost::any> events;
};

在这里,您在两端传入一个函数签名,例如void()or void(int)。这些类型必须完全匹配。

我将参数完美转发给fire_event我从地图中拉出的函数。

我做了一些其他改进,比如正确移动/放置并删除了一些虚假副本。

出于几个原因,推断 lambda 的签名是一个坏主意。首先,因为 C++14 autolambda 即将到来。其次,这意味着 lambda 或函数采用 aT const&或 aT或任何“泄漏”到您必须如何调用它的事实(您的原始实现要求所有值都按值获取)。

现在,给定事件的签名在您注册它的位置和触发它的位置都明确列出。如果不匹配,应该更容易注意到。

我也很想去any_cast/来自一个指针,并断言它是非空的,而不是在你得到错误的签名时抛出。

于 2014-10-24T14:20:08.187 回答