return CoreCallbackState<T, _t<std::decay<F>>>(
std::move(p), std::forward<F>(f));
}
-} // namespace detail
-} // namespace futures
-
-template <class T>
-SemiFuture<typename std::decay<T>::type> makeSemiFuture(T&& t) {
- return makeSemiFuture(Try<typename std::decay<T>::type>(std::forward<T>(t)));
-}
-
-// makeSemiFutureWith(SemiFuture<T>()) -> SemiFuture<T>
-template <class F>
-typename std::enable_if<
- isSemiFuture<typename std::result_of<F()>::type>::value,
- typename std::result_of<F()>::type>::type
-makeSemiFutureWith(F&& func) {
- using InnerType =
- typename isSemiFuture<typename std::result_of<F()>::type>::Inner;
- try {
- return std::forward<F>(func)();
- } catch (std::exception& e) {
- return makeSemiFuture<InnerType>(
- exception_wrapper(std::current_exception(), e));
- } catch (...) {
- return makeSemiFuture<InnerType>(
- exception_wrapper(std::current_exception()));
- }
-}
-
-// makeSemiFutureWith(T()) -> SemiFuture<T>
-// makeSemiFutureWith(void()) -> SemiFuture<Unit>
-template <class F>
-typename std::enable_if<
- !(isSemiFuture<typename std::result_of<F()>::type>::value),
- SemiFuture<Unit::LiftT<typename std::result_of<F()>::type>>>::type
-makeSemiFutureWith(F&& func) {
- using LiftedResult = Unit::LiftT<typename std::result_of<F()>::type>;
- return makeSemiFuture<LiftedResult>(
- makeTryWith([&func]() mutable { return std::forward<F>(func)(); }));
-}
-
-template <class T>
-SemiFuture<T> makeSemiFuture(std::exception_ptr const& e) {
- return makeSemiFuture(Try<T>(e));
-}
template <class T>
-SemiFuture<T> makeSemiFuture(exception_wrapper ew) {
- return makeSemiFuture(Try<T>(std::move(ew)));
-}
-
-template <class T, class E>
-typename std::
- enable_if<std::is_base_of<std::exception, E>::value, SemiFuture<T>>::type
- makeSemiFuture(E const& e) {
- return makeSemiFuture(Try<T>(make_exception_wrapper<E>(e)));
-}
-
-template <class T>
-SemiFuture<T> makeSemiFuture(Try<T>&& t) {
- return SemiFuture<T>(new futures::detail::Core<T>(std::move(t)));
-}
-
-template <class T>
-SemiFuture<T> SemiFuture<T>::makeEmpty() {
- return SemiFuture<T>(futures::detail::EmptyConstruct{});
-}
-
-template <class T>
-SemiFuture<T>::SemiFuture(SemiFuture<T>&& other) noexcept : core_(other.core_) {
+FutureBase<T>::FutureBase(SemiFuture<T>&& other) noexcept : core_(other.core_) {
other.core_ = nullptr;
}
template <class T>
-SemiFuture<T>& SemiFuture<T>::operator=(SemiFuture<T>&& other) noexcept {
- std::swap(core_, other.core_);
- return *this;
-}
-
-template <class T>
-SemiFuture<T>::SemiFuture(Future<T>&& other) noexcept : core_(other.core_) {
+FutureBase<T>::FutureBase(Future<T>&& other) noexcept : core_(other.core_) {
other.core_ = nullptr;
}
-template <class T>
-SemiFuture<T>& SemiFuture<T>::operator=(Future<T>&& other) noexcept {
- std::swap(core_, other.core_);
- return *this;
-}
-
template <class T>
template <class T2, typename>
-SemiFuture<T>::SemiFuture(T2&& val)
+FutureBase<T>::FutureBase(T2&& val)
: core_(new futures::detail::Core<T>(Try<T>(std::forward<T2>(val)))) {}
template <class T>
template <typename T2>
-SemiFuture<T>::SemiFuture(
+FutureBase<T>::FutureBase(
typename std::enable_if<std::is_same<Unit, T2>::value>::type*)
: core_(new futures::detail::Core<T>(Try<T>(T()))) {}
class... Args,
typename std::enable_if<std::is_constructible<T, Args&&...>::value, int>::
type>
-SemiFuture<T>::SemiFuture(in_place_t, Args&&... args)
+FutureBase<T>::FutureBase(in_place_t, Args&&... args)
: core_(
new futures::detail::Core<T>(in_place, std::forward<Args>(args)...)) {
}
template <class T>
-SemiFuture<T>::~SemiFuture() {
- detach();
+template <class FutureType>
+void FutureBase<T>::assign(FutureType& other) noexcept {
+ std::swap(core_, other.core_);
}
-// This must be defined after the constructors to avoid a bug in MSVC
-// https://connect.microsoft.com/VisualStudio/feedback/details/3142777/out-of-line-constructor-definition-after-implicit-reference-causes-incorrect-c2244
-inline SemiFuture<Unit> makeSemiFuture() {
- return makeSemiFuture(Unit{});
+template <class T>
+FutureBase<T>::~FutureBase() {
+ detach();
}
template <class T>
-T& SemiFuture<T>::value() & {
+T& FutureBase<T>::value() & {
throwIfInvalid();
return core_->getTry().value();
}
template <class T>
-T const& SemiFuture<T>::value() const& {
+T const& FutureBase<T>::value() const& {
throwIfInvalid();
return core_->getTry().value();
}
template <class T>
-T&& SemiFuture<T>::value() && {
+T&& FutureBase<T>::value() && {
throwIfInvalid();
return std::move(core_->getTry().value());
}
template <class T>
-T const&& SemiFuture<T>::value() const&& {
+T const&& FutureBase<T>::value() const&& {
throwIfInvalid();
return std::move(core_->getTry().value());
}
template <class T>
-inline Future<T> SemiFuture<T>::via(Executor* executor, int8_t priority) && {
+inline Future<T> FutureBase<T>::via(Executor* executor, int8_t priority) && {
throwIfInvalid();
setExecutor(executor, priority);
}
template <class T>
-inline Future<T> SemiFuture<T>::via(Executor* executor, int8_t priority) & {
- throwIfInvalid();
- Promise<T> p;
- auto f = p.getFuture();
- auto func = [p = std::move(p)](Try<T>&& t) mutable {
- p.setTry(std::move(t));
- };
- using R = futures::detail::callableResult<T, decltype(func)>;
- thenImplementation<decltype(func), R>(std::move(func), typename R::Arg());
- return std::move(f).via(executor, priority);
-}
-
-template <class T>
-bool SemiFuture<T>::isReady() const {
+bool FutureBase<T>::isReady() const {
throwIfInvalid();
return core_->ready();
}
template <class T>
-bool SemiFuture<T>::hasValue() {
+bool FutureBase<T>::hasValue() {
return getTry().hasValue();
}
template <class T>
-bool SemiFuture<T>::hasException() {
+bool FutureBase<T>::hasException() {
return getTry().hasException();
}
template <class T>
-void SemiFuture<T>::detach() {
+void FutureBase<T>::detach() {
if (core_) {
core_->detachFuture();
core_ = nullptr;
}
template <class T>
-Try<T>& SemiFuture<T>::getTry() {
+Try<T>& FutureBase<T>::getTry() {
throwIfInvalid();
return core_->getTry();
}
template <class T>
-void SemiFuture<T>::throwIfInvalid() const {
+void FutureBase<T>::throwIfInvalid() const {
if (!core_) {
throwNoState();
-}
+ }
}
template <class T>
-Optional<Try<T>> SemiFuture<T>::poll() {
+Optional<Try<T>> FutureBase<T>::poll() {
Optional<Try<T>> o;
if (core_->ready()) {
o = std::move(core_->getTry());
}
template <class T>
-void SemiFuture<T>::raise(exception_wrapper exception) {
+void FutureBase<T>::raise(exception_wrapper exception) {
core_->raise(std::move(exception));
}
template <class T>
template <class F>
-void SemiFuture<T>::setCallback_(F&& func) {
+void FutureBase<T>::setCallback_(F&& func) {
throwIfInvalid();
core_->setCallback(std::forward<F>(func));
}
template <class T>
-SemiFuture<T>::SemiFuture(futures::detail::EmptyConstruct) noexcept
+FutureBase<T>::FutureBase(futures::detail::EmptyConstruct) noexcept
: core_(nullptr) {}
-template <class T>
-Future<T> Future<T>::makeEmpty() {
- return Future<T>(futures::detail::EmptyConstruct{});
-}
-
-template <class T>
-Future<T>::Future(Future<T>&& other) noexcept
- : SemiFuture<T>(std::move(other)) {}
-
-template <class T>
-Future<T>& Future<T>::operator=(Future<T>&& other) noexcept {
- SemiFuture<T>::operator=(SemiFuture<T>{std::move(other)});
- return *this;
-}
-
-template <class T>
-template <
- class T2,
- typename std::enable_if<
- !std::is_same<T, typename std::decay<T2>::type>::value &&
- std::is_constructible<T, T2&&>::value &&
- std::is_convertible<T2&&, T>::value,
- int>::type>
-Future<T>::Future(Future<T2>&& other)
- : Future(std::move(other).then([](T2&& v) { return T(std::move(v)); })) {}
-
-template <class T>
-template <
- class T2,
- typename std::enable_if<
- !std::is_same<T, typename std::decay<T2>::type>::value &&
- std::is_constructible<T, T2&&>::value &&
- !std::is_convertible<T2&&, T>::value,
- int>::type>
-Future<T>::Future(Future<T2>&& other)
- : Future(std::move(other).then([](T2&& v) { return T(std::move(v)); })) {}
-
-template <class T>
-template <
- class T2,
- typename std::enable_if<
- !std::is_same<T, typename std::decay<T2>::type>::value &&
- std::is_constructible<T, T2&&>::value,
- int>::type>
-Future<T>& Future<T>::operator=(Future<T2>&& other) {
- return operator=(
- std::move(other).then([](T2&& v) { return T(std::move(v)); }));
-}
-
-// TODO: isSemiFuture
-template <class T>
-template <class T2, typename>
-Future<T>::Future(T2&& val) : SemiFuture<T>(std::forward<T2>(val)) {}
-
-template <class T>
-template <typename T2>
-Future<T>::Future(typename std::enable_if<std::is_same<Unit, T2>::value>::type*)
- : SemiFuture<T>() {}
-
-template <class T>
-template <
- class... Args,
- typename std::enable_if<std::is_constructible<T, Args&&...>::value, int>::
- type>
-Future<T>::Future(in_place_t, Args&&... args)
- : SemiFuture<T>(in_place, std::forward<Args>(args)...) {}
-
-template <class T>
-Future<T>::~Future() {
-}
-
-// unwrap
-
-template <class T>
-template <class F>
-typename std::enable_if<isFuture<F>::value,
- Future<typename isFuture<T>::Inner>>::type
-Future<T>::unwrap() {
- return then([](Future<typename isFuture<T>::Inner> internal_future) {
- return internal_future;
- });
-}
-
// then
// Variant: returns a value
template <class T>
template <typename F, typename R, bool isTry, typename... Args>
typename std::enable_if<!R::ReturnsFuture::value, typename R::Return>::type
-SemiFuture<T>::thenImplementation(
+FutureBase<T>::thenImplementation(
F&& func,
futures::detail::argResult<isTry, F, Args...>) {
static_assert(sizeof...(Args) <= 1, "Then must take zero/one argument");
template <class T>
template <typename F, typename R, bool isTry, typename... Args>
typename std::enable_if<R::ReturnsFuture::value, typename R::Return>::type
-SemiFuture<T>::thenImplementation(
+FutureBase<T>::thenImplementation(
F&& func,
futures::detail::argResult<isTry, F, Args...>) {
static_assert(sizeof...(Args) <= 1, "Then must take zero/one argument");
return f;
}
+} // namespace detail
+} // namespace futures
+
+template <class T>
+SemiFuture<typename std::decay<T>::type> makeSemiFuture(T&& t) {
+ return makeSemiFuture(Try<typename std::decay<T>::type>(std::forward<T>(t)));
+}
+
+// makeSemiFutureWith(SemiFuture<T>()) -> SemiFuture<T>
+template <class F>
+typename std::enable_if<
+ isSemiFuture<typename std::result_of<F()>::type>::value,
+ typename std::result_of<F()>::type>::type
+makeSemiFutureWith(F&& func) {
+ using InnerType =
+ typename isSemiFuture<typename std::result_of<F()>::type>::Inner;
+ try {
+ return std::forward<F>(func)();
+ } catch (std::exception& e) {
+ return makeSemiFuture<InnerType>(
+ exception_wrapper(std::current_exception(), e));
+ } catch (...) {
+ return makeSemiFuture<InnerType>(
+ exception_wrapper(std::current_exception()));
+ }
+}
+
+// makeSemiFutureWith(T()) -> SemiFuture<T>
+// makeSemiFutureWith(void()) -> SemiFuture<Unit>
+template <class F>
+typename std::enable_if<
+ !(isSemiFuture<typename std::result_of<F()>::type>::value),
+ SemiFuture<Unit::LiftT<typename std::result_of<F()>::type>>>::type
+makeSemiFutureWith(F&& func) {
+ using LiftedResult = Unit::LiftT<typename std::result_of<F()>::type>;
+ return makeSemiFuture<LiftedResult>(
+ makeTryWith([&func]() mutable { return std::forward<F>(func)(); }));
+}
+
+template <class T>
+SemiFuture<T> makeSemiFuture(std::exception_ptr const& e) {
+ return makeSemiFuture(Try<T>(e));
+}
+
+template <class T>
+SemiFuture<T> makeSemiFuture(exception_wrapper ew) {
+ return makeSemiFuture(Try<T>(std::move(ew)));
+}
+
+template <class T, class E>
+typename std::
+ enable_if<std::is_base_of<std::exception, E>::value, SemiFuture<T>>::type
+ makeSemiFuture(E const& e) {
+ return makeSemiFuture(Try<T>(make_exception_wrapper<E>(e)));
+}
+
+template <class T>
+SemiFuture<T> makeSemiFuture(Try<T>&& t) {
+ return SemiFuture<T>(new futures::detail::Core<T>(std::move(t)));
+}
+
+// This must be defined after the constructors to avoid a bug in MSVC
+// https://connect.microsoft.com/VisualStudio/feedback/details/3142777/out-of-line-constructor-definition-after-implicit-reference-causes-incorrect-c2244
+inline SemiFuture<Unit> makeSemiFuture() {
+ return makeSemiFuture(Unit{});
+}
+
+template <class T>
+SemiFuture<T> SemiFuture<T>::makeEmpty() {
+ return SemiFuture<T>(futures::detail::EmptyConstruct{});
+}
+
+template <class T>
+SemiFuture<T>::SemiFuture(SemiFuture<T>&& other) noexcept
+ : futures::detail::FutureBase<T>(std::move(other)) {}
+
+template <class T>
+SemiFuture<T>::SemiFuture(Future<T>&& other) noexcept
+ : futures::detail::FutureBase<T>(std::move(other)) {
+ // SemiFuture should not have an executor on construction
+ if (this->core_) {
+ this->setExecutor(nullptr);
+ }
+}
+
+template <class T>
+SemiFuture<T>& SemiFuture<T>::operator=(SemiFuture<T>&& other) noexcept {
+ this->assign(other);
+ return *this;
+}
+
+template <class T>
+SemiFuture<T>& SemiFuture<T>::operator=(Future<T>&& other) noexcept {
+ this->assign(other);
+ // SemiFuture should not have an executor on construction
+ if (this->core_) {
+ this->setExecutor(nullptr);
+ }
+ return *this;
+}
+
+template <class T>
+Future<T> Future<T>::makeEmpty() {
+ return Future<T>(futures::detail::EmptyConstruct{});
+}
+
+template <class T>
+Future<T>::Future(Future<T>&& other) noexcept
+ : futures::detail::FutureBase<T>(std::move(other)) {}
+
+template <class T>
+Future<T>& Future<T>::operator=(Future<T>&& other) noexcept {
+ this->assign(other);
+ return *this;
+}
+
+template <class T>
+template <
+ class T2,
+ typename std::enable_if<
+ !std::is_same<T, typename std::decay<T2>::type>::value &&
+ std::is_constructible<T, T2&&>::value &&
+ std::is_convertible<T2&&, T>::value,
+ int>::type>
+Future<T>::Future(Future<T2>&& other)
+ : Future(std::move(other).then([](T2&& v) { return T(std::move(v)); })) {}
+
+template <class T>
+template <
+ class T2,
+ typename std::enable_if<
+ !std::is_same<T, typename std::decay<T2>::type>::value &&
+ std::is_constructible<T, T2&&>::value &&
+ !std::is_convertible<T2&&, T>::value,
+ int>::type>
+Future<T>::Future(Future<T2>&& other)
+ : Future(std::move(other).then([](T2&& v) { return T(std::move(v)); })) {}
+
+template <class T>
+template <
+ class T2,
+ typename std::enable_if<
+ !std::is_same<T, typename std::decay<T2>::type>::value &&
+ std::is_constructible<T, T2&&>::value,
+ int>::type>
+Future<T>& Future<T>::operator=(Future<T2>&& other) {
+ return operator=(
+ std::move(other).then([](T2&& v) { return T(std::move(v)); }));
+}
+
+// unwrap
+
+template <class T>
+template <class F>
+typename std::
+ enable_if<isFuture<F>::value, Future<typename isFuture<T>::Inner>>::type
+ Future<T>::unwrap() {
+ return then([](Future<typename isFuture<T>::Inner> internal_future) {
+ return internal_future;
+ });
+}
+
+template <class T>
+inline Future<T> Future<T>::via(Executor* executor, int8_t priority) & {
+ this->throwIfInvalid();
+ Promise<T> p;
+ auto f = p.getFuture();
+ auto func = [p = std::move(p)](Try<T>&& t) mutable {
+ p.setTry(std::move(t));
+ };
+ using R = futures::detail::callableResult<T, decltype(func)>;
+ this->template thenImplementation<decltype(func), R>(
+ std::move(func), typename R::Arg());
+ return std::move(f).via(executor, priority);
+}
template <typename T>
template <typename R, typename Caller, typename... Args>
return via(x).then(std::forward<Func>(func));
}
-template <class T>
-Future<T>::Future(futures::detail::EmptyConstruct) noexcept
- : SemiFuture<T>(futures::detail::EmptyConstruct{}) {}
-
// makeFuture
template <class T>
template <class T>
Future<T> Future<T>::delayed(Duration dur, Timekeeper* tk) {
return collectAll(*this, futures::sleep(dur, tk))
- .then([](std::tuple<Try<T>, Try<Unit>> tup) {
- Try<T>& t = std::get<0>(tup);
- return makeFuture<T>(std::move(t));
- });
+ .then([](std::tuple<Try<T>, Try<Unit>> tup) {
+ Try<T>& t = std::get<0>(tup);
+ return makeFuture<T>(std::move(t));
+ });
}
namespace futures {
Promise<T> promise;
auto ret = promise.getFuture();
auto baton = std::make_shared<FutureBatonType>();
- f.setCallback_([ baton, promise = std::move(promise) ](Try<T> && t) mutable {
+ f.setCallback_([baton, promise = std::move(promise)](Try<T>&& t) mutable {
promise.setTry(std::move(t));
baton->post();
});
}
template <class T>
-T SemiFuture<T>::get() {
+T SemiFuture<T>::get() && {
return std::move(wait().value());
}
template <class T>
-T SemiFuture<T>::get(Duration dur) {
+T SemiFuture<T>::get(Duration dur) && {
wait(dur);
if (this->isReady()) {
return std::move(this->value());
return std::move(*this);
}
+template <class T>
+T Future<T>::get() {
+ return std::move(wait().value());
+}
+
+template <class T>
+T Future<T>::get(Duration dur) {
+ wait(dur);
+ if (this->isReady()) {
+ return std::move(this->value());
+ } else {
+ throwTimedOut();
+ }
+}
+
template <class T>
T Future<T>::getVia(DrivableExecutor* e) {
return std::move(waitVia(e).value());
class Future;
template <class T>
-class SemiFuture {
+class SemiFuture;
+
+namespace futures {
+namespace detail {
+template <class T>
+class FutureBase {
public:
typedef T value_type;
- static SemiFuture<T> makeEmpty(); // equivalent to moved-from
-
- // not copyable
- SemiFuture(SemiFuture const&) = delete;
- SemiFuture& operator=(SemiFuture const&) = delete;
-
- // movable
- SemiFuture(SemiFuture&&) noexcept;
- SemiFuture& operator=(SemiFuture&&) noexcept;
-
- // safe move-constructabilty from Future
- /* implicit */ SemiFuture(Future<T>&&) noexcept;
- SemiFuture& operator=(Future<T>&&) noexcept;
-
/// Construct a Future from a value (perfect forwarding)
template <
class T2 = T,
typename = typename std::enable_if<
!isFuture<typename std::decay<T2>::type>::value>::type>
- /* implicit */ SemiFuture(T2&& val);
+ /* implicit */ FutureBase(T2&& val);
template <class T2 = T>
- /* implicit */ SemiFuture(
- typename std::enable_if<std::is_same<Unit, T2>::value>::type* = nullptr);
+ /* implicit */ FutureBase(
+ typename std::enable_if<std::is_same<Unit, T2>::value>::type*);
template <
class... Args,
typename std::enable_if<std::is_constructible<T, Args&&...>::value, int>::
type = 0>
- explicit SemiFuture(in_place_t, Args&&... args);
+ explicit FutureBase(in_place_t, Args&&... args);
+
+ FutureBase(FutureBase<T> const&) = delete;
+ FutureBase(SemiFuture<T>&&) noexcept;
+ FutureBase(Future<T>&&) noexcept;
- ~SemiFuture();
+ // not copyable
+ FutureBase(Future<T> const&) = delete;
+ FutureBase(SemiFuture<T> const&) = delete;
+
+ ~FutureBase();
/// Returns a reference to the result, with a reference category and const-
/// qualification equivalent to the reference category and const-qualification
Executor* executor,
int8_t priority = Executor::MID_PRI) &&;
- /// This variant creates a new future, where the ref-qualifier && version
- /// moves `this` out. This one is less efficient but avoids confusing users
- /// when "return f.via(x);" fails.
- inline Future<T> via(
- Executor* executor,
- int8_t priority = Executor::MID_PRI) &;
-
/** True when the result (or exception) is ready. */
bool isReady() const;
/// Note that this moves the Try<T> out.
Optional<Try<T>> poll();
- /// Block until the future is fulfilled. Returns the value (moved out), or
- /// throws the exception. The future must not already have a callback.
- T get();
-
- /// Block until the future is fulfilled, or until timed out. Returns the
- /// value (moved out), or throws the exception (which might be a TimedOut
- /// exception).
- T get(Duration dur);
-
- /// Block until this Future is complete. Returns a reference to this Future.
- SemiFuture<T>& wait() &;
-
- /// Overload of wait() for rvalue Futures
- SemiFuture<T>&& wait() &&;
-
- /// Block until this Future is complete or until the given Duration passes.
- /// Returns a reference to this Future
- SemiFuture<T>& wait(Duration) &;
-
- /// Overload of wait(Duration) for rvalue Futures
- SemiFuture<T>&& wait(Duration) &&;
-
/// This is not the method you're looking for.
///
/// This needs to be public because it's used by make* and when*, and it's
}
protected:
- typedef futures::detail::Core<T>* corePtr;
+ friend class Promise<T>;
+ template <class>
+ friend class SemiFuture;
+ template <class>
+ friend class Future;
+
+ using corePtr = futures::detail::Core<T>*;
// shared core state object
corePtr core_;
- explicit SemiFuture(corePtr obj) : core_(obj) {}
+ explicit FutureBase(corePtr obj) : core_(obj) {}
- explicit SemiFuture(futures::detail::EmptyConstruct) noexcept;
+ explicit FutureBase(futures::detail::EmptyConstruct) noexcept;
void detach();
void throwIfInvalid() const;
- friend class Promise<T>;
- template <class>
- friend class SemiFuture;
-
- template <class T2>
- friend SemiFuture<T2> makeSemiFuture(Try<T2>&&);
+ template <class FutureType>
+ void assign(FutureType&) noexcept;
Executor* getExecutor() {
return core_->getExecutor();
typename std::enable_if<R::ReturnsFuture::value, typename R::Return>::type
thenImplementation(F&& func, futures::detail::argResult<isTry, F, Args...>);
};
+} // namespace detail
+} // namespace futures
template <class T>
-class Future : public SemiFuture<T> {
+class SemiFuture : private futures::detail::FutureBase<T> {
+ private:
+ using Base = futures::detail::FutureBase<T>;
+
public:
- typedef T value_type;
+ static SemiFuture<T> makeEmpty(); // equivalent to moved-from
- static Future<T> makeEmpty(); // equivalent to moved-from
+ // Export public interface of FutureBase
+ // FutureBase is inherited privately to avoid subclasses being cast to
+ // a FutureBase pointer
+ using typename Base::value_type;
- // not copyable
- Future(Future const&) = delete;
- Future& operator=(Future const&) = delete;
+ /// Construct a Future from a value (perfect forwarding)
+ template <
+ class T2 = T,
+ typename = typename std::enable_if<
+ !isFuture<typename std::decay<T2>::type>::value>::type>
+ /* implicit */ SemiFuture(T2&& val) : Base(std::forward<T2>(val)) {}
+ template <class T2 = T>
+ /* implicit */ SemiFuture(
+ typename std::enable_if<std::is_same<Unit, T2>::value>::type* p = nullptr)
+ : Base(p) {}
+
+ template <
+ class... Args,
+ typename std::enable_if<std::is_constructible<T, Args&&...>::value, int>::
+ type = 0>
+ explicit SemiFuture(in_place_t, Args&&... args)
+ : Base(in_place, std::forward<Args>(args)...) {}
+
+ SemiFuture(SemiFuture<T> const&) = delete;
// movable
- Future(Future&&) noexcept;
- Future& operator=(Future&&) noexcept;
+ SemiFuture(SemiFuture<T>&&) noexcept;
+ // safe move-constructabilty from Future
+ /* implicit */ SemiFuture(Future<T>&&) noexcept;
+
+ using Base::cancel;
+ using Base::getTry;
+ using Base::hasException;
+ using Base::hasValue;
+ using Base::isActive;
+ using Base::isReady;
+ using Base::poll;
+ using Base::raise;
+ using Base::setCallback_;
+ using Base::value;
+ using Base::via;
+
+ SemiFuture& operator=(SemiFuture const&) = delete;
+ SemiFuture& operator=(SemiFuture&&) noexcept;
+ SemiFuture& operator=(Future<T>&&) noexcept;
+
+ /// Block until the future is fulfilled. Returns the value (moved out), or
+ /// throws the exception. The future must not already have a callback.
+ T get() &&;
+
+ /// Block until the future is fulfilled, or until timed out. Returns the
+ /// value (moved out), or throws the exception (which might be a TimedOut
+ /// exception).
+ T get(Duration dur) &&;
+
+ /// Block until this Future is complete. Returns a reference to this Future.
+ SemiFuture<T>& wait() &;
+
+ /// Overload of wait() for rvalue Futures
+ SemiFuture<T>&& wait() &&;
+
+ /// Block until this Future is complete or until the given Duration passes.
+ /// Returns a reference to this Future
+ SemiFuture<T>& wait(Duration) &;
+
+ /// Overload of wait(Duration) for rvalue Futures
+ SemiFuture<T>&& wait(Duration) &&;
+
+ private:
+ template <class>
+ friend class futures::detail::FutureBase;
+
+ using typename Base::corePtr;
+
+ template <class T2>
+ friend SemiFuture<T2> makeSemiFuture(Try<T2>&&);
+
+ explicit SemiFuture(corePtr obj) : Base(obj) {}
+
+ explicit SemiFuture(futures::detail::EmptyConstruct) noexcept
+ : Base(futures::detail::EmptyConstruct{}) {}
+};
+
+template <class T>
+class Future : private futures::detail::FutureBase<T> {
+ private:
+ using Base = futures::detail::FutureBase<T>;
+
+ public:
+ // Export public interface of FutureBase
+ // FutureBase is inherited privately to avoid subclasses being cast to
+ // a FutureBase pointer
+ using typename Base::value_type;
+
+ /// Construct a Future from a value (perfect forwarding)
+ template <
+ class T2 = T,
+ typename = typename std::enable_if<
+ !isFuture<typename std::decay<T2>::type>::value>::type>
+ /* implicit */ Future(T2&& val) : Base(std::forward<T2>(val)) {}
+
+ template <class T2 = T>
+ /* implicit */ Future(
+ typename std::enable_if<std::is_same<Unit, T2>::value>::type* p = nullptr)
+ : Base(p) {}
+
+ template <
+ class... Args,
+ typename std::enable_if<std::is_constructible<T, Args&&...>::value, int>::
+ type = 0>
+ explicit Future(in_place_t, Args&&... args)
+ : Base(in_place, std::forward<Args>(args)...) {}
+
+ Future(Future<T> const&) = delete;
+ // movable
+ Future(Future<T>&&) noexcept;
// converting move
template <
int>::type = 0>
Future& operator=(Future<T2>&&);
- /// Construct a Future from a value (perfect forwarding)
- template <
- class T2 = T,
- typename = typename std::enable_if<
- !isFuture<typename std::decay<T2>::type>::value &&
- !isSemiFuture<typename std::decay<T2>::type>::value>::type>
- /* implicit */ Future(T2&& val);
+ using Base::cancel;
+ using Base::getTry;
+ using Base::hasException;
+ using Base::hasValue;
+ using Base::isActive;
+ using Base::isReady;
+ using Base::poll;
+ using Base::raise;
+ using Base::setCallback_;
+ using Base::value;
+ using Base::via;
- template <class T2 = T>
- /* implicit */ Future(
- typename std::enable_if<std::is_same<Unit, T2>::value>::type* = nullptr);
+ static Future<T> makeEmpty(); // equivalent to moved-from
- template <
- class... Args,
- typename std::enable_if<std::is_constructible<T, Args&&...>::value, int>::
- type = 0>
- explicit Future(in_place_t, Args&&... args);
+ // not copyable
+ Future& operator=(Future const&) = delete;
- ~Future();
+ // movable
+ Future& operator=(Future&&) noexcept;
/// Call e->drive() repeatedly until the future is fulfilled. Examples
/// of DrivableExecutor include EventBase and ManualExecutor. Returns a
/// Unwraps the case of a Future<Future<T>> instance, and returns a simple
/// Future<T> instance.
template <class F = T>
- typename std::enable_if<isFuture<F>::value,
- Future<typename isFuture<T>::Inner>>::type
- unwrap();
+ typename std::
+ enable_if<isFuture<F>::value, Future<typename isFuture<T>::Inner>>::type
+ unwrap();
+
+ /// This variant creates a new future, where the ref-qualifier && version
+ /// moves `this` out. This one is less efficient but avoids confusing users
+ /// when "return f.via(x);" fails.
+ inline Future<T> via(
+ Executor* executor,
+ int8_t priority = Executor::MID_PRI) &;
/** When this Future has completed, execute func which is a function that
takes one of:
///
/// f1.then(std::bind(&Worker::doWork, w));
template <typename R, typename Caller, typename... Args>
- Future<typename isFuture<R>::Inner>
- then(R(Caller::*func)(Args...), Caller *instance);
+ Future<typename isFuture<R>::Inner> then(
+ R (Caller::*func)(Args...),
+ Caller* instance);
/// Execute the callback via the given Executor. The executor doesn't stick.
///
/// now. The optional Timekeeper is as with futures::sleep().
Future<T> delayed(Duration, Timekeeper* = nullptr);
+ /// Block until the future is fulfilled. Returns the value (moved out), or
+ /// throws the exception. The future must not already have a callback.
+ T get();
+
+ /// Block until the future is fulfilled, or until timed out. Returns the
+ /// value (moved out), or throws the exception (which might be a TimedOut
+ /// exception).
+ T get(Duration dur);
+
/// Block until this Future is complete. Returns a reference to this Future.
Future<T>& wait() &;
}
protected:
- typedef futures::detail::Core<T>* corePtr;
+ friend class Promise<T>;
+ template <class>
+ friend class futures::detail::FutureBase;
+ template <class>
+ friend class Future;
- explicit Future(corePtr obj) : SemiFuture<T>(obj) {}
+ using typename Base::corePtr;
- explicit Future(futures::detail::EmptyConstruct) noexcept;
+ explicit Future(corePtr obj) : Base(obj) {}
- friend class Promise<T>;
- template <class> friend class Future;
- friend class SemiFuture<T>;
+ explicit Future(futures::detail::EmptyConstruct) noexcept
+ : Base(futures::detail::EmptyConstruct{}) {}
template <class T2>
friend Future<T2> makeFuture(Try<T2>&&);