diff --git a/include/continuable/continuable-base.hpp b/include/continuable/continuable-base.hpp index ca34fd9..af9e89d 100644 --- a/include/continuable/continuable-base.hpp +++ b/include/continuable/continuable-base.hpp @@ -196,9 +196,10 @@ public: template auto then(T&& callback, E&& executor = detail::types::this_thread_executor_tag{}) && { - return detail::base::chain_continuation(std::move(*this).materialize(), - std::forward(callback), - std::forward(executor)); + return detail::base::chain_continuation( + std::move(*this).materialize(), std::forward(callback), + std::forward(executor)); } /// Additional overload of the continuable_base::then() method @@ -268,9 +269,10 @@ public: template auto fail(T&& callback, E&& executor = detail::types::this_thread_executor_tag{}) && { - return detail::base::chain_error_handler(std::move(*this).materialize(), - std::forward(callback), - std::forward(executor)); + return detail::base::chain_continuation( + std::move(*this).materialize(), std::forward(callback), + std::forward(executor)); } /// A method which allows to use an overloaded callable for the error @@ -303,9 +305,11 @@ public: template auto flow(T&& callback, E&& executor = detail::types::this_thread_executor_tag{}) && { - return detail::base::chain_flow_handler(std::move(*this).materialize(), - std::forward(callback), - std::forward(executor)); + return detail::base::chain_continuation< + detail::base::handle_results::yes, + detail::base::handle_errors::forward>(std::move(*this).materialize(), + std::forward(callback), + std::forward(executor)); } /// Ignores all error which ocured until the point the function was called diff --git a/include/continuable/detail/base.hpp b/include/continuable/detail/base.hpp index ed7fc35..530d853 100644 --- a/include/continuable/detail/base.hpp +++ b/include/continuable/detail/base.hpp @@ -261,12 +261,34 @@ void packed_dispatch(Executor&& executor, Invoker&& invoker, Args&&... args) { std::forward(executor)(std::move(work)); } +/// Tells whether we potentially move the chain upwards and handle the result +enum class handle_results { + no, //< The result is forwarded to the next callable + yes //< The result is handled by the current callable +}; + +/// Tells whether we handle the error through the callback +enum class handle_errors { + no, //< The error is forwarded to the next callable + plain, //< The error is the only argument accepted by the callable + forward //< The error is forwarded to the callable while keeping its tag +}; + namespace callbacks { namespace proto { -template -struct accept_result_base; +template +struct result_handler_base; template -struct accept_result_base> { +struct result_handler_base> { + void operator()(Args... args) { + // Forward the arguments to the next callback + std::move(static_cast(this)->next_callback_)(std::move(args)...); + } +}; +template +struct result_handler_base> { /// The operator which is called when the result was provided void operator()(Args... args) { // In order to retrieve the correct decorator we must know what the @@ -286,25 +308,27 @@ struct accept_result_base> { } }; -template -struct reject_result_base; -template -struct reject_result_base> { - void operator()(Args... args) { - // Forward the arguments to the next callback - std::move(static_cast(this)->next_callback_)(std::move(args)...); - } -}; +inline auto make_error_invoker( + std::integral_constant) noexcept { + return [](auto&& callback, types::error_type&& error) { + // Errors are not partial invoked + std::move(callback)(std::move(error)); + }; +} +inline auto make_error_invoker( + std::integral_constant) noexcept { + return [](auto&& callback, types::error_type&& error) { + // Errors are not partial invoked + std::move(callback)(types::dispatch_error_tag{}, std::move(error)); + }; +} -template -struct accept_error_base { +template +struct error_handler_base { void operator()(types::dispatch_error_tag, types::error_type error) { // Just invoke the error handler, cancel the calling hierarchy after - auto invoker = [](typename Base::CallbackT&& callback, - types::error_type&& error) { - // Errors are not partial invoked - std::move(callback)(std::move(error)); - }; + auto invoker = make_error_invoker( + std::integral_constant{}); // Invoke the error handler packed_dispatch( @@ -312,35 +336,35 @@ struct accept_error_base { std::move(static_cast(this)->callback_), std::move(error)); } }; - template -struct reject_error_base { +struct error_handler_base { /// The operator which is called when an error occurred void operator()(types::dispatch_error_tag tag, types::error_type error) { // Forward the error to the next callback std::move(static_cast(this)->next_callback_)(tag, std::move(error)); } }; +} // namespace proto -template class ResultCallbackBase, - template class ErrorCallbackBase> +template struct callback_base; -template class ResultCallbackBase, - template class ErrorCallbackBase> -struct callback_base, Callback, Executor, - NextCallback, ResultCallbackBase, ErrorCallbackBase> - : ResultCallbackBase< - callback_base, Callback, Executor, - NextCallback, ResultCallbackBase, ErrorCallbackBase>, +template +struct callback_base, HandleResults, + HandleErrors, Callback, Executor, NextCallback> + : proto::result_handler_base< + HandleResults, + callback_base, HandleResults, + HandleErrors, Callback, Executor, NextCallback>, hints::signature_hint_tag>, - ErrorCallbackBase< - callback_base, Callback, Executor, - NextCallback, ResultCallbackBase, ErrorCallbackBase>> { + proto::error_handler_base< + HandleErrors, + callback_base, HandleResults, + HandleErrors, Callback, Executor, NextCallback>> { using CallbackT = Callback; @@ -355,15 +379,17 @@ struct callback_base, Callback, Executor, } /// Pull the result handling operator() in - using ResultCallbackBase< - callback_base, Callback, Executor, - NextCallback, ResultCallbackBase, ErrorCallbackBase>, + using proto::result_handler_base< + HandleResults, + callback_base, HandleResults, + HandleErrors, Callback, Executor, NextCallback>, hints::signature_hint_tag>::operator(); /// Pull the error handling operator() in - using ErrorCallbackBase< - callback_base, Callback, Executor, - NextCallback, ResultCallbackBase, ErrorCallbackBase>>:: + using proto::error_handler_base< + HandleErrors, + callback_base, HandleResults, + HandleErrors, Callback, Executor, NextCallback>>:: operator(); /// Resolves the continuation with the given values @@ -377,18 +403,17 @@ struct callback_base, Callback, Executor, } }; -template class ResultCallbackBase, - template class ErrorCallbackBase, typename Callback, - typename Executor, typename NextCallback> +template auto make_callback(Callback&& callback, Executor&& executor, NextCallback&& next_callback) { - return callback_base, std::decay_t, - std::decay_t, ResultCallbackBase, - ErrorCallbackBase>{ + return callback_base, std::decay_t, + std::decay_t>{ std::forward(callback), std::forward(executor), std::forward(next_callback)}; } -} // namespace proto /// Once this was a workaround for GCC bug: /// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64095 @@ -415,8 +440,10 @@ struct final_callback { /// Returns the next hint when the callback is invoked with the given hint template -constexpr auto next_hint_of(traits::identity /*callback*/, - hints::signature_hint_tag /*current*/) { +constexpr auto +next_hint_of(std::integral_constant, + traits::identity /*callback*/, + hints::signature_hint_tag /*current*/) { // Partial Invoke the given callback using Result = decltype( util::partial_invoke(std::declval(), std::declval()...)); @@ -424,6 +451,14 @@ constexpr auto next_hint_of(traits::identity /*callback*/, // Return the hint of thr given invoker return decoration::invoker_of(traits::identity_of()).hint(); } +/// Don't progress the hint when we don't continue +template +constexpr auto +next_hint_of(std::integral_constant, + traits::identity /*callback*/, + hints::signature_hint_tag current) { + return current; +} /// Chains a callback together with a continuation and returns a continuation: /// @@ -434,14 +469,17 @@ constexpr auto next_hint_of(traits::identity /*callback*/, /// This function returns a function accepting the next callback in the chain: /// - Result: continuation<[](auto&& callback) { /*...*/ }> /// -template +template auto chain_continuation(Continuation&& continuation, Callback&& callback, Executor&& executor) { static_assert(is_continuation>{}, "Expected a continuation!"); - auto hint = hint_of(traits::identity_of(continuation)); - auto next_hint = next_hint_of(traits::identity_of(callback), hint); + using Hint = decltype(hint_of(traits::identity_of(continuation))); + auto next_hint = + next_hint_of(std::integral_constant{}, + traits::identity_of(callback), Hint{}); // TODO consume only the data here so the freeze isn't needed auto ownership_ = attorney::ownership_of(continuation); @@ -462,13 +500,10 @@ auto chain_continuation(Continuation&& continuation, Callback&& callback, // - Continuation: continuation<[](auto&& callback) { callback("hi"); }> // - Callback: [](std::string) { } // - NextCallback: []() { } - using Hint = decltype(hint_of(traits::identity_of(continuation))); - - auto proxy = callbacks::proto::make_callback< - Hint, callbacks::proto::accept_result_base, - callbacks::proto::reject_error_base>( - std::move(callback), std::move(executor), - std::forward(next_callback)); + auto proxy = + callbacks::make_callback( + std::move(callback), std::move(executor), + std::forward(next_callback)); // Invoke the continuation with a proxy callback. // The proxy callback is responsible for passing @@ -479,49 +514,6 @@ auto chain_continuation(Continuation&& continuation, Callback&& callback, next_hint, ownership_); } -/// Chains an error handler together with a continuation and -/// returns a continuation. The current future result of the continuation -//// stays unchanged. -/// -template -auto chain_error_handler(Continuation&& continuation, Callback&& callback, - Executor&& executor) { - static_assert(is_continuation>{}, - "Expected a continuation!"); - - // The current hint will also be the next one - auto hint = hint_of(traits::identity_of(continuation)); - - // TODO consume only the data here so the freeze isn't needed - auto ownership_ = attorney::ownership_of(continuation); - continuation.freeze(); - - return attorney::create( - [ - continuation = std::forward(continuation), - callback = std::forward(callback), - executor = std::forward(executor) - ](auto&& next_callback) mutable { - - using Hint = decltype(hint_of(traits::identity_of(continuation))); - - auto proxy = callbacks::proto::make_callback< - Hint, callbacks::proto::reject_result_base, - callbacks::proto::accept_error_base>( - std::move(callback), std::move(executor), - std::forward(next_callback)); - - attorney::invoke_continuation(std::move(continuation), - std::move(proxy)); - }, - hint, ownership_); -} - -template -auto chain_flow_handler(Continuation&& /*continuation*/, - Callback&& /*callback*/, Executor&& /*executor*/) { -} - /// Final invokes the given continuation chain: /// /// For example given: