I would like to use boost::future
with continuations and boost::when_all
/ boost::when_any
.
Boost trunk - not 1.55 - includes implementations for the latter (modeled after the proposal here, upcoming for C++14/17 and Boost 1.56).
This is what I have (and it does not compile):
#include <iostream>
#define BOOST_THREAD_PROVIDES_FUTURE
#define BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION
#define BOOST_THREAD_PROVIDES_FUTURE_WHEN_ALL_WHEN_ANY
#include <boost/thread/future.hpp>
using namespace boost;
int main() {
future<int> f1 = async([]() { return 1; });
future<int> f2 = async([]() { return 2; });
auto f3 = when_all(f1, f2);
f3.then([](decltype(f3)) {
std::cout << "done" << std::endl;
});
f3.get();
}
Clang 3.4 bails out with a this - here is an excerpt:
/usr/include/c++/v1/memory:1685:31: error: call to deleted constructor of 'boost::future<int>'
::new((void*)__p) _Up(_VSTD::forward<_Args>(__args)...);
Am I doing it wrong or is this a bug?
The problem is that when_all
may only be called with rvalue future
or shared_future
. From N3857:
template <typename... T> see below when_all(T&&... futures);
Requires:
T
is of typefuture<R>
orshared_future<R>
.
Thanks to the reference-collapsing rules, passing an lvalue results in T
being deduced to future<T>&
in violation of the stated requirement. The boost implementation doesn't check this precondition so you get an error deep in the template code where what should be a move of an rvalue future turns into an attempted copy of an lvalue future.
You need to either move the futures into the when_all
parameters:
auto f3 = when_all(std::move(f1), std::move(f2));
or avoid naming them in the first place:
auto f = when_all(async([]{return 1;}),
async([]{return 2;}));
Also, you must get
the future returned from then
instead of the intermediate future:
auto done = f.then([](decltype(f)) {
std::cout << "done" << std::endl;
});
done.get();
since the future upon which you call then
is moved into the parameter of the continuation. From the description of then
in N3857:
Postcondition:
The
future
object is moved to the parameter of the continuation function
valid() == false
on originalfuture
object immediately after it returns
Per 30.6.6 [futures.unique_future]/3:
The effect of calling any member function other than the destructor, the move-assignment operator, or
valid
on afuture
object for whichvalid() == false
is undefined.
You could avoid most of these issues in c++14 by avoiding naming the futures at all:
when_all(
async([]{return 1;}),
async([]{return 2;})
).then([](auto&) {
std::cout << "done" << std::endl;
}).get();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With