From a9375c7f22d59f35f46e50bcf3ec77a14984bd9e Mon Sep 17 00:00:00 2001 From: Denis Blank Date: Mon, 26 Nov 2018 04:12:17 +0100 Subject: [PATCH] Implement the exception invokers which fully implements recover, rethrow and cancel now --- include/continuable/detail/core/base.hpp | 147 ++++++++++++++++------- test/playground/test-playground.cpp | 5 + 2 files changed, 110 insertions(+), 42 deletions(-) diff --git a/include/continuable/detail/core/base.hpp b/include/continuable/detail/core/base.hpp index 618c81e..099d40e 100644 --- a/include/continuable/detail/core/base.hpp +++ b/include/continuable/detail/core/base.hpp @@ -323,14 +323,86 @@ constexpr auto invoker_of(traits::identity>) { return make_invoker(sequenced_unpack_invoker(), traits::identity{}); } +inline auto exception_invoker_of(traits::identity) noexcept { + return [](auto&& callback, auto&& next_callback, auto&&... args) { + CONTINUABLE_BLOCK_TRY_BEGIN + util::invoke(std::forward(callback), + std::forward(args)...); + + // The legacy behaviour is not to proceed the chain + // on the first invoked failure handler + (void)next_callback; + CONTINUABLE_BLOCK_TRY_END + }; +} + +inline auto exception_invoker_of(traits::identity) noexcept { + return [](auto&& callback, auto&& next_callback, auto&&... args) { + CONTINUABLE_BLOCK_TRY_BEGIN + empty_result result = + util::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 + }; +} + +inline auto +exception_invoker_of(traits::identity) noexcept { + return [](auto&& callback, auto&& next_callback, auto&&... args) { + CONTINUABLE_BLOCK_TRY_BEGIN + exceptional_result result = + util::invoke(std::forward(callback), + std::forward(args)...); + + // Rethrow the exception to the next handler + invoke_no_except(std::forward(next_callback), + exception_arg_t{}, std::move(result).get_exception()); + CONTINUABLE_BLOCK_TRY_END + }; +} + +template +auto exception_invoker_of(traits::identity>) noexcept { + return [](auto&& callback, auto&& next_callback, auto&&... args) { + CONTINUABLE_BLOCK_TRY_BEGIN + result result = + util::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); + + result_trait::visit( + std::move(result), // + [&](auto&&... values) { + invoke_no_except(std::forward(next_callback), + std::forward(values)...); + }); + + } else if (result.is_exception()) { + // 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 + }; +} + #undef CONTINUABLE_BLOCK_TRY_BEGIN #undef CONTINUABLE_BLOCK_TRY_END } // namespace decoration /// Invoke the callback immediately template -void packed_dispatch(types::this_thread_executor_tag, Invoker&& invoker, - Args&&... args) { +void on_executor(types::this_thread_executor_tag, Invoker&& invoker, + Args&&... args) { // Invoke the callback with the decorated invoker immediately std::forward(invoker)(std::forward(args)...); @@ -338,7 +410,7 @@ void packed_dispatch(types::this_thread_executor_tag, Invoker&& invoker, /// Invoke the callback through the given executor template -void packed_dispatch(Executor&& executor, Invoker&& invoker, Args&&... args) { +void on_executor(Executor&& executor, Invoker&& invoker, Args&&... args) { // Create a worker object which when invoked calls the callback with the // the returned arguments. @@ -348,9 +420,8 @@ void packed_dispatch(Executor&& executor, Invoker&& invoker, Args&&... args) { [&](auto&&... captured_args) { // Just use the packed dispatch method which dispatches the work on // the current thread. - packed_dispatch( - types::this_thread_executor_tag{}, std::move(invoker), - std::forward(captured_args)...); + on_executor(types::this_thread_executor_tag{}, std::move(invoker), + std::forward(captured_args)...); }, std::move(args)); }; @@ -394,32 +465,21 @@ struct result_handler_base(this)->callback_), std::move(args)...))>{}; // Pick the correct invoker that handles decorating of the result auto invoker = decoration::invoker_of(result); // Invoke the callback - packed_dispatch(std::move(static_cast(this)->executor_), - std::move(invoker), - std::move(static_cast(this)->callback_), - std::move(static_cast(this)->next_callback_), - std::move(args)...); + on_executor(std::move(static_cast(this)->executor_), + std::move(invoker), + std::move(static_cast(this)->callback_), + std::move(static_cast(this)->next_callback_), + std::move(args)...); } }; -/* -inline auto make_exception_invoker( - std::integral_constant) noexcept { -return; -} -inline auto make_exception_invoker( - std::integral_constant) noexcept { -return; -} -*/ - template struct error_handler_base; template @@ -435,33 +495,36 @@ template struct error_handler_base { /// The operator which is called when an error occurred void operator()(exception_arg_t, exception_t exception) && { + constexpr auto result = traits::identify(this)->callback_), + std::move(exception)))>{}; + + auto invoker = decoration::exception_invoker_of(result); + // Invoke the error handler - packed_dispatch( - std::move(static_cast(this)->executor_), - [](auto&& callback, exception_t exception) { - // Errors are not partial invoked - // NOLINTNEXTLINE(hicpp-move-const-arg, performance-move-const-arg) - std::forward(callback)(std::move(exception)); - }, - std::move(static_cast(this)->callback_), std::move(exception)); + on_executor(std::move(static_cast(this)->executor_), + std::move(invoker), + std::move(static_cast(this)->callback_), + std::move(static_cast(this)->next_callback_), + std::move(exception)); } }; template struct error_handler_base { /// The operator which is called when an error occurred void operator()(exception_arg_t, exception_t exception) && { + constexpr auto result = traits::identify(this)->callback_), + exception_arg_t{}, std::move(exception)))>{}; + + auto invoker = decoration::exception_invoker_of(result); + // Invoke the error handler - packed_dispatch( - std::move(static_cast(this)->executor_), - [](auto&& callback, exception_t exception) { - // Errors are not partial invoked - std::forward(callback)( - exception_arg_t{}, - // NOLINTNEXTLINE(hicpp-move-const-arg, - // performance-move-const-arg) - std::move(exception)); - }, - std::move(static_cast(this)->callback_), std::move(exception)); + on_executor(std::move(static_cast(this)->executor_), + std::move(invoker), + std::move(static_cast(this)->callback_), + std::move(static_cast(this)->next_callback_), + exception_arg_t{}, std::move(exception)); } }; } // namespace proto diff --git a/test/playground/test-playground.cpp b/test/playground/test-playground.cpp index cdd60e4..3f4b0eb 100644 --- a/test/playground/test-playground.cpp +++ b/test/playground/test-playground.cpp @@ -22,6 +22,11 @@ #include +using namespace cti; + int main(int, char**) { // ... + make_exceptional_continuable(exception_t{}).fail([](exception_t) { + // ... + }); }