问题描述
具体来说,假设我有结构accel_t
和gyro_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_t
或gyro_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>