问题
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.
回答1:
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();
}
回答2:
Thanks to @PSIAlt and @free_coffee I know how to use callback functions in stackful coroutine.
Here is a simple example just for asio newbies(like me :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;
}
回答3:
Great thanks to free_coffe i managed this to work. Posting solution for my case, possibly someone need it.
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();
}
Later we can use this proxy:
RequestResult res = async_foo(pkt, std::forward<boost::asio::yield_context>(yield) );
来源:https://stackoverflow.com/questions/24497881/boostasiospawn-yield-as-callback