diff --git a/include/continuable/continuable-result.hpp b/include/continuable/continuable-result.hpp index 461a6c0..0324b88 100644 --- a/include/continuable/continuable-result.hpp +++ b/include/continuable/continuable-result.hpp @@ -39,11 +39,23 @@ #include namespace cti { -/// A class which is convertible to any result and that definitly holds no -/// value so the real result gets invalidated when -/// this object is passed to it +/// \defgroup Result Result +/// provides the \ref result class and corresponding utility functions to work +/// with the result of an asynchronous operation which can possibly yield: +/// - *no result*: If the operation didn't finish +/// - *a value*: If the operation finished successfully +/// - *an exception*: If the operation finished with an exception +/// \{ + +/// A class which is convertible to any \ref result and that definitly holds no +/// value so the real result gets invalidated when this object is passed to it. struct empty_result {}; +/// Returns a new empty result +inline empty_result make_empty_result() { + return {}; +} + /// A class which is convertible to any result and that definitly holds /// an exception which is then passed to the converted result object. class exceptional_result { @@ -68,24 +80,38 @@ public: return *this; } + /// Sets an exception void set_exception(exception_t exception) { // NOLINTNEXTLINE(hicpp-move-const-arg, performance-move-const-arg) exception_ = std::move(exception); } + /// Returns the contained exception exception_t& get_exception() & noexcept { return exception_; } + /// \copydoc get_exception exception_t const& get_exception() const& noexcept { return exception_; } + /// \copydoc get_exception exception_t&& get_exception() && noexcept { return std::move(exception_); } }; -/// A class similar to the one in the result proposal, -/// however it's capable of carrying an exception_t. +/// Returns a new exceptional result from the given exception +// NOLINTNEXTLINE(performance-unnecessary-value-param) +inline exceptional_result make_exceptional_result(exception_t exception) { + // NOLINTNEXTLINE(hicpp-move-const-arg, performance-move-const-arg) + return exceptional_result{std::move(exception)}; +} + +/// The result class can carry the three kinds of results an asynchronous +/// operation can return: no result, a value or an exception. +/// - *no result*: If the operation didn't finish +/// - *a value*: If the operation finished successfully +/// - *an exception*: If the operation finished with an exception template class result { using trait_t = detail::result_trait; @@ -108,7 +134,8 @@ public: } explicit result(exception_t exception) : variant_(std::move(exception)) { } - result(empty_result){}; + result(empty_result) { + } result(exceptional_result exceptional_result) : variant_(std::move(exceptional_result.get_exception())) { } @@ -146,6 +173,7 @@ public: return is_value(); } + /// Returns the decltype(auto) get_value() & noexcept { return trait_t::unwrap(variant_.template cast()); } @@ -184,16 +212,7 @@ template auto make_result(T&&... values) { return result...>(std::forward(values)...); } - -// NOLINTNEXTLINE(performance-unnecessary-value-param) -inline exceptional_result make_exceptional_result(exception_t exception) { - // NOLINTNEXTLINE(hicpp-move-const-arg, performance-move-const-arg) - return exceptional_result{std::move(exception)}; -} - -inline empty_result make_empty_result() { - return {}; -} +/// \} } // namespace cti #endif // CONTINUABLE_RESULT_HPP_INCLUDED diff --git a/include/continuable/detail/core/base.hpp b/include/continuable/detail/core/base.hpp index d7a29e8..8faba9f 100644 --- a/include/continuable/detail/core/base.hpp +++ b/include/continuable/detail/core/base.hpp @@ -159,9 +159,9 @@ constexpr auto make_invoker(T&& invoke, hints::signature_hint_tag) { } /// - continuable -> result(next_callback); -template +template constexpr auto -invoker_of(Hint, traits::identity>) { +invoker_of(traits::identity>) { /// Get the hint of the unwrapped returned continuable using Type = decltype(std::declval>().finish()); @@ -184,8 +184,8 @@ invoker_of(Hint, traits::identity>) { } /// - ? -> next_callback(?) -template -constexpr auto invoker_of(Hint, traits::identity) { +template +constexpr auto invoker_of(traits::identity) { return make_invoker( [](auto&& callback, auto&& next_callback, auto&&... args) { CONTINUABLE_BLOCK_TRY_BEGIN @@ -201,8 +201,7 @@ constexpr auto invoker_of(Hint, traits::identity) { } /// - void -> next_callback() -template -auto invoker_of(Hint, traits::identity) { +inline auto invoker_of(traits::identity) { return make_invoker( [](auto&& callback, auto&& next_callback, auto&&... args) { CONTINUABLE_BLOCK_TRY_BEGIN @@ -216,52 +215,73 @@ auto invoker_of(Hint, traits::identity) { } /// - empty_result -> -template -auto invoker_of(Hint, traits::identity) { +inline auto invoker_of(traits::identity) { return make_invoker( [](auto&& callback, auto&& next_callback, auto&&... args) { - util::unused(callback, next_callback, args...); - // TODO - /*CONTINUABLE_BLOCK_TRY_BEGIN - util::partial_invoke(std::forward(callback), - std::forward(args)...); - invoke_no_except( - std::forward(next_callback)); - CONTINUABLE_BLOCK_TRY_END*/ + (void)next_callback; + CONTINUABLE_BLOCK_TRY_BEGIN + empty_result result = + util::partial_invoke(std::forward(callback), + std::forward(args)...); + + // Don't invoke anything here since returning an empty result + // cancels the asynchronous chain effectively. + (void)result; + CONTINUABLE_BLOCK_TRY_END }, traits::identity<>{}); } -/// - exceptional_result -> Hint -template -auto invoker_of(Hint, traits::identity) { +/// - exceptional_result -> +inline auto invoker_of(traits::identity) { return make_invoker( [](auto&& callback, auto&& next_callback, auto&&... args) { util::unused(callback, next_callback, args...); - // TODO - /*CONTINUABLE_BLOCK_TRY_BEGIN - util::partial_invoke(std::forward(callback), - std::forward(args)...); - invoke_no_except( - std::forward(next_callback)); - CONTINUABLE_BLOCK_TRY_END*/ + CONTINUABLE_BLOCK_TRY_BEGIN + exceptional_result result = + util::partial_invoke(std::forward(callback), + std::forward(args)...); + + // Forward the exception to the next available handler + invoke_no_except(std::forward(next_callback), + exception_arg_t{}, + std::move(result).get_exception()); + CONTINUABLE_BLOCK_TRY_END }, traits::identity<>{}); } -/// - result -> Args... -template -auto invoker_of(Hint, traits::identity>) { +/// - result -> next_callback(?...) +template +auto invoker_of(traits::identity>) { return make_invoker( [](auto&& callback, auto&& next_callback, auto&&... args) { - util::unused(callback, next_callback, args...); - // TODO - /*CONTINUABLE_BLOCK_TRY_BEGIN - util::partial_invoke(std::forward(callback), - std::forward(args)...); - invoke_no_except( - std::forward(next_callback)); - CONTINUABLE_BLOCK_TRY_END*/ + CONTINUABLE_BLOCK_TRY_BEGIN + result result = + util::partial_invoke(std::forward(callback), + std::forward(args)...); + // + if (result.is_value()) { + // Workaround for MSVC not capturing the reference + // correctly inside the lambda. + using Next = decltype(next_callback); + + traits::unpack( + [&](auto&&... types) { + /// TODO Add inplace resolution here + + invoke_no_except(std::forward(next_callback), + std::forward(types)...); + }, + std::move(result)); + + } else if (result.is_exception()) { + + } + + // Otherwise the result is empty and we are cancelling our + // asynchronous chain. + CONTINUABLE_BLOCK_TRY_END }, traits::identity{}); } @@ -275,8 +295,8 @@ inline auto sequenced_unpack_invoker() { util::partial_invoke(std::forward(callback), std::forward(args)...); - // Workaround for MSVC not capturing the reference correctly inside - // the lambda. + // Workaround for MSVC not capturing the reference + // correctly inside the lambda. using Next = decltype(next_callback); traits::unpack( @@ -292,15 +312,15 @@ inline auto sequenced_unpack_invoker() { } // namespace decoration // - std::pair -> next_callback(?, ?) -template -constexpr auto invoker_of(Hint, traits::identity>) { +template +constexpr auto invoker_of(traits::identity>) { return make_invoker(sequenced_unpack_invoker(), traits::identity{}); } // - std::tuple -> next_callback(?...) -template -constexpr auto invoker_of(Hint, traits::identity>) { +template +constexpr auto invoker_of(traits::identity>) { return make_invoker(sequenced_unpack_invoker(), traits::identity{}); } @@ -379,9 +399,7 @@ struct result_handler_base(this)->callback_), std::move(args)...))>{}; // Pick the correct invoker that handles decorating of the result - auto invoker = - decoration::invoker_of(hints::signature_hint_tag{}, // - result); + auto invoker = decoration::invoker_of(result); // Invoke the callback packed_dispatch(std::move(static_cast(this)->executor_), @@ -547,15 +565,13 @@ template constexpr auto next_hint_of(std::integral_constant, traits::identity /*callback*/, - hints::signature_hint_tag current) { + hints::signature_hint_tag /*current*/) { // Partial Invoke the given callback using Result = decltype( util::partial_invoke(std::declval(), std::declval()...)); // Return the hint of thr given invoker - return decltype(decoration::invoker_of(current, // - traits::identify{}) - .hint()){}; + return decltype(decoration::invoker_of(traits::identify{}).hint()){}; } /// Don't progress the hint when we don't continue template diff --git a/test/unit-test/multi/test-continuable-await.cpp b/test/unit-test/multi/test-continuable-await.cpp index 90cc85e..73b1466 100644 --- a/test/unit-test/multi/test-continuable-await.cpp +++ b/test/unit-test/multi/test-continuable-await.cpp @@ -39,7 +39,7 @@ cti::continuable<> resolve_async(S&& supplier) { co_await supplier(); // 1 args - co_await supplier(1); + int a1 = co_await supplier(1); EXPECT_EQ(a1, 1); // 2-n args