在模板函数 C++ 中的 F&&,Args &&... 参数之后添加另一个函数作为参数
Adding another function as a parameter after F&&,Args &&... parameters in a template function C++
我正在为我的 EventLoop class 编写一个异步成员函数,它将繁重的计算任务发布到全局线程池,然后获取该任务的结果并将完成回调函数排队将前面工作的结果作为参数返回到 EventLoop,让 eventloop 稍后处理(调用)finish 回调。
所以一般async成员函数应该带两个函数作为参数,第一个被设计成任何return类型的任务函数,第二个是一个finish回调,它以return 第一个函数的值作为参数。
我第一次尝试代码如下:
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 时才有效,否则编译器无法正常工作。然后我做了一些搜索并找到了以下问题:
它基本上告诉我:
[...]If a template-parameter of a primary class template or alias >template is a template parameter pack, it shall be the last template->parameter.[...]
而且如果我必须显式实例化异步函数,它将很难使用。
那我试试这个:
template<typename Ret,typename ...Args>
void async(std::function<Ret(Args...)> &&task_func,std::function<void(Ret&)> &&finish_cb){ ... }
并发现除了 std::function 之外,我无法将 lambda 函数或成员函数传递给第一个参数,这并不理想。相关问题:Deduce template argument from std::function call signature
那么有什么办法解决吗?
And if I have to explicitly instantiate the async function it will be hard to use.
在你的情况下这特别困难,因为给定签名
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 可以转换为 std::function
但不是 std::function
,因此 F
和 Args...
无法从 lambda 中推导出来。
So is there any way to work around?
建议:完全避免 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
return void
(return 不是一个值)和一个 F1
return 值,您可以将 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 */ }
我正在为我的 EventLoop class 编写一个异步成员函数,它将繁重的计算任务发布到全局线程池,然后获取该任务的结果并将完成回调函数排队将前面工作的结果作为参数返回到 EventLoop,让 eventloop 稍后处理(调用)finish 回调。
所以一般async成员函数应该带两个函数作为参数,第一个被设计成任何return类型的任务函数,第二个是一个finish回调,它以return 第一个函数的值作为参数。
我第一次尝试代码如下:
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 时才有效,否则编译器无法正常工作。然后我做了一些搜索并找到了以下问题:
[...]If a template-parameter of a primary class template or alias >template is a template parameter pack, it shall be the last template->parameter.[...]
而且如果我必须显式实例化异步函数,它将很难使用。
那我试试这个:
template<typename Ret,typename ...Args>
void async(std::function<Ret(Args...)> &&task_func,std::function<void(Ret&)> &&finish_cb){ ... }
并发现除了 std::function 之外,我无法将 lambda 函数或成员函数传递给第一个参数,这并不理想。相关问题:Deduce template argument from std::function call signature
那么有什么办法解决吗?
And if I have to explicitly instantiate the async function it will be hard to use.
在你的情况下这特别困难,因为给定签名
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 可以转换为 std::function
但不是 std::function
,因此 F
和 Args...
无法从 lambda 中推导出来。
So is there any way to work around?
建议:完全避免 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
return void
(return 不是一个值)和一个 F1
return 值,您可以将 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 */ }