模板回调的包装器?
Wrapper for template callback?
>立即回调
请考虑以下示例:
template <typename lambda> void call_me_back(const lambda & callback)
{
// Very complicated calculation to find the meaning of everything
callback(42);
}
int main()
{
call_me_back([](const int & value)
{
std :: cout << value << std :: endl;
});
}
在这里,我为call_me_back
提供了一个接受int
的lambda。 经过长时间的计算后,call_me_back
调用callback
,callback
将其打印出来。像蛋糕一样简单。
现在,假设根据执行,call_me_back
需要使用int
或其他类型调用callback
。我们可以将前面的示例编辑为
template <typename lambda> void call_me_back(const lambda & callback)
{
// Very complicated calculation to find the meaning of everything
if(rand() % 2)
callback(42);
else
callback("Kill all the humans.");
}
int main()
{
call_me_back([](const auto & value)
{
std :: cout << value << std :: endl;
});
}
目前为止,一切都好。现在callback
可以根据其类型执行各种技巧并处理value
。
延迟回调
现在,回到第一个示例。说call_me_back
不准备立即用int
打电话给callback
.但是,它可以做的是将callback
存储在某个地方,然后稍后调用它。
例如:
std :: function <void(const int &)> the_callback;
template <typename lambda> void call_me_back(const lambda & callback)
{
the_callback = callback;
}
void ready_to_answer()
{
the_callback(42);
}
int main()
{
call_me_back([](const auto & value)
{
std :: cout << value << std :: endl;
});
}
现在,call_me_back
不是立即调用callback
,而是将callback
存储在std :: function <void(const int &)>
对象中(我知道,它在全局范围内,请耐心等待(。然后可能会发生许多事情,在某些时候有人可以调用ready_to_answer
,这会检索以前存储的回调并调用它。例如,ready_to_answer
可以由另一个线程调用,但是有很多原因需要这样的范式,其中回调可以存储并在以后调用。
问题所在
如果我想实现第二个示例,但有延迟回调怎么办?我似乎无法解决这个问题。
我可以想象std :: function
是用接受某种特定类型的虚拟呼叫运营商实现的。然后,std :: function
包装指向模板包装器类的指针/引用,该类存储实际的 lambda,并通过将其参数转发到它存储的 lambda 来实现调用运算符。很好,很容易。但是我不能有模板虚拟方法!
我尝试过提出各种解决方案,但我找不到任何可以合理工作的东西。这真的不可能做到吗?难道不可能将一些外部提供的 lambda 接受const auto &
参数存储在某处,然后稍后调用它吗?
你是对的,无限的类型集是不可能的,但如果你事先知道所有类型,你可以做到:
std :: function <void(const std :: variant<int, std :: string> &)> the_callback;
template <typename lambda> void call_me_back(const lambda & callback)
{
the_callback = [callback](const auto & arg)
{
std :: visit(callback, arg);
};
}
template <typename T> void ready_to_answer(const T & x)
{
the_callback(x);
}
int main()
{
call_me_back([](const auto & value)
{
std :: cout << value << std :: endl;
});
if (std :: rand() % 2)
{
ready_to_answer(42);
}
else
{
ready_to_answer("Hi!");
}
}
在第二个示例中,您不传递函数,而是使用两个方法operator()(int)
和operator(char const (&)[..])
传递对匿名 lambda 对象的引用。因此,要存储这种回调,您需要复制 lambda 对象或存储特定的回调方法,即使用多个具有相应签名的::std::function
。实际上,为这两种情况显式传递两个回调会更清晰。
std::function< void (int) > delalyed_int_cb;
std::function< void (const char *) > delalyed_str_cb;
template< typename callable_taking_int, typename callable_taking_string > void
call_me_back(callable_taking_int && int_cb, callable_taking_string && str_cb)
{
delalyed_int_cb = std::forward< callable_taking_int >(int_cb);
delalyed_str_cb = std::forward< callable_taking_str >(str_cb);
...
}
void
ready_to_answer()
{
if(rand() % 2)
{
delalyed_int_cb(42);
}
else
{
delalyed_str_cb("Kill all the humans.");
}
}
您可以实现一个为您存储 lambda 的类模板。
#include<iostream>
template<typename L>
struct Telephone
{
L l;
Telephone(L l) : l{std::move(l)} {}
template<typename... Args>
decltype(auto) ready_to_answer(Args&&... args)
{
return l(std::forward<Args>(args)...);
}
};
template<typename L>
auto call_me_back(L&& l)
{
return Telephone<std::decay_t<L>>(std::forward<L>(l));
}
int main()
{
auto telephone = call_me_back([](auto x){ std::cout << x << std::endl; });
telephone.ready_to_answer(42);
telephone.ready_to_answer("Kill all the humans.");
}
这样做的缺点是Telephone
模板化在 lambda 上,并且对于每个 lambda 都是不同的类型。
如果您事先知道函数签名的外观,则可以Telephone
从公共基类继承并具有虚拟的非模板方法。
对Telephone
进行一些自动构建和管理,您基本上实现了另一种std::function
但具有自定义签名
struct Phone
{
// all Telephone<L> inherits from Phone and have these methods
virtual void ready_to_answer(int) = 0;
virtual void ready_to_answer(const char*) = 0;
};
struct Spouse
{
std::unique_ptr<Phone> phone;
template<typename L>
Spouse(L&& l) : phone{ new Telephone<std::decay_t<L>>{std::forward<L>(l)} } {}
void ready_to_answer(int i) { phone->ready_to_answer(i); }
void ready_to_answer(const char* str) { phone->ready_to_answer(str); }
};
编辑:我继续实施了一个std::function
的增强版本,它接受任意数量的签名,所以你可以写
function<void (int), void (std::string)> callback =
[](auto x){std::cout << x << std::endl;};
callback(42);
callback("Kill all humans!");
住
可以说实现不是微不足道的,这里要解释的太多了。
- 架构决策:返回std::future还是提供回调
- 正在为Xtensa simcall函数编写回调函数
- 如何在C++中使用非静态成员函数作为回调函数
- FLTK:按下哪个按钮 - 将数字传递给按钮的回调 (lambda)
- 在简单示例中,Python3 + ctypes 回调会导致内存泄漏
- 用于在回调中调用解析器的设计模式
- 如何使用C++对象的成员函数作为 C 样式回调?
- Java从C++回调到C++回调
- 如何将成员函数作为回调参数传递给需要"typedef-ed"自由函数指针的函数?
- 从不同的 cpp 调用回调函数会导致bad_function_call
- std::包含 std::函数回调的多个包装器的向量不起作用
- 模板回调的包装器?
- CPP成员对C回调包装器的反馈
- 用C++lambda包装C回调,可以使用模板多态性
- C++ RapidJSON 解析包装在回调函数中的 JSON 对象
- 在类中包装freeglut回调的最优雅方法
- 如何将包装的C++对象传递给 Javascript 回调
- 使用 C++11 接口包装 C 回调的最佳方法是什么?
- 装饰器模式:如何从核心类中调用(回调)装饰器包装器的一些成员
- 用于包装类方法的回调函数和static_cast