boost :: asio :: spawn yield as callback

时间:2014-06-30 19:49:21

标签: c++ boost boost-asio coroutine

我尝试使用boost::asio::spawn协程重写项目。项目的某些部分无法更改。例如,存储协议库也使用boost::asio编写,但没有协同程序。

问题是如何将yield_context转换为普通回调(boost::function对象或经典仿函数)。

这就是我们在存储库API中所拥有的:

void async_request_data(uint64_t item_id, boost::function< void(Request_result *) > callback);

正如我们从示例中所知,asio yield上下文可以像这样使用:

    my_socket.async_read_some(boost::asio::buffer(data), yield);

在这种情况下,boost::asio::yield_context对象充当 async_read_some 的回调。我想将 yield 对象作为async_request_data的第二个参数传递给我,所以我可以以同步的方式使用它。

如何做到这一点?我认为可能通过一些代理对象,可能使用基于 asio_handler_invoke 的方法。但我很难看到如何做到这一点。

3 个答案:

答案 0 :(得分:13)

看起来这个功能的最佳文档可以在boost asio作者编写的C ++标准提案中找到:

N4045 – Library Foundations for Asynchronous Operations, Revision 2

参见第9.1节:

handler_type_t<CompletionToken, void(error_code, size_t)>   #3
  handler(std::forward<CompletionToken>(token));
  

3:完成标记被转换为处理程序,即要调用的函数对象   当异步操作完成时。签名指定参数   将被传递给处理程序。

我想在你的情况下CompletionToken模板参数实际上是boost::asio::yield_contexthandler_type将它转换为回调对象。


以下是9.1版中更新的代码,用于调用async_request_data函数:

template <class CompletionToken>
auto async_foo(uint64_t item_id, CompletionToken&& token)
{
  handler_type_t<CompletionToken, void(Request_result *)>
    handler(std::forward<CompletionToken>(token));

  async_result<decltype(handler)> result(handler);  

  async_request_data(item_id, handler);

  return result.get();  
}

答案 1 :(得分:6)

感谢@PSIAlt和@free_coffee我知道如何在堆栈协程中使用回调函数。

这是一个简单的例子,仅适用于asio新手(比如我:D)

https://gist.github.com/chenfengyuan/4d764b0bca82a42c05a9

#include <iostream>
#include <boost/asio.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>
#include <boost/asio/spawn.hpp>
#include <memory>

void bar(boost::asio::io_service &io, std::function<void()> cb){
    auto ptr = std::make_shared<boost::asio::deadline_timer>(io, boost::posix_time::seconds(1));
    ptr->async_wait([ptr, cb](const boost::system::error_code&){cb();});
}

template<typename Handler>
void foo(boost::asio::io_service &io, Handler && handler){
    typename boost::asio::handler_type<Handler, void()>::type handler_(std::forward<Handler>(handler));
    boost::asio::async_result<decltype(handler_)> result(handler_);
    bar(io, handler_);
    result.get();
    return;
}

int main()
{
  boost::asio::io_service io;
  boost::asio::spawn(io, [&io](boost::asio::yield_context yield){
      foo(io, yield);
      std::cout << "hello, world!\n";
  });

  io.run();

  return 0;
}

答案 2 :(得分:3)


非常感谢 free_coffe 我设法让它发挥作用。为我的案子发布解决方案,可能有人需要它。

template <class CompletionToken>
RequestResult async_foo(Packet &pkt, CompletionToken&& token) {
   typename boost::asio::handler_type< CompletionToken, void(RequestResult) >::type handler( std::forward<CompletionToken>(token) );
  boost::asio::async_result<decltype(handler)> result(handler);
  storage_api->writePacket(pkt, handler);
  return result.get();
}

稍后我们可以使用此代理:

RequestResult res = async_foo(pkt, std::forward<boost::asio::yield_context>(yield) );