PSIAlt PSIAlt - 17 days ago 9
C++ Question

boost::asio::spawn yield as callback

I'm trying to rewrite a project using

boost::asio::spawn
coroutines. Some parts of the project cannot be changed. For example, the storage protocol library is also written with
boost::asio
, but without coroutines.

The problem is how to convert
yield_context
into a normal callback (a
boost::function
object or a classical functor).

This is what we have in the storage library API:

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


As we know from examples, the asio yield context can be used like this:

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


In this case a
boost::asio::yield_context
object serves as a callback for async_read_some. I would like to pass a yield object as the second argument to
async_request_data
, so i can use it in a synchronous manner.

How can this be done? I think it may be possible via some proxy-object, possibly using an approach based on asio_handler_invoke. But I am having trouble seeing how to do this.

Answer

Looks like the best documentation for this feature can be found in a C++ standard proposal written by the boost asio author:

N4045 – Library Foundations for Asynchronous Operations, Revision 2

See section 9.1 which says:

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

3: The completion token is converted into a handler, i.e. a function object to be called when the asynchronous operation completes. The signature specifies the arguments that will be passed to the handler.

I guess in your case the CompletionToken template argument will actually be boost::asio::yield_context and handler_type converts it to a callback object.


Here is the code from section 9.1 updated to call your async_request_data function:

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();  
}