I am using Boost 1.66.0, in which asio has built-in support for interoperating with futures (and for some time now). The examples I've seen online indicate how to achieve this cleanly when using networking functions such as async_read
, async_read_some
, etc. That is done by providing boost::asio::use_future
in place of the completion handler, which causes the initiating function to return a future
as expected.
What kind of object do I need to provide or wrap my function in to get the same behavior from boost::asio::post
?
My purpose for posting the work is to execute it in the context of a strand but otherwise wait for the work to complete, so I can get the behavior I want doing:
std::packaged_task<void()> task( [] { std::cout << "Hello world\n"; } );
auto f = task.get_future();
boost::asio::post(
boost::asio::bind_executor(
strand_, std::move( task ) ) );
f.wait();
but according to the boost::asio
documentation, the return type for boost::asio::post
is deduced in the same way as for functions like boost::asio::async_read
, so I feel like there has to be a nicer way that can avoid the intermediate packaged_task
. Unlike async_read
there is no "other work" to be done by post
so providing just boost::asio::use_future
doesn't makes sense, but we could define an async_result
trait to get the same behavior for post.
Is there a wrapper or something that has the necessary traits defined to get the behavior I want or do I need to define it myself?
@MartiNitro's idea with packaged_task
has become part of the library: now you can just post a packaged_task
and it will magically return its future:
auto f = post(strand_, std::packaged_task<int()>(task));
Thanks to @niXman's comment, discovered a more convenient interface on the use_future
token:
auto f = post(ex, boost::asio::use_future(task));
#include <boost/asio.hpp>
#include <iostream>
#include <future>
using namespace std::chrono_literals;
int task() {
std::this_thread::sleep_for(1s);
std::cout << "Hello world\n";
return 42;
}
int main() {
boost::asio::thread_pool ioc(1);
auto ex = ioc.get_executor();
auto f = post(ex, std::packaged_task<int()>(task));
// optionally wait for future:
f.wait();
// otherwise .get() would block:
std::cout << "Answer: " << f.get() << "\n";
f = post(ex, boost::asio::use_future(task));
f.wait();
std::cout << "Second answer: " << f.get() << "\n";
ioc.join();
}
Prints
Hello world
Answer: 42
Hello world
Second answer: 42