【问题标题】:Boost::ASIO and std::packaged_taskBoost::ASIO 和 std::packaged_task
【发布时间】:2016-06-07 07:33:42
【问题描述】:

我有以下 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 文档,这是可以做到的。有什么想法吗?

【问题讨论】:

    标签: c++ c++11 boost boost-asio variadic-templates


    【解决方案1】:

    来自post() 的文档:

    处理程序

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

    您正在传递std::shared_ptr&lt;std::packaged_task&lt;int()&gt;&gt;shared_ptr 没有定义 operator()packaged_task unwrapped 是不可复制的。

    因此,为了完成这项工作,您必须创建一个shared_ptr&lt;promise&gt;

    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 调用post() 函数,我也会得到同样的错误...即--auto task = std::packaged_task&lt;rType()&gt;(std::bind(std::forward&lt;F&gt;(f), std::forward&lt;Args&gt;(args)...));service_.post(std::move(task));
    • 我猜现在的问题是operator() 有一个非void 返回类型
    • @subzero packaged_task::operator() 绝对是 void,我想知道他们正在做的概念检查是否有问题(比如它要求类型可以在某处复制)。跨度>
    • 将任务包装在 void 返回类型 lambda 似乎可行...auto task = std::make_shared&lt;std::packaged_task&lt;rType()&gt;&gt;(std::bind(std::forward&lt;F&gt;(f), std::forward&lt;Args&gt;(args)...)); std::future&lt;rType&gt; res = task-&gt;get_future(); service_.post([=] () { task-&gt;operator()(); });
    • 哇,“io_service 会复制处理程序”——我看不懂。
    【解决方案2】:

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

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

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

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

    非 void 返回类型是一个红鲱鱼 - 不要浪费你的时间。

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

    【讨论】:

      【解决方案3】:

      通过最近的提升,您可以在没有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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-11-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-09-02
        • 1970-01-01
        • 2017-10-23
        相关资源
        最近更新 更多