c++ - 正确使用模板将函数传递给线程池

标签 c++ templates c++17 decltype

我正在尝试制作一个线程池,它接受任何类型的函数,并返回函数可能具有的任何返回值/异常的 future 。我这样做主要是为了学习现代线程和一些模板编程。我尝试将我的语法松散地基于 MSVC 如何执行 std::function 和 futures。

这是我的问题所在的最短片段:

#include <functional>
#include <future>
#include <utility>
#include <queue>
#include <memory>

using Job = std::function<void()>;

std::queue<std::unique_ptr<Job>> queue;

template<typename FuncType, typename... Args>
auto add(FuncType&& func, Args&&... args) ->std::future<decltype(func)(decltype(args)...)> {
    auto task = std::packaged_task<decltype(func)(decltype(args)...)>(std::bind (std::forward<FuncType>(func), std::forward<Args>(args)...));
    auto future = task.get_future();

    queue.push(std::make_unique<Job>([task]() { task(); }));

    return future;
}

void voidFunc(){};

int main()
{
    add(voidFunc);
}

编译失败,出现以下错误:

    /usr/include/c++/4.9/future: In instantiation of 'class std::future<void (&())()>':
28:17:   required from here
/usr/include/c++/4.9/future:697:7: error: function returning a function
       get()
       ^
 In instantiation of 'add(FuncType&&, Args&& ...)::<lambda()> [with FuncType = void (&)(); Args = {}]':
19:37:   required from 'struct add(FuncType&&, Args&& ...) [with FuncType = void (&)(); Args = {}; decltype (func) = void (&)()]::<lambda()>'
19:56:   required from 'std::future<decltype (func)(decltype (args)...)> add(FuncType&&, Args&& ...) [with FuncType = void (&)(); Args = {}; decltype (func) = void (&)()]'
28:17:   required from here
19:52: error: passing 'const std::packaged_task<void (&())()>' as 'this' argument of 'void std::packaged_task<_Res(_ArgTypes ...)>::operator()(_ArgTypes ...) [with _Res = void (&)(); _ArgTypes = {}]' discards qualifiers [-fpermissive]
 In instantiation of 'std::future<decltype (func)(decltype (args)...)> add(FuncType&&, Args&& ...) [with FuncType = void (&)(); Args = {}; decltype (func) = void (&)()]':
28:17:   required from here
19:36: error: use of deleted function 'std::packaged_task<_Res(_ArgTypes ...)>::packaged_task(const std::packaged_task<_Res(_ArgTypes ...)>&) [with _Res = void (&)(); _ArgTypes = {}]'
In file included from 5:0:
/usr/include/c++/4.9/future:1413:7: note: declared here
       packaged_task(const packaged_task&) = delete;
       ^
21:10: error: could not convert 'future' from 'std::future<void (&)()>' to 'std::future<void (&())()>'
In file included from 5:0:
/usr/include/c++/4.9/future: In instantiation of 'static std::__future_base::_Task_setter<_Res_ptr> std::__future_base::_S_task_setter(_Res_ptr&, _BoundFn&&) [with _Res_ptr = std::unique_ptr<std::__future_base::_Result<void (&)()>, std::__future_base::_Result_base::_Deleter>; _BoundFn = std::_Bind_simple<std::reference_wrapper<std::_Bind<void (*())()> >()>; typename _Res_ptr::element_type::result_type = void (&)()]':
/usr/include/c++/4.9/future:1318:70:   required from 'void std::__future_base::_Task_state<_Fn, _Alloc, _Res(_Args ...)>::_M_run(_Args ...) [with _Fn = std::_Bind<void (*())()>; _Alloc = std::allocator<int>; _Res = void (&)(); _Args = {}]'
29:1:   required from here
/usr/include/c++/4.9/future:539:57: error: could not convert 'std::ref(_Tp&) [with _Tp = std::_Bind_simple<std::reference_wrapper<std::_Bind<void (*())()> >()>]()' from 'std::reference_wrapper<std::_Bind_simple<std::reference_wrapper<std::_Bind<void (*())()> >()> >' to 'std::function<void (&())()>'
  return _Task_setter<_Res_ptr>{ __ptr, std::ref(__call) };
                                                         ^
In file included from /usr/include/c++/4.9/memory:81:0,
                 from /usr/include/c++/4.9/thread:40,
                 from /usr/include/c++/4.9/future:40,
                 from 5:
/usr/include/c++/4.9/bits/unique_ptr.h:764:5: error: 'typename std::_MakeUniq<_Tp>::__single_object std::make_unique(_Args&& ...) [with _Tp = std::function<void()>; _Args = {add(FuncType&&, Args&& ...) [with FuncType = void (&)(); Args = {}; decltype (func) = void (&)()]::<lambda()>}; typename std::_MakeUniq<_Tp>::__single_object = std::unique_ptr<std::function<void()> >]', declared using local type 'add(FuncType&&, Args&& ...) [with FuncType = void (&)(); Args = {}; decltype (func) = void (&)()]::<lambda()>', is used but never defined [-fpermissive]
     make_unique(_Args&&... __args)
     ^
 In function 'std::future<decltype (func)(decltype (args)...)> add(FuncType&&, Args&& ...) [with FuncType = void (&)(); Args = {}; decltype (func) = void (&)()]':
22:2: warning: control reaches end of non-void function [-Wreturn-type]

我想我在这里有两个问题:我不知道如何正确使用 decltype 来获得正确的函数签名(我也研究过 invoke_result,但我在那里也没有任何运气),并且我我想我可能也没有将打包的任务正确传递到队列。

如何为 future 和打包的任务获得正确的函数签名,以及如何将打包的任务正确地传递给队列上的 std::function(稍后将被另一个线程获取)?

最佳答案

两个问题:

1) 打包任务的签名应该是 std::packaged_task<std::invoke_result_t<Func&&,Args&&...>(Args&&...)> .这使用 invoke_result_t计算返回类型,但也将参数类型传递给打包任务。

2) 更大的问题:std::function要求该函数是可复制构造的,这 std::packaged_task不是。您必须创建自己的队列来保存打包的任务。在使用带有模板化派生类的基类 Task 来保存打包任务之前,我已经实现了它。

关于c++ - 正确使用模板将函数传递给线程池,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51459414/

相关文章:

c++ - 如何测试B类是否派生自A类?

c++ - 在C++17中移动/复制构造函数/赋值是否为 "deleted"或 "not declared"是否成立?

c++ - 一个静态对象,其析构函数是作用域感知的

c++ - 简单的Opengl程序不起作用,因为它应该

c++ - 在 Visual Studio 2008 和 Windows XP 中使用 libxml2

具有特定成员方法的 C++ 模板类型

c++ - 为什么 std::vector<std::string> 中的字符串最终具有相同的数据地址?

c++ - 正确的完全特化模板类的前向声明

c++ - CRTP 模式不会触发完整模板实例化

c++ - 删除 vector 中的对象时出错。如何将移动赋值运算符添加到我的类中?