From 0b1b284e3a8523a6e7bbf36437658bdc9f07557a Mon Sep 17 00:00:00 2001 From: Denis Blank Date: Thu, 26 Dec 2019 05:27:13 +0100 Subject: [PATCH] Add continuable_base::is_ready and continuable_base::unpack. * Can be used to specialize the asynchronous control flow on immediate available asynchronous results mostly returned by: - make_ready_continuable - make_exceptional_continuable * Usable to avoid longer unnecessary synchronous callback nestings. * cti::query_arg_t was renamed into cti::unpack_arg_t. * The continuation overload nowreturns result rather than std::tuple. * Add is_ready optimizations to make_exceptional_continuable. --- include/continuable/continuable-base.hpp | 57 ++++-- .../continuable/continuable-primitives.hpp | 31 +-- include/continuable/continuable-result.hpp | 2 +- .../connection/connection-aggregated.hpp | 1 + .../detail/connection/connection-all.hpp | 10 +- .../detail/connection/connection.hpp | 5 + .../continuable/detail/core/annotation.hpp | 12 -- include/continuable/detail/core/base.hpp | 96 +++++++--- .../continuable/detail/other/coroutines.hpp | 30 +-- include/continuable/detail/other/erasure.hpp | 4 +- include/continuable/detail/other/testing.hpp | 39 ++-- test/unit-test/CMakeLists.txt | 1 + .../single/test-continuable-erasure.cpp | 4 +- .../single/test-continuable-ready.cpp | 181 ++++++++++++++++++ 14 files changed, 352 insertions(+), 121 deletions(-) create mode 100644 test/unit-test/single/test-continuable-ready.cpp diff --git a/include/continuable/continuable-base.hpp b/include/continuable/continuable-base.hpp index 78197d2..c1388ec 100644 --- a/include/continuable/continuable-base.hpp +++ b/include/continuable/continuable-base.hpp @@ -138,7 +138,7 @@ public: std::enable_if_t, Data>::value>* = nullptr> /* implicit */ continuable_base(continuable_base&& other) - : continuable_base(std::move(other).consume()) { + : data_(std::move(other).consume()) { } /// Constructor taking the data of other continuable_base objects @@ -627,6 +627,35 @@ public: return annotation_trait::finish(std::move(*this)); } + /// Returns true when the continuable can provide its result immediately, + /// and its lazy invocation would be side-effect free. + /// + /// \since 4.0.0 + bool is_ready() const noexcept { + return annotation_trait::is_ready(*this); + } + + /// Invalidates the continuable and returns its immediate invocation result. + /// + /// This method can be used to specialize the asynchronous control flow + /// based on whether the continuable ìs_ready at every time, + /// which is true for a continuable created through the following functions: + /// - make_ready_continuable + /// - make_exceptional_continuable + /// + /// \returns A result where Args... represent the current + /// asynchronous parameters or the currently stored exception. + /// + /// \attention unpack requires that continuable_base::is_ready returned true + /// in a previous check, otherwise its behaviour is unspecified. + /// + /// \since 4.0.0 + auto unpack() && { + assert(ownership_.is_acquired()); + assert(is_ready()); + return detail::base::attorney::query(std::move(*this).finish()); + } + /// Predicate to check whether the cti::continuable_base is frozen or not. /// /// \returns Returns true when the continuable_base is frozen. @@ -860,15 +889,16 @@ constexpr auto make_continuable(Continuation&& continuation) { /// \attention Usually using this function isn't needed at all since /// the continuable library is capable of working with /// plain values in most cases. -/// Try not to use it since it causes unneccessary recursive +/// Try not to use it since it causes unnecessary recursive /// function calls. /// /// \since 3.0.0 template auto make_ready_continuable(Args&&... args) { return detail::base::attorney::create_from_raw( - detail::base::ready_continuation...>{ - std::forward(args)...}, + detail::base::ready_continuation...>( + result...>::from( + std::forward(args)...)), detail::identity...>{}, detail::util::ownership{}); } @@ -883,19 +913,22 @@ auto make_ready_continuable(Args&&... args) { /// auto ct = cti::make_exceptional_continuable(ptr); /// ``` /// -/// \tparam Signature The fake signature of the returned continuable. +/// \tparam Args The fake signature of the returned continuable. /// /// \since 3.0.0 -template +template constexpr auto make_exceptional_continuable(Exception&& exception) { - static_assert(sizeof...(Signature) > 0, + static_assert(sizeof...(Args) > 0, "Requires at least one type for the fake signature!"); - return make_continuable( // ... - [exception = std::forward(exception)](auto&& promise) mutable { - std::forward(promise).set_exception( - std::move(exception)); - }); + using hint_t = typename detail::hints::from_args::type; + using ready_continuation_t = + typename detail::base::ready_continuation_from_hint::type; + using result_t = typename detail::base::result_from_hint::type; + return detail::base::attorney::create_from_raw( + ready_continuation_t(result_t::from(exception_arg_t{}, + std::forward(exception))), + hint_t{}, detail::util::ownership{}); } /// Returns a continuable_base with the parameterized result which never diff --git a/include/continuable/continuable-primitives.hpp b/include/continuable/continuable-primitives.hpp index 12aae3b..ef68cb5 100644 --- a/include/continuable/continuable-primitives.hpp +++ b/include/continuable/continuable-primitives.hpp @@ -40,13 +40,13 @@ namespace cti { /// and continuations. /// /// For the callback and the continuation `Args...` represents the -/// asynchronous results: +/// asynchronous result: /// ```cpp /// template /// struct continuation { /// void operator() (callback); /// bool operator() (cti::is_ready_arg_t) const; -/// std::tuple operator() (cti::query_arg_t); +/// result operator() (cti::unpack_arg_t); /// }; /// ``` /// ```cpp @@ -62,7 +62,6 @@ namespace cti { /// of a continuable_base or promise_base. /// /// \since 4.0.0 -/// template using signature_arg_t = detail::identity; @@ -71,17 +70,24 @@ using signature_arg_t = detail::identity; /// without having side effects. /// /// \since 4.0.0 -/// struct is_ready_arg_t {}; -/// Represents the tag type that is used to query the continuation -/// for its arguments when resolves the callback instantly -/// without having side effects. -/// It's required that the query of is_ready_arg_t returns true. +/// Represents the tag type that is used to unpack the result of a continuation. +/// +/// \attention It's required that the query of is_ready_arg_t returns true, +/// otherwise the behaviour when unpacking is unspecified. /// /// \since 4.0.0 +struct unpack_arg_t {}; + +/// \copydoc unpack_arg_t /// -struct query_arg_t {}; +/// \deprecated The query_arg_t was deprecated because of +/// its new naming unpack_arg_t. +/// +[[deprecated("The dispatch_error_tag was replaced by unpack_arg_t and will " + "be removed in a later major version!")]] // + typedef unpack_arg_t query_arg_t; /// Represents the tag type that is used to disambiguate the /// callback operator() in order to take the exception asynchronous chain. @@ -89,7 +95,6 @@ struct query_arg_t {}; /// \note see continuable::next for details. /// /// \since 4.0.0 -/// struct exception_arg_t {}; /// \copydoc exception_arg_t @@ -100,7 +105,7 @@ struct exception_arg_t {}; /// [[deprecated("The dispatch_error_tag was replaced by exception_arg_t and will " "be removed in a later major version!")]] // -typedef exception_arg_t dispatch_error_tag; + typedef exception_arg_t dispatch_error_tag; /// Represents the type that is used as exception type /// @@ -111,7 +116,6 @@ typedef exception_arg_t dispatch_error_tag; /// defining `CONTINUABLE_WITH_CUSTOM_ERROR_TYPE`. /// /// \since 4.0.0 -/// using exception_t = detail::types::exception_t; /// \copydoc exception_t @@ -122,14 +126,13 @@ using exception_t = detail::types::exception_t; /// [[deprecated("The error_type was replaced by exception_t and will " "be removed in a later major version!")]] // -typedef exception_t error_type; + typedef exception_t error_type; /// Represents the type that is used to disable the special meaning of types /// which are returned by a asynchronous result handler. /// See cti::plain for details. /// /// \since 4.0.0 -/// template using plain_t = detail::types::plain_tag; /// \} diff --git a/include/continuable/continuable-result.hpp b/include/continuable/continuable-result.hpp index 67550d6..33ff9d4 100644 --- a/include/continuable/continuable-result.hpp +++ b/include/continuable/continuable-result.hpp @@ -248,7 +248,7 @@ public: return result{detail::init_arg_t{}, std::move(values)...}; } /// Creates a present result from the given exception - static result from(exception_t exception) { + static result from(exception_arg_t, exception_t exception) { return result{detail::init_arg_t{}, std::move(exception)}; } diff --git a/include/continuable/detail/connection/connection-aggregated.hpp b/include/continuable/detail/connection/connection-aggregated.hpp index 8ae01d6..dc258bd 100644 --- a/include/continuable/detail/connection/connection-aggregated.hpp +++ b/include/continuable/detail/connection/connection-aggregated.hpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include diff --git a/include/continuable/detail/connection/connection-all.hpp b/include/continuable/detail/connection/connection-all.hpp index d200f06..f8c5ccf 100644 --- a/include/continuable/detail/connection/connection-all.hpp +++ b/include/continuable/detail/connection/connection-all.hpp @@ -157,21 +157,21 @@ struct connection_finalizer { template static auto finalize(Connection&& connection, util::ownership ownership) { // Create the target result from the connection - auto result = + auto res = aggregated::box_continuables(std::forward(connection)); - auto signature = aggregated::hint_of_data(); + auto signature = aggregated::hint_of_data(); return base::attorney::create_from( - [result = std::move(result)](auto&& callback) mutable { + [res = std::move(res)](auto&& callback) mutable { using submitter_t = all::result_submitter, - std::decay_t>; + std::decay_t>; // Create the shared state which holds the result // and the final callback auto state = std::make_shared( - std::forward(callback), std::move(result)); + std::forward(callback), std::move(res)); // Dispatch the continuables and store its partial result // in the whole result diff --git a/include/continuable/detail/connection/connection.hpp b/include/continuable/detail/connection/connection.hpp index 1959f18..46d0328 100644 --- a/include/continuable/detail/connection/connection.hpp +++ b/include/continuable/detail/connection/connection.hpp @@ -138,6 +138,11 @@ struct connection_annotation_trait { // Return a new continuable which return finalizer::finalize(std::move(connection), std::move(ownership)); } + + template + static bool is_ready(Continuable const& /*continuable*/) noexcept { + return false; + } }; class prepare_continuables { diff --git a/include/continuable/detail/core/annotation.hpp b/include/continuable/detail/core/annotation.hpp index 37c9ecb..652992b 100644 --- a/include/continuable/detail/core/annotation.hpp +++ b/include/continuable/detail/core/annotation.hpp @@ -37,18 +37,6 @@ namespace cti { namespace detail { -template -struct annotation_trait; - -/// Specialization for a present signature hint -template -struct annotation_trait> { - template - static Continuable&& finish(Continuable&& continuable) { - return std::forward(continuable); - } -}; - namespace hints { /// Extracts the signature we pass to the internal continuable /// from an argument pack as specified by make_continuable. diff --git a/include/continuable/detail/core/base.hpp b/include/continuable/detail/core/base.hpp index 20a3720..32b8c5d 100644 --- a/include/continuable/detail/core/base.hpp +++ b/include/continuable/detail/core/base.hpp @@ -71,7 +71,9 @@ struct is_continuable> : std::true_type {}; template struct ready_continuation { - std::tuple values_; + explicit ready_continuation(result result) + : result_(std::move(result)) { + } ready_continuation() = delete; ~ready_continuation() = default; @@ -80,43 +82,42 @@ struct ready_continuation { ready_continuation& operator=(ready_continuation&&) = default; ready_continuation& operator=(ready_continuation const&) = delete; - explicit ready_continuation(Args... values) : values_(std::move(values)...) { - } - template void operator()(Callback&& callback) { - traits::unpack(std::forward(callback), std::move(values_)); + if (result_.is_value()) { + traits::unpack(std::forward(callback), std::move(result_)); + } else if (result_.is_exception()) { + util::invoke(std::forward(callback), exception_arg_t{}, + result_.get_exception()); + } } bool operator()(is_ready_arg_t) const noexcept { return true; } - std::tuple operator()(query_arg_t) { - return std::move(values_); + result operator()(unpack_arg_t) { + return std::move(result_); } + +private: + result result_; }; -template <> -struct ready_continuation<> { - ready_continuation() = default; - ~ready_continuation() = default; - ready_continuation(ready_continuation&&) = default; - ready_continuation(ready_continuation const&) = delete; - ready_continuation& operator=(ready_continuation&&) = default; - ready_continuation& operator=(ready_continuation const&) = delete; - template - void operator()(Callback&& callback) { - util::invoke(std::forward(callback)); - } +template +struct ready_continuation_from_hint; - bool operator()(is_ready_arg_t) const noexcept { - return true; - } +template +struct ready_continuation_from_hint> { + using type = ready_continuation; +}; - std::tuple<> operator()(query_arg_t) { - return std::make_tuple(); - } +template +struct result_from_hint; + +template +struct result_from_hint> { + using type = result; }; template @@ -139,7 +140,7 @@ struct proxy_continuable, Continuation> : Continuation { return false; } - std::tuple operator()(query_arg_t) { + result operator()(unpack_arg_t) { CTI_DETAIL_UNREACHABLE(); } }; @@ -185,10 +186,29 @@ struct attorney { template static auto query(continuable_base&& continuation) { - return std::move(continuation).consume()(query_arg_t{}); + return std::move(continuation).consume()(unpack_arg_t{}); + } +}; +} // namespace base + +template +struct annotation_trait; + +/// Specialization for a present signature hint +template +struct annotation_trait> { + template + static Continuable&& finish(Continuable&& continuable) { + return std::forward(continuable); + } + + template + static bool is_ready(Continuable const& continuable) noexcept { + return base::attorney::is_ready(continuable); } }; +namespace base { /// Returns the signature hint of the given continuable template constexpr identity @@ -777,6 +797,7 @@ namespace detail { template struct exception_stripper_proxy { Callable callable_; + template auto operator()(exception_arg_t, Args&&... args) -> decltype(util::invoke(std::declval(), // @@ -843,7 +864,14 @@ struct chained_continuation, identity, if (is_ready) { // Invoke the proxy callback directly with the result to // avoid a potential type erasure. - traits::unpack(std::move(proxy), std::move(continuation_)(query_arg_t{})); + auto result = std::move(continuation_)(unpack_arg_t{}); + + if (result.is_value()) { + traits::unpack(std::move(proxy), std::move(result)); + } else if (result.is_exception()) { + util::invoke(std::move(proxy), exception_arg_t{}, + std::move(result.get_exception())); + } } else { // Invoke the continuation with a proxy callback. // The proxy callback is responsible for passing @@ -856,7 +884,7 @@ struct chained_continuation, identity, return false; } - std::tuple operator()(query_arg_t) { + result operator()(unpack_arg_t) { CTI_DETAIL_UNREACHABLE(); } }; @@ -892,14 +920,20 @@ struct chained_continuation, identity, std::forward(next_callback)); // Extract the result out of the ready continuable - traits::unpack(std::move(proxy), std::move(continuation_)(query_arg_t{})); + auto result = std::move(continuation_)(unpack_arg_t{}); + if (result.is_value()) { + traits::unpack(std::move(proxy), std::move(result)); + } else if (result.is_exception()) { + util::invoke(std::move(proxy), exception_arg_t{}, + std::move(result.get_exception())); + } } bool operator()(is_ready_arg_t) const noexcept { return false; } - std::tuple operator()(query_arg_t) { + result operator()(unpack_arg_t) { CTI_DETAIL_UNREACHABLE(); } }; diff --git a/include/continuable/detail/other/coroutines.hpp b/include/continuable/detail/other/coroutines.hpp index db47c70..d5def3b 100644 --- a/include/continuable/detail/other/coroutines.hpp +++ b/include/continuable/detail/other/coroutines.hpp @@ -79,18 +79,15 @@ public: : continuable_(std::move(continuable)) { // If the continuable is ready resolve the result from the - // continuable immediatly. + // continuable immediately. if (base::attorney::is_ready(continuable_)) { - traits::unpack( - [&](auto&&... args) { - resolve(std::forward(args)...); - }, - base::attorney::query(std::move(continuable_))); + assert(result_.is_empty()); + result_ = base::attorney::query(std::move(continuable_)); } } - /// Since continuables are evaluated lazily we are not - /// capable to say whether the resumption will be instantly. + /// Return whether the continuable can provide its result instantly, + /// which also means its execution is side-effect free. bool await_ready() const noexcept { return !result_.is_empty(); } @@ -102,7 +99,8 @@ public: // Forward every result to the current awaitable std::move(continuable_) .next([h, this](auto&&... args) mutable { - resolve(std::forward(args)...); + assert(result_.is_empty()); + result_ = result_t::from(std::forward(args)...); h.resume(); }) .done(); @@ -122,20 +120,6 @@ public: CTI_DETAIL_TRAP(); #endif // CONTINUABLE_HAS_EXCEPTIONS } - -private: - /// Resolve the continuation through the result - template - void resolve(Args&&... args) { - assert(result_.is_empty()); - result_.set_value(std::forward(args)...); - } - - /// Resolve the continuation through an error - void resolve(exception_arg_t, exception_t exception) { - assert(result_.is_empty()); - result_.set_exception(std::move(exception)); - } }; /// Converts a continuable into an awaitable object as described by diff --git a/include/continuable/detail/other/erasure.hpp b/include/continuable/detail/other/erasure.hpp index b3b340e..733d82f 100644 --- a/include/continuable/detail/other/erasure.hpp +++ b/include/continuable/detail/other/erasure.hpp @@ -173,7 +173,7 @@ template using continuation_erasure_t = fu2::function_base< true, false, continuation_capacity, true, false, void(promise_base, signature_arg_t>), - bool(is_ready_arg_t) const, std::tuple(query_arg_t)>; + bool(is_ready_arg_t) const, result(unpack_arg_t)>; #ifdef CONTINUABLE_HAS_IMMEDIATE_TYPES template @@ -229,7 +229,7 @@ public: return erasure_(is_ready_arg); } - std::tuple operator()(query_arg_t query_arg) { + result operator()(unpack_arg_t query_arg) { return erasure_(query_arg); } }; diff --git a/include/continuable/detail/other/testing.hpp b/include/continuable/detail/other/testing.hpp index b028f26..130222e 100644 --- a/include/continuable/detail/other/testing.hpp +++ b/include/continuable/detail/other/testing.hpp @@ -100,10 +100,10 @@ template void assert_async_validation(C&& continuable, V&& validator) { assert_async_completion( std::forward(continuable) - .then( - [validator = std::forward(validator)](auto&&... args) mutable { - validator(std::forward(args)...); - })); + .then([validator = + std::forward(validator)](auto&&... args) mutable { + validator(std::forward(args)...); + })); } /// Expects that the continuable is finished with the given arguments @@ -113,17 +113,17 @@ void assert_async_binary_validation(V&& validator, C&& continuable, using size = std::integral_constant; - assert_async_validation( - std::forward(continuable), - [expected_pack = std::make_tuple(std::forward(expected)...), - validator = std::forward(validator)](auto&&... args) mutable { - static_assert(size::value == sizeof...(args), - "Async completion handler called with a different count " - "of arguments!"); + assert_async_validation(std::forward(continuable), [ + expected_pack = std::make_tuple(std::forward(expected)...), + validator = std::forward(validator) + ](auto&&... args) mutable { + static_assert(size::value == sizeof...(args), + "Async completion handler called with a different count " + "of arguments!"); - validator(std::make_tuple(std::forward(args)...), - expected_pack); - }); + validator(std::make_tuple(std::forward(args)...), + expected_pack); + }); } /// Expects that the continuable is finished with the given arguments @@ -136,19 +136,20 @@ void assert_async_binary_exception_validation(V&& validator, C&& continuable, // Workaround for our known GCC bug. util::unused(std::forward(args)...); - // ... + // The exception was not thrown! FAIL(); }) - .fail([called, validator = std::forward(validator), - expected = std::forward(expected)]( - exception_t error) { + .fail([ + called, validator = std::forward(validator), + expected = std::forward(expected) + ](exception_t error) { ASSERT_FALSE(*called); *called = true; #if defined(CONTINUABLE_HAS_EXCEPTIONS) try { std::rethrow_exception(error); - } catch (std::decay_t& exception) { + } catch (std::decay_t const& exception) { validator(exception, expected); } catch (...) { FAIL(); diff --git a/test/unit-test/CMakeLists.txt b/test/unit-test/CMakeLists.txt index f8c9dda..5247732 100644 --- a/test/unit-test/CMakeLists.txt +++ b/test/unit-test/CMakeLists.txt @@ -21,6 +21,7 @@ add_executable(test-continuable-single ${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-forward-decl.cpp ${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-flat-variant.cpp ${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-result.cpp + ${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-ready.cpp ${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-promisify.cpp ${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-erasure.cpp ${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-traverse.cpp diff --git a/test/unit-test/single/test-continuable-erasure.cpp b/test/unit-test/single/test-continuable-erasure.cpp index 0fc8afd..a78951a 100644 --- a/test/unit-test/single/test-continuable-erasure.cpp +++ b/test/unit-test/single/test-continuable-erasure.cpp @@ -44,8 +44,8 @@ struct my_continuation { return true; } - std::tuple<> operator()(query_arg_t) { - return std::make_tuple(); + result<> operator()(unpack_arg_t) { + return make_result(); } }; diff --git a/test/unit-test/single/test-continuable-ready.cpp b/test/unit-test/single/test-continuable-ready.cpp new file mode 100644 index 0000000..400143a --- /dev/null +++ b/test/unit-test/single/test-continuable-ready.cpp @@ -0,0 +1,181 @@ + +/* + Copyright(c) 2015 - 2019 Denis Blank + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files(the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and / or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions : + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +**/ + +#include +#include +#include + +using namespace cti; + +TEST(single_ready_test, is_not_ready_non_immediate) { + auto c = async([] { + // ... + }); + + ASSERT_FALSE(c.is_ready()); +} + +TEST(single_ready_test, is_not_ready_non_immediate_erasure) { + continuable<> c = async([] { + // ... + }); + + ASSERT_FALSE(c.is_ready()); +} + +TEST(single_ready_test, is_ready_immediate) { + { + auto c = make_ready_continuable(); + ASSERT_TRUE(c.is_ready()); + + result<> res = std::move(c).unpack(); + + ASSERT_TRUE(res.is_value()); + } + + { + auto c = make_ready_continuable(22); + ASSERT_TRUE(c.is_ready()); + + result res = std::move(c).unpack(); + + ASSERT_EQ(get<0>(res), 22); + } + + { + auto c = make_ready_continuable(33, 44); + ASSERT_TRUE(c.is_ready()); + + result res = std::move(c).unpack(); + + ASSERT_EQ(get<0>(res), 33); + ASSERT_EQ(get<1>(res), 44); + } + + { + auto c = make_ready_continuable(55, 66, 77); + ASSERT_TRUE(c.is_ready()); + + result res = std::move(c).unpack(); + + ASSERT_EQ(get<0>(res), 55); + ASSERT_EQ(get<1>(res), 66); + ASSERT_EQ(get<2>(res), 77); + } +} + +TEST(single_ready_test, is_ready_immediate_erasure) { + { + continuable<> c = make_ready_continuable(); + + ASSERT_TRUE(c.is_ready()); + + result<> res = std::move(c).unpack(); + + ASSERT_TRUE(res.is_value()); + } + + { + continuable c = make_ready_continuable(22); + ASSERT_TRUE(c.is_ready()); + + result res = std::move(c).unpack(); + + ASSERT_EQ(get<0>(res), 22); + } + + { + continuable c = make_ready_continuable(33, 44); + ASSERT_TRUE(c.is_ready()); + + result res = std::move(c).unpack(); + + ASSERT_EQ(get<0>(res), 33); + ASSERT_EQ(get<1>(res), 44); + } + + { + continuable c = make_ready_continuable(55, 66, 77); + ASSERT_TRUE(c.is_ready()); + + result res = std::move(c).unpack(); + + ASSERT_EQ(get<0>(res), 55); + ASSERT_EQ(get<1>(res), 66); + ASSERT_EQ(get<2>(res), 77); + } +} + +TEST(single_ready_test, is_ready_exception) { + { + auto c = make_exceptional_continuable(supply_test_exception()); + ASSERT_TRUE(c.is_ready()); + + result<> res = std::move(c).unpack(); + + ASSERT_TRUE(res.is_exception()); + + ASSERT_ASYNC_EXCEPTION_RESULT( + make_exceptional_continuable(res.get_exception()), + get_test_exception_proto()) + } + + { + auto c = make_exceptional_continuable(supply_test_exception()); + ASSERT_TRUE(c.is_ready()); + + result res = std::move(c).unpack(); + + ASSERT_ASYNC_EXCEPTION_RESULT( + make_exceptional_continuable(res.get_exception()), + get_test_exception_proto()) + } +} + +TEST(single_ready_test, is_ready_exception_erasure) { + { + continuable<> c = + make_exceptional_continuable(supply_test_exception()); + ASSERT_TRUE(c.is_ready()); + + result<> res = std::move(c).unpack(); + + ASSERT_TRUE(res.is_exception()); + + ASSERT_ASYNC_EXCEPTION_RESULT( + make_exceptional_continuable(res.get_exception()), + get_test_exception_proto()) + } + + { + continuable c = + make_exceptional_continuable(supply_test_exception()); + ASSERT_TRUE(c.is_ready()); + + result res = std::move(c).unpack(); + + ASSERT_ASYNC_EXCEPTION_RESULT( + make_exceptional_continuable(res.get_exception()), + get_test_exception_proto()) + } +}