Boost::ASIO and std::packaged_task

Boost::ASIO and std::packaged_task

本文关键字:task packaged and ASIO Boost std      更新时间:2023-10-16

我有以下 C++14 代码

boost::asio::io_service service_;

我想将工作片段提交到io_service中,使用以下代码,该代码接受任何函数,它是输入参数并返回返回值的std::future

template <typename F, typename... Args>
auto enqueue(F &&f, Args &&... args) -> std::future<typename std::result_of<F(Args...)>::type> {
  typedef typename std::result_of<F(Args...)>::type rType;
  auto task = std::make_shared<std::packaged_task<rType()>>(std::bind(std::forward<F>(f),
                                                                       std::forward<Args>(args)...));
  std::future<rType> res = task->get_future();
  service_.post(task);
  return res;
}

然后这被称为使用

enqueue([] (int i) {
  return i+1;
}, 100);

这似乎不起作用。我收到一个错误,说service_.post()没有预料到这个输入。

xxxxxxxxxxxxxx:49:3:   required from ‘std::future<typename std::result_of<_Functor(_ArgTypes ...)>::type> enqueue(F&&, Args&& ...) [with F = main()::<lambda()>::<lambda()>; Args = {int}; typename std::result_of<_Functor(_ArgTypes ...)>::type = int]’
xxxxxxxxxxxxxx:44:6:   required from here
/usr/include/boost/asio/impl/io_service.hpp:102:3: error: static assertion failed: CompletionHandler type requirements not met
   BOOST_ASIO_COMPLETION_HANDLER_CHECK(CompletionHandler, handler) type_check;
   ^
/usr/include/boost/asio/impl/io_service.hpp:85:3: error: no match for call to ‘(std::shared_ptr<std::packaged_task<void()> >) ()’
   BOOST_ASIO_COMPLETION_HANDLER_CHECK(CompletionHandler, handler) type_check;
   ^

据我了解boost::asio文档,这是可以做到的。有什么想法吗?

来自post()的文档:

处理器

要调用的处理程序。io_service将根据需要创建处理程序对象的副本。处理程序的函数签名必须是:void handler();

你正在通过一个std::shared_ptr<std::packaged_task<int()>>.shared_ptr没有定义operator()。而且packaged_task拆包是不可复制的。

因此,为了使这项工作,您必须制作一个shared_ptr<promise>

using R = std::result_of_t<F(Args&&...)>;
auto promise = std::make_shared<std::promise<R>>();
std::future<R> res = promise->get_future();
service.post([promise = std::move(promise),
    f = std::forward<F>(f),
    args = std::make_tuple(std::forward<Args>(args)...)]{
        promise->set_value(std::experimental::apply(f, args));    
    });
return res;

我似乎记得自己也被这个咬过。这是因为std::packaged_task不可复制(显式删除复制构造函数)。

这是因为它包含一个承诺,这个承诺也是不可复制的——只能移动。

asio::io_service要求处理程序对象是可复制的。

您可能需要考虑构建自己的类似打包任务的函数对象,以保持对承诺shared_ptr。那将是可复制的。

非空返回类型是红鲱鱼 - 不要浪费时间。

打包任务的文档在这里:http://en.cppreference.com/w/cpp/thread/packaged_task/packaged_task

通过最近的提升,您可以在没有shared_ptr的情况下执行此操作,如下所示。 您需要进行两项修改:

    使用
  1. boost::asio::post 而不是使用(已弃用的)io_service或较新io_context的(已弃用)post函数
  2. 由于我不知道的原因,packaged_task需要用std::bind包裹。 否则,将在运行时引发异常。
template <typename F, typename... Args>
auto enqueue(boost::asio::io_service& service_, F &&f, Args &&... args)
    -> std::future<typename std::result_of<F(Args...)>::type>
{
  typedef typename std::result_of<F(Args...)>::type rType;
  auto task = std::packaged_task<rType()>(std::bind(std::forward<F>(f),
     std::forward<Args>(args)...));
  std::future<rType> res = task.get_future();
  // Note the std::bind.  Without it the program would throw "future already received"
  boost::asio::post(service_.get_executor(), std::bind(std::move(task)));
  return res;
}

可以在此处找到一个自包含的示例:https://godbolt.org/z/zE4qWbh1d