通用回调函数的容器

问题描述

我正在尝试找到一种方法来保存具有不同签名的多个回调函数

具体来说,假设我有结构accel_tgyro_t,它们将是两个不同的回调函数void cb1(const accel_t &msg)void cb2(const gyro_t &msg)的参数,然后我想传递cb1和将cb2添加subscribe()函数中,以便将其添加到某种形式的容器中(例如std :: list或std :: vector)。

我在想我的身体中定义了一个回调函数,如下所示:

void cb1(const accel_t &msg)
{
/**do stuff*/
}

我的订阅功能将如下所示:

myclass.subscribe<accel_t>(cb1);

实现此目标的最佳方法是什么?

解决方法

您不能轻易将这些回调放在同一容器中。如果返回回调,编译器将无法知道使用accel_tgyro_t进行调用是否正确。对于您的代码来说,要知道它会比较棘手,可能需要额外的数据。

但是要获得代码重用,我们可以使用一个容器元组,每次我们要使用一个容器时都选择正确的容器:

#include <functional>
#include <vector>
#include <tuple>
#include <utility>

class MyClass
{
public:
    template <typename T,typename FuncT>
    auto subscribe(FuncT&& func)
        -> std::void_t<decltype(
           std::declval<const FuncT&>()(std::declval<const T&>()))>;

    template <typename T>
    void invoke_callbacks(const T& arg) const;

private:
    template <typename T>
    using storage_type_for = std::vector<std::function<void(const T&)>>;

    std::tuple<storage_type_for<accel_t>,storage_type_for<gyro_t> /* ... */ >
        m_callbacks;
};

template <typename T,typename FuncT>
auto MyClass::subscribe(FuncT&& func)
        -> std::void_t<decltype(
           std::declval<const FuncT&>()(std::declval<const T&>()))>
{
     std::get<storage_type_for<T>>(m_callbacks)
         .emplace_back(std::forward<FuncT>(func));
}

template <typename T>
void MyClass::invoke_callbacks(const T& arg) const
{
    for (const auto& cb : std::get<storage_type_for<T>>(m_callbacks))
        cb(arg);
}

请参见the full program example on coliru

这确实需要有效类型的已知列表。如果您真的确实需要能够为任何类型订阅回调,那是可能的,但要复杂得多。可能的改进包括:如果将“错误”类型指定为模板参数,则给出清晰的错误消息,和/或在某些情况下自动推导subscribe的模板参数,因此不必始终指定它。 / p>