| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef LIB_FIT_PROMISE_INCLUDE_LIB_FPROMISE_PROMISE_INTERNAL_H_ |
| #define LIB_FIT_PROMISE_INCLUDE_LIB_FPROMISE_PROMISE_INTERNAL_H_ |
| |
| #include <assert.h> |
| #include <lib/fit/function.h> |
| #include <lib/fit/nullable.h> |
| #include <lib/fit/traits.h> |
| #include <lib/stdcompat/optional.h> |
| #include <lib/stdcompat/type_traits.h> |
| |
| #include <tuple> |
| #include <type_traits> |
| #include <utility> |
| |
| #include "result.h" |
| |
| namespace fpromise { |
| |
| template <typename Continuation> |
| class promise_impl; |
| |
| template <typename Promise> |
| class future_impl; |
| |
| class context; |
| class executor; |
| class suspended_task; |
| |
| namespace internal { |
| |
| // Determines whether a type is a kind of fpromise::result. |
| template <typename Result> |
| struct is_result : std::false_type {}; |
| template <typename V, typename E> |
| struct is_result<::fpromise::result<V, E>> : std::true_type {}; |
| |
| // Deduces a continuation's result. |
| // Also ensures that the continuation has a compatible signature. |
| template <typename Continuation, |
| typename = std::enable_if_t<is_result<decltype(std::declval<Continuation&>()( |
| std::declval<::fpromise::context&>()))>::value>> |
| struct continuation_traits { |
| using type = Continuation; |
| using result_type = decltype(std::declval<Continuation&>()(std::declval<::fpromise::context&>())); |
| }; |
| template <typename Continuation, typename = cpp17::void_t<>> |
| struct is_continuation : std::false_type {}; |
| template <typename Continuation> |
| struct is_continuation<Continuation, |
| cpp17::void_t<typename continuation_traits<Continuation>::type>> |
| : std::true_type {}; |
| |
| // Interposer type that provides uniform move construction/assignment for |
| // callable types that may or may not be move assignable. Lambdas are not |
| // typically move assignable, even though they may be move constructible. |
| // |
| // This type has a well-defined empty state. Instances of this type that are |
| // the source of move operation are left in the empty state. |
| template <typename Handler> |
| class movable_handler { |
| static_assert(std::is_move_constructible<Handler>::value, "Handler must be move constructible!"); |
| |
| template <typename... Conditions> |
| using requires_conditions = ::fit::internal::requires_conditions<Conditions...>; |
| |
| template <typename... Conditions> |
| using assignment_requires_conditions = |
| ::fit::internal::assignment_requires_conditions<movable_handler&, Conditions...>; |
| |
| template <typename U> |
| using not_self_type = ::fit::internal::not_same_type<movable_handler, U>; |
| |
| public: |
| constexpr movable_handler() = default; |
| |
| constexpr movable_handler(const movable_handler&) = delete; |
| constexpr movable_handler& operator=(const movable_handler&) = delete; |
| |
| constexpr movable_handler(movable_handler&& other) : handler_{std::move(other.handler_)} { |
| other.handler_.reset(); |
| } |
| |
| constexpr movable_handler& operator=(movable_handler&& other) { |
| if (this != &other) { |
| reset(); |
| if (other.handler_.has_value()) { |
| handler_.emplace(std::move(other.handler_.value())); |
| other.handler_.reset(); |
| } |
| } |
| return *this; |
| } |
| |
| template <typename U = Handler, |
| requires_conditions<not_self_type<U>, std::is_constructible<Handler, U&&>, |
| std::is_convertible<U&&, Handler>> = true> |
| constexpr movable_handler(U&& handler) { |
| if (!fit::is_null(handler)) { |
| handler_.emplace(std::forward<U>(handler)); |
| } |
| } |
| |
| ~movable_handler() = default; |
| |
| template <typename U> |
| constexpr assignment_requires_conditions<not_self_type<U>, std::is_constructible<Handler, U>, |
| std::is_assignable<Handler&, U>> |
| operator=(U&& handler) { |
| handler_.reset(); |
| if (!fit::is_null(handler)) { |
| handler_.emplace(std::forward<U>(handler)); |
| } |
| return *this; |
| } |
| |
| template <typename... Args> |
| constexpr auto operator()(Args&&... args) { |
| // Seamlessly handle void by casting call expression to return type. |
| using Return = typename ::fit::callable_traits<Handler>::return_type; |
| return static_cast<Return>((handler_.value())(std::forward<Args>(args)...)); |
| } |
| |
| explicit constexpr operator bool() const { return handler_.has_value(); } |
| |
| constexpr void reset() { handler_.reset(); } |
| |
| private: |
| cpp17::optional<Handler> handler_; |
| }; |
| |
| // Wraps a handler function and adapts its return type to a fpromise::result |
| // via its specializations. |
| template <typename Handler, typename DefaultV, typename DefaultE, |
| typename ReturnType = typename ::fit::callable_traits<Handler>::return_type, |
| bool callable_result = ::fit::is_callable<ReturnType>::value> |
| class result_adapter final { |
| // This expression always evaluates to false but depends on the template |
| // type parameters so that it only gets evaluated when the template is |
| // expanded. If we simply wrote "false", the compiler would raise the |
| // static assertion failure as soon as it encountered the statement. |
| template <typename T> |
| struct check_result { |
| static constexpr bool value = false; |
| }; |
| static_assert(check_result<ReturnType>::value, |
| "The provided handler's result type was expected to be " |
| "fpromise::result<V, E>, fpromise::ok_result<V>, fpromise::error_result<E>, " |
| "fpromise::pending_result, void, or a continuation with the signature " |
| "fpromise::result<V, E>(fpromise::context&). " |
| "Please refer to the combinator's documentation for a list of " |
| "supported handler function signatures."); |
| }; |
| |
| // Supports handlers that return void. |
| template <typename Handler, typename DefaultV, typename DefaultE> |
| class result_adapter<Handler, DefaultV, DefaultE, void, false> final { |
| public: |
| using result_type = ::fpromise::result<DefaultV, DefaultE>; |
| |
| explicit result_adapter(Handler handler) : handler_(std::move(handler)) {} |
| |
| template <typename... Args> |
| result_type call(::fpromise::context& context, Args... args) { |
| handler_(std::forward<Args>(args)...); |
| return ::fpromise::ok(); |
| } |
| |
| result_adapter(const result_adapter&) = delete; |
| result_adapter& operator=(const result_adapter&) = delete; |
| |
| result_adapter(result_adapter&&) = default; |
| result_adapter& operator=(result_adapter&&) = default; |
| |
| private: |
| movable_handler<Handler> handler_; |
| }; |
| |
| // Supports handlers that return pending_result. |
| template <typename Handler, typename DefaultV, typename DefaultE> |
| class result_adapter<Handler, DefaultV, DefaultE, ::fpromise::pending_result, false> final { |
| public: |
| using result_type = ::fpromise::result<DefaultV, DefaultE>; |
| |
| explicit result_adapter(movable_handler<Handler> handler) : handler_(std::move(handler)) {} |
| |
| template <typename... Args> |
| result_type call(::fpromise::context& context, Args... args) { |
| return handler_(std::forward<Args>(args)...); |
| } |
| |
| result_adapter(const result_adapter&) = delete; |
| result_adapter& operator=(const result_adapter&) = delete; |
| |
| result_adapter(result_adapter&&) = default; |
| result_adapter& operator=(result_adapter&&) = default; |
| |
| private: |
| movable_handler<Handler> handler_; |
| }; |
| |
| // Supports handlers that return ok_result<V>. |
| template <typename Handler, typename DefaultV, typename DefaultE, typename V> |
| class result_adapter<Handler, DefaultV, DefaultE, ::fpromise::ok_result<V>, false> final { |
| public: |
| using result_type = ::fpromise::result<V, DefaultE>; |
| |
| explicit result_adapter(movable_handler<Handler> handler) : handler_(std::move(handler)) {} |
| |
| template <typename... Args> |
| result_type call(::fpromise::context& context, Args... args) { |
| return handler_(std::forward<Args>(args)...); |
| } |
| |
| result_adapter(const result_adapter&) = delete; |
| result_adapter& operator=(const result_adapter&) = delete; |
| |
| result_adapter(result_adapter&&) = default; |
| result_adapter& operator=(result_adapter&&) = default; |
| |
| private: |
| movable_handler<Handler> handler_; |
| }; |
| |
| // Supports handlers that return error_result<E>. |
| template <typename Handler, typename DefaultV, typename DefaultE, typename E> |
| class result_adapter<Handler, DefaultV, DefaultE, ::fpromise::error_result<E>, false> final { |
| public: |
| using result_type = ::fpromise::result<DefaultV, E>; |
| |
| explicit result_adapter(movable_handler<Handler> handler) : handler_(std::move(handler)) {} |
| |
| template <typename... Args> |
| result_type call(::fpromise::context& context, Args... args) { |
| return handler_(std::forward<Args>(args)...); |
| } |
| |
| result_adapter(const result_adapter&) = delete; |
| result_adapter& operator=(const result_adapter&) = delete; |
| |
| result_adapter(result_adapter&&) = default; |
| result_adapter& operator=(result_adapter&&) = default; |
| |
| private: |
| movable_handler<Handler> handler_; |
| }; |
| |
| // Supports handlers that return result<V, E>. |
| template <typename Handler, typename DefaultV, typename DefaultE, typename V, typename E> |
| class result_adapter<Handler, DefaultV, DefaultE, ::fpromise::result<V, E>, false> final { |
| public: |
| using result_type = ::fpromise::result<V, E>; |
| |
| explicit result_adapter(movable_handler<Handler> handler) : handler_(std::move(handler)) {} |
| |
| template <typename... Args> |
| result_type call(::fpromise::context& context, Args... args) { |
| return handler_(std::forward<Args>(args)...); |
| } |
| |
| result_adapter(const result_adapter&) = delete; |
| result_adapter& operator=(const result_adapter&) = delete; |
| |
| result_adapter(result_adapter&&) = default; |
| result_adapter& operator=(result_adapter&&) = default; |
| |
| private: |
| movable_handler<Handler> handler_; |
| }; |
| |
| // Supports handlers that return continuations or promises. |
| // This works for any callable whose signature is: |
| // fpromise::result<...>(fpromise::context&) |
| template <typename Handler, typename DefaultV, typename DefaultE, typename ReturnType> |
| class result_adapter<Handler, DefaultV, DefaultE, ReturnType, true> final { |
| // If the handler doesn't actually return a continuation then the |
| // compilation will fail here which is slightly easier to diagnose |
| // than if we dropped the result_adapter specialization entirely. |
| using result_continuation_traits = continuation_traits<ReturnType>; |
| using continuation_type = typename result_continuation_traits::type; |
| |
| public: |
| using result_type = typename result_continuation_traits::result_type; |
| |
| explicit result_adapter(movable_handler<Handler> handler) : handler_(std::move(handler)) {} |
| |
| template <typename... Args> |
| result_type call(::fpromise::context& context, Args... args) { |
| if (handler_) { |
| continuation_ = handler_(std::forward<Args>(args)...); |
| handler_.reset(); |
| } |
| if (!continuation_) { |
| return ::fpromise::pending(); |
| } |
| return continuation_(context); |
| } |
| |
| result_adapter(const result_adapter&) = delete; |
| result_adapter& operator=(const result_adapter&) = delete; |
| |
| result_adapter(result_adapter&&) = default; |
| result_adapter& operator=(result_adapter&&) = default; |
| |
| private: |
| movable_handler<Handler> handler_; |
| movable_handler<continuation_type> continuation_; |
| }; |
| |
| // Wraps a handler that may or may not have a fpromise::context& as first argument. |
| // This is determined by checking the argument count. |
| template <typename Handler, typename DefaultV, typename DefaultE, size_t num_args = 0, |
| int excess_args = (static_cast<int>(::fit::callable_traits<Handler>::args::size) - |
| static_cast<int>(num_args))> |
| class context_adapter final { |
| static_assert(excess_args >= 0, |
| "The provided handler has too few arguments. " |
| "Please refer to the combinator's documentation for a list of " |
| "supported handler function signatures."); |
| static_assert(excess_args <= 1, |
| "The provided handler has too many arguments. " |
| "Please refer to the combinator's documentation for a list of " |
| "supported handler function signatures."); |
| }; |
| |
| // Supports handlers without a context argument. |
| template <typename Handler, typename DefaultV, typename DefaultE, size_t num_args> |
| class context_adapter<Handler, DefaultV, DefaultE, num_args, 0> final { |
| using base_type = result_adapter<Handler, DefaultV, DefaultE>; |
| |
| public: |
| using result_type = typename base_type::result_type; |
| static constexpr size_t next_arg_index = 0; |
| |
| explicit context_adapter(Handler handler) : base_(std::move(handler)) {} |
| |
| template <typename... Args> |
| result_type call(::fpromise::context& context, Args... args) { |
| return base_.template call<Args...>(context, std::forward<Args>(args)...); |
| } |
| |
| private: |
| base_type base_; |
| }; |
| |
| // Supports handlers with a context argument. |
| template <typename Handler, typename DefaultV, typename DefaultE, size_t num_args> |
| class context_adapter<Handler, DefaultV, DefaultE, num_args, 1> final { |
| using base_type = result_adapter<Handler, DefaultV, DefaultE>; |
| using context_arg_type = typename ::fit::callable_traits<Handler>::args::template at<0>; |
| static_assert(std::is_same<context_arg_type, ::fpromise::context&>::value, |
| "The provided handler's first argument was expected to be of type " |
| "fpromise::context& based on the number of arguments it has. " |
| "Please refer to the combinator's documentation for a list of " |
| "supported handler function signatures."); |
| |
| public: |
| using result_type = typename base_type::result_type; |
| static constexpr size_t next_arg_index = 1; |
| |
| explicit context_adapter(Handler handler) : base_(std::move(handler)) {} |
| |
| template <typename... Args> |
| result_type call(::fpromise::context& context, Args... args) { |
| return base_.template call<::fpromise::context&, Args...>(context, context, |
| std::forward<Args>(args)...); |
| } |
| |
| private: |
| base_type base_; |
| }; |
| |
| // Wraps a handler that may accept a context argument. |
| template <typename Handler> |
| class context_handler_invoker final { |
| using base_type = context_adapter<Handler, void, void, 0>; |
| |
| public: |
| using result_type = typename base_type::result_type; |
| |
| explicit context_handler_invoker(Handler handler) : base_(std::move(handler)) {} |
| |
| result_type operator()(::fpromise::context& context) { return base_.template call<>(context); } |
| |
| private: |
| base_type base_; |
| }; |
| |
| // Wraps a handler that may accept a context and result argument. |
| template <typename Handler, typename PriorResult> |
| class result_handler_invoker final { |
| using base_type = context_adapter<Handler, void, void, 1>; |
| using result_arg_type = |
| typename ::fit::callable_traits<Handler>::args::template at<base_type::next_arg_index>; |
| static_assert(std::is_same<result_arg_type, PriorResult&>::value || |
| std::is_same<result_arg_type, const PriorResult&>::value, |
| "The provided handler's last argument was expected to be of type " |
| "fpromise::result<V, E>& or const fpromise::result<V, E>& where V is the prior " |
| "result's value type and E is the prior result's error type. " |
| "Please refer to the combinator's documentation for a list of " |
| "supported handler function signatures."); |
| |
| public: |
| using result_type = typename base_type::result_type; |
| |
| explicit result_handler_invoker(Handler handler) : base_(std::move(handler)) {} |
| |
| result_type operator()(::fpromise::context& context, PriorResult& result) { |
| return base_.template call<PriorResult&>(context, result); |
| } |
| |
| private: |
| base_type base_; |
| }; |
| |
| // Wraps a handler that may accept a context and value argument. |
| template <typename Handler, typename PriorResult, typename V = typename PriorResult::value_type> |
| class value_handler_invoker final { |
| using base_type = context_adapter<Handler, void, typename PriorResult::error_type, 1>; |
| using value_arg_type = |
| typename ::fit::callable_traits<Handler>::args::template at<base_type::next_arg_index>; |
| static_assert(std::is_same<value_arg_type, V&>::value || |
| std::is_same<value_arg_type, const V&>::value, |
| "The provided handler's last argument was expected to be of type " |
| "V& or const V& where V is the prior result's value type. " |
| "Please refer to the combinator's documentation for a list of " |
| "supported handler function signatures."); |
| |
| public: |
| using result_type = typename base_type::result_type; |
| |
| explicit value_handler_invoker(Handler handler) : base_(std::move(handler)) {} |
| |
| result_type operator()(::fpromise::context& context, PriorResult& result) { |
| return base_.template call<V&>(context, result.value()); |
| } |
| |
| private: |
| base_type base_; |
| }; |
| |
| // Specialization for void value. |
| template <typename Handler, typename PriorResult> |
| class value_handler_invoker<Handler, PriorResult, void> final { |
| using base_type = context_adapter<Handler, void, typename PriorResult::error_type, 0>; |
| |
| public: |
| using result_type = typename base_type::result_type; |
| |
| explicit value_handler_invoker(Handler handler) : base_(std::move(handler)) {} |
| |
| result_type operator()(::fpromise::context& context, PriorResult& result) { |
| return base_.template call<>(context); |
| } |
| |
| private: |
| base_type base_; |
| }; |
| |
| // Wraps a handler that may accept a context and error argument. |
| template <typename Handler, typename PriorResult, typename E = typename PriorResult::error_type> |
| class error_handler_invoker final { |
| using base_type = context_adapter<Handler, typename PriorResult::value_type, void, 1>; |
| using error_arg_type = |
| typename ::fit::callable_traits<Handler>::args::template at<base_type::next_arg_index>; |
| static_assert(std::is_same<error_arg_type, E&>::value || |
| std::is_same<error_arg_type, const E&>::value, |
| "The provided handler's last argument was expected to be of type " |
| "E& or const E& where E is the prior result's error type. " |
| "Please refer to the combinator's documentation for a list of " |
| "supported handler function signatures."); |
| |
| public: |
| using result_type = typename base_type::result_type; |
| |
| explicit error_handler_invoker(Handler handler) : base_(std::move(handler)) {} |
| |
| result_type operator()(::fpromise::context& context, PriorResult& result) { |
| return base_.template call<E&>(context, result.error()); |
| } |
| |
| private: |
| base_type base_; |
| }; |
| |
| // Specialization for void error. |
| template <typename Handler, typename PriorResult> |
| class error_handler_invoker<Handler, PriorResult, void> final { |
| using base_type = context_adapter<Handler, typename PriorResult::value_type, void, 0>; |
| |
| public: |
| using result_type = typename base_type::result_type; |
| |
| explicit error_handler_invoker(Handler handler) : base_(std::move(handler)) {} |
| |
| result_type operator()(::fpromise::context& context, PriorResult& result) { |
| return base_.template call<>(context); |
| } |
| |
| private: |
| base_type base_; |
| }; |
| |
| // The continuation produced by |fpromise::promise::then()|. |
| template <typename PriorPromise, typename ResultHandler> |
| class then_continuation final { |
| using invoker_type = |
| ::fpromise::internal::result_handler_invoker<ResultHandler, |
| typename PriorPromise::result_type>; |
| |
| public: |
| then_continuation(PriorPromise prior_promise, ResultHandler handler) |
| : prior_(std::move(prior_promise)), invoker_(std::move(handler)) {} |
| |
| typename invoker_type::result_type operator()(::fpromise::context& context) { |
| if (!prior_(context)) |
| return ::fpromise::pending(); |
| return invoker_(context, prior_.result()); |
| } |
| |
| private: |
| future_impl<PriorPromise> prior_; |
| invoker_type invoker_; |
| }; |
| |
| // The continuation produced by |fpromise::promise::and_then()|. |
| template <typename PriorPromise, typename ValueHandler> |
| class and_then_continuation final { |
| using invoker_type = |
| ::fpromise::internal::value_handler_invoker<ValueHandler, typename PriorPromise::result_type>; |
| |
| public: |
| and_then_continuation(PriorPromise prior_promise, ValueHandler handler) |
| : prior_(std::move(prior_promise)), invoker_(std::move(handler)) {} |
| |
| typename invoker_type::result_type operator()(::fpromise::context& context) { |
| if (!prior_(context)) |
| return ::fpromise::pending(); |
| if (prior_.is_error()) |
| return prior_.take_error_result(); |
| return invoker_(context, prior_.result()); |
| } |
| |
| private: |
| future_impl<PriorPromise> prior_; |
| invoker_type invoker_; |
| }; |
| |
| // The continuation produced by |fpromise::promise::or_else()|. |
| template <typename PriorPromise, typename ErrorHandler> |
| class or_else_continuation final { |
| using invoker_type = |
| ::fpromise::internal::error_handler_invoker<ErrorHandler, typename PriorPromise::result_type>; |
| |
| public: |
| or_else_continuation(PriorPromise prior_promise, ErrorHandler handler) |
| : prior_(std::move(prior_promise)), invoker_(std::move(handler)) {} |
| |
| typename invoker_type::result_type operator()(::fpromise::context& context) { |
| if (!prior_(context)) |
| return ::fpromise::pending(); |
| if (prior_.is_ok()) |
| return prior_.take_ok_result(); |
| return invoker_(context, prior_.result()); |
| } |
| |
| private: |
| future_impl<PriorPromise> prior_; |
| invoker_type invoker_; |
| }; |
| |
| // The continuation produced by |fpromise::promise::inspect()|. |
| template <typename PriorPromise, typename InspectHandler> |
| class inspect_continuation final { |
| using invoker_type = |
| ::fpromise::internal::result_handler_invoker<InspectHandler, |
| typename PriorPromise::result_type>; |
| |
| public: |
| inspect_continuation(PriorPromise prior_promise, InspectHandler handler) |
| : prior_(std::move(prior_promise)), invoker_(std::move(handler)) {} |
| |
| typename PriorPromise::result_type operator()(::fpromise::context& context) { |
| typename PriorPromise::result_type result = prior_(context); |
| if (result) |
| invoker_(context, result); |
| return result; |
| } |
| |
| private: |
| PriorPromise prior_; |
| invoker_type invoker_; |
| }; |
| |
| // The continuation produced by |fpromise::promise::discard_result()|. |
| template <typename PriorPromise> |
| class discard_result_continuation final { |
| public: |
| explicit discard_result_continuation(PriorPromise prior_promise) |
| : prior_(std::move(prior_promise)) {} |
| |
| fpromise::result<> operator()(::fpromise::context& context) { |
| if (!prior_(context)) |
| return ::fpromise::pending(); |
| return ::fpromise::ok(); |
| } |
| |
| private: |
| PriorPromise prior_; |
| }; |
| |
| // The continuation produced by |make_promise()|. |
| // This turns out to be equivalent to a context handler invoker. |
| template <typename PromiseHandler> |
| using promise_continuation = context_handler_invoker<PromiseHandler>; |
| |
| // The continuation produced by |make_result_promise()|. |
| template <typename V, typename E> |
| class result_continuation final { |
| public: |
| explicit result_continuation(::fpromise::result<V, E> result) : result_(std::move(result)) {} |
| |
| ::fpromise::result<V, E> operator()(::fpromise::context& context) { return std::move(result_); } |
| |
| private: |
| ::fpromise::result<V, E> result_; |
| }; |
| |
| // Returns true if all arguments are true or if there are none. |
| inline bool all_true() { return true; } |
| template <typename... Ts> |
| inline bool all_true(bool value, Ts... values) { |
| return value & all_true(values...); |
| } |
| |
| // The continuation produced by |join_promises()|. |
| template <typename... Promises> |
| class join_continuation final { |
| public: |
| explicit join_continuation(Promises... promises) |
| : promises_(std::make_tuple(std::move(promises)...)) {} |
| |
| ::fpromise::result<std::tuple<typename Promises::result_type...>> operator()( |
| ::fpromise::context& context) { |
| return evaluate(context, std::index_sequence_for<Promises...>{}); |
| } |
| |
| private: |
| template <size_t... i> |
| ::fpromise::result<std::tuple<typename Promises::result_type...>> evaluate( |
| ::fpromise::context& context, std::index_sequence<i...>) { |
| bool done = all_true(std::get<i>(promises_)(context)...); |
| if (!done) |
| return ::fpromise::pending(); |
| return ::fpromise::ok(std::make_tuple(std::get<i>(promises_).take_result()...)); |
| } |
| |
| std::tuple<future_impl<Promises>...> promises_; |
| }; |
| |
| // The continuation produced by |join_promise_vector()|. |
| template <typename Promise> |
| class join_vector_continuation final { |
| using promise_vector = std::vector<Promise>; |
| using result_vector = std::vector<typename Promise::result_type>; |
| |
| public: |
| explicit join_vector_continuation(promise_vector promises) |
| : promises_(std::move(promises)), results_(promises_.size()) {} |
| |
| ::fpromise::result<result_vector> operator()(::fpromise::context& context) { |
| bool all_done{true}; |
| for (size_t i = 0; i < promises_.size(); ++i) { |
| if (!results_[i]) { |
| results_[i] = promises_[i](context); |
| all_done &= !results_[i].is_pending(); |
| } |
| } |
| if (all_done) { |
| return fpromise::ok(std::move(results_)); |
| } |
| return ::fpromise::pending(); |
| } |
| |
| private: |
| promise_vector promises_; |
| result_vector results_; |
| }; |
| |
| } // namespace internal |
| |
| template <typename PromiseHandler> |
| inline promise_impl<::fpromise::internal::promise_continuation<PromiseHandler>> make_promise( |
| PromiseHandler handler); |
| |
| template <typename Continuation> |
| inline promise_impl<Continuation> make_promise_with_continuation(Continuation continuation); |
| |
| } // namespace fpromise |
| |
| #endif // LIB_FIT_PROMISE_INCLUDE_LIB_FPROMISE_PROMISE_INTERNAL_H_ |