c++ - 在模板函数 C++ 中的 F&&,Args &&... 参数之后添加另一个函数作为参数
问题描述
我正在为我的 EventLoop 类编写一个异步成员函数,它将繁重的计算任务发布到全局线程池,然后获取该任务的结果并将完成回调函数排队,该函数将先前工作的结果作为参数返回到 EventLoop 以让 eventloop 稍后处理(调用)完成回调。
所以一般 async 成员函数应该接受两个函数作为参数,第一个被设计为任意返回类型的任务函数,第二个是一个完成回调,以第一个函数的返回值作为参数。
我的第一次尝试代码如下:
class EventLoop
{
public:
using DeferCallback=std::function<void()>;
//call by another thread to queue work into the eventloop
void queue_work(DeferCallback cb);
template<typename F,typename ...Args>
void async(F &&task_func,Args&& ...args,std::function<void(std::invoke_result_t<F,Args...>&)> &&finish_cb){
g_threadpool::get_instance()->post([this,task_func,args...,finish_cb](){
using task_ret_t=std::invoke_result_t<F,Args...>;
task_ret_t res=task_func(args...);
this->queue_work([finish_cb,&res](){
finish_cb(res);
});
});
}
//for task func of void return type
template<typename F,typename ...Args>
void async(F &&task_func,Args&& ...args,std::function<void(void)> &&finish_cb){
g_threadpool::get_instance()->post([this,task_func,args...,finish_cb](){
task_func(args...);
this->queue_work([finish_cb](){
finish_cb();
});
});
}
我测试并发现它仅在我不向 ...args 传递任何内容或编译器无法正常工作时才有效。然后我做了一些搜索,发现以下问题:参数包必须在参数列表的末尾......何时以及为什么? 它基本上告诉我:
[...]如果主类模板或别名 >template 的模板参数是模板参数包,它应该是最后一个模板->参数。[...]
如果我必须显式实例化异步函数,它将很难使用。
然后我试试这个:
template<typename Ret,typename ...Args>
void async(std::function<Ret(Args...)> &&task_func,std::function<void(Ret&)> &&finish_cb){ ... }
并发现除了 std::function 之外,我不能将 lambda 函数或成员函数传递给第一个参数,这并不理想。相关问题:从 std::function 调用签名推断模板参数
那么有什么办法可以解决吗?
解决方案
如果我必须显式实例化异步函数,它将很难使用。
在您的情况下,这特别困难,因为鉴于签名
template <typename F, typename ... Args>
void async (F &&task_func,
Args && ...args,
std::function<void(std::invoke_result_t<F,Args...>&)> &&finish_cb)
(并忽略Args... args
应该在最后一个位置的问题)两者F
和Args...
也存在于最后一个参数的类型(finish_cb
)中,因此从最后一个参数推导两者F
和Args...
也依赖于必须与前面的参数一致。
因此,例如,您不能将 lambda 传递给最后一个参数,因为 lambda 可以转换为 astd::function
但不是 a std::function
,因此F
不能Args...
从 lambda 推导出来。
那么有什么办法可以解决吗?
建议:完全避免std::function
并将两个可调用参数放在前面,后面的(可变参数)参数放在后面。
我的意思是......如下所示(但要注意:代码未经测试,完美转发被忽略)
template <typename F1, typename F2, typename ...Args>
void async (F1 && task_func, F2 && finish_cb, Args && ... args) {
g_threadpool::get_instance()->post([this,task_func,args...,finish_cb](){
auto res=task_func(args...);
this->queue_work([finish_cb,&res](){
finish_cb(res);
});
});
}
如果您的问题是您需要两个版本的async()
,一个F1
返回void
(不返回值)和一个F1
返回值,您可以std::invoke_result_t
与 SFINAE 一起使用
// void (no res) version
template <typename F1, typename F2, typename ...Args>
std::enable_if_t<true == std::is_same_v<void, std::invoke_result_t<F,Args...>>>
async (F1 && task_func, F2 && finish_cb, Args && ... args)
{ /* do something */ }
// non-void (with res) version
template <typename F1, typename F2, typename ...Args>
std::enable_if_t<false == std::is_same_v<void, std::invoke_result_t<F,Args...>>>
async (F1 && task_func, F2 && finish_cb, Args && ... args)
{ /* do something */ }
推荐阅读
- java - 如何在给定字符串中获取 \" 字符串的正则表达式
- sql - 尝试在 SQL 中插入多行时出现语法错误?
- c# - 如何仅在当前页面顶部/覆盖当前页面上显示 ProgressRing 控件?
- vue.js - VueJs CLI 3 - 如何添加外部库
- java - Hibernate 在整个 Spring Boot 应用程序上设置属性访问策略
- java - Java Base64.getDecoder().decode() c# 等效
- solr - Solr 数据导入 12/12 已获取但 0/12 已处理
- python-3.x - py2app 捆绑系统 MacOSX python 2.7 而不是 python 3
- java - 如何让用户保持登录到 WebView 应用程序?
- ddev - 让两个 ddev 容器相互卷曲