diff --git a/include/continuable/detail/core/base.hpp b/include/continuable/detail/core/base.hpp index 4fe74e6..16cc0f2 100644 --- a/include/continuable/detail/core/base.hpp +++ b/include/continuable/detail/core/base.hpp @@ -326,8 +326,8 @@ constexpr auto invoker_of(traits::identity>) { inline auto exception_invoker_of(traits::identity) noexcept { return [](auto&& callback, auto&& next_callback, auto&&... args) { CONTINUABLE_BLOCK_TRY_BEGIN - util::partial_invoke(std::forward(callback), - std::forward(args)...); + util::invoke(std::forward(callback), + std::forward(args)...); // The legacy behaviour is not to proceed the chain // on the first invoked failure handler @@ -336,9 +336,81 @@ inline auto exception_invoker_of(traits::identity) noexcept { }; } -template -auto exception_invoker_of(Other other) noexcept { - return invoker_of(other); +/*template +auto exception_invoker_of(traits::identity> id) noexcept { + return invoker_of(id); +}*/ + +/// - empty_result -> +inline auto exception_invoker_of(traits::identity) { + return make_invoker( + [](auto&& callback, auto&& next_callback, auto&&... args) { + (void)next_callback; + 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 + }, + traits::identity<>{}); +} + +/// - exceptional_result -> +inline auto exception_invoker_of(traits::identity) { + return make_invoker( + [](auto&& callback, auto&& next_callback, auto&&... args) { + util::unused(callback, next_callback, args...); + CONTINUABLE_BLOCK_TRY_BEGIN + exceptional_result result = + util::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 -> next_callback(?...) +template +auto exception_invoker_of(traits::identity>) { + return make_invoker( + [](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()); + } + + // Otherwise the result is empty and we are cancelling our + // asynchronous chain. + CONTINUABLE_BLOCK_TRY_END + }, + traits::identity{}); } #undef CONTINUABLE_BLOCK_TRY_BEGIN @@ -587,16 +659,25 @@ next_hint_of(std::integral_constant, return current; } +namespace detail { +template +struct exception_stripper_proxy { + Callable callable_; + template + auto operator()(exception_arg_t, Args&&... args) + -> decltype(util::invoke(std::declval(), // + std::declval()...)) { + return util::invoke(std::move(callable_), // + std::forward(args)...); + }; +}; +} // namespace detail + /// Removes the exception_arg_t from the arguments passed to the given callable template auto strip_exception_arg(Callable&& callable) { - return [callable = std::forward(callable)] // - (exception_arg_t, auto&&... args) mutable - -> decltype(util::invoke(std::declval(), // - std::declval()...)) { - return util::invoke(std::move(callable), // - std::forward(args)...); - }; + using proxy = detail::exception_stripper_proxy>; + return proxy{std::forward(callable)}; } /// Chains a callback together with a continuation and returns a continuation: diff --git a/include/continuable/detail/utility/util.hpp b/include/continuable/detail/utility/util.hpp index 0eacf66..d0d3777 100644 --- a/include/continuable/detail/utility/util.hpp +++ b/include/continuable/detail/utility/util.hpp @@ -64,59 +64,62 @@ auto forward_except_last(T&& sequenceable) { return forward_except_last_impl(std::forward(sequenceable), sequence); } -/// We are able to call the callable with the arguments given in the tuple -template -auto partial_invoke_impl(std::true_type, T&& callable, - std::tuple args) { - return traits::unpack(std::forward(callable), std::move(args)); -} +template +struct invocation_env { + /// We are able to call the callable with the arguments given in the tuple + template + static auto partial_invoke_impl(std::true_type, T&& callable, + std::tuple args) { + return traits::unpack(std::forward(callable), std::move(args)); + } -/// We were unable to call the callable with the arguments in the tuple. -/// Remove the last argument from the tuple and try it again. -template -auto partial_invoke_impl(std::false_type, T&& callable, - std::tuple args) { + /// We were unable to call the callable with the arguments in the tuple. + /// Remove the last argument from the tuple and try it again. + template + static auto partial_invoke_impl(std::false_type, T&& callable, + std::tuple args) { - // If you are encountering this assertion you tried to attach a callback - // which can't accept the arguments of the continuation. - // - // ```cpp - // continuable c; - // std::move(c).then([](std::vector v) { /*...*/ }) - // ``` - static_assert( - sizeof...(Args) > 0, - "There is no way to call the given object with these arguments!"); + // If you are encountering this assertion you tried to attach a callback + // which can't accept the arguments of the continuation. + // + // ```cpp + // continuable c; + // std::move(c).then([](std::vector v) { /*...*/ }) + // ``` + static_assert( + sizeof...(Args) > Keep, + "There is no way to call the given object with these arguments!"); - // Remove the last argument from the tuple - auto next = forward_except_last(std::move(args)); + // Remove the last argument from the tuple + auto next = forward_except_last(std::move(args)); - // Test whether we are able to call the function with the given tuple - traits::is_invokable_from_tuple - is_invokable; + // Test whether we are able to call the function with the given tuple + traits::is_invokable_from_tuple + is_invokable; - return partial_invoke_impl(is_invokable, std::forward(callable), - std::move(next)); -} + return partial_invoke_impl(is_invokable, std::forward(callable), + std::move(next)); + } -/// Shortcut - we can call the callable directly -template -auto partial_invoke_impl_shortcut(std::true_type, T&& callable, - Args&&... args) { - return std::forward(callable)(std::forward(args)...); -} + /// Shortcut - we can call the callable directly + template + static auto partial_invoke_impl_shortcut(std::true_type, T&& callable, + Args&&... args) { + return std::forward(callable)(std::forward(args)...); + } -/// Failed shortcut - we were unable to invoke the callable with the -/// original arguments. -template -auto partial_invoke_impl_shortcut(std::false_type failed, T&& callable, - Args&&... args) { + /// Failed shortcut - we were unable to invoke the callable with the + /// original arguments. + template + static auto partial_invoke_impl_shortcut(std::false_type failed, T&& callable, + Args&&... args) { - // Our shortcut failed, convert the arguments into a forwarding tuple - return partial_invoke_impl( - failed, std::forward(callable), - std::forward_as_tuple(std::forward(args)...)); -} + // Our shortcut failed, convert the arguments into a forwarding tuple + return partial_invoke_impl( + failed, std::forward(callable), + std::forward_as_tuple(std::forward(args)...)); + } +}; } // namespace detail /// Partially invokes the given callable with the given arguments. @@ -131,7 +134,8 @@ template // The implementation is done in a shortcut way so there are less // type instantiations needed to call the callable with its full signature. - return detail::partial_invoke_impl_shortcut( + using env = detail::invocation_env<0U>; + return env::partial_invoke_impl_shortcut( is_invokable, std::forward(callable), std::forward(args)...); }