From dc205c8e513687082818608702183b8e87b4928a Mon Sep 17 00:00:00 2001 From: Denis Blank Date: Wed, 4 Oct 2017 01:11:25 +0200 Subject: [PATCH] Started on accepting flowing callables which accept all paths --- include/continuable/continuable-base.hpp | 51 +++++- include/continuable/detail/base.hpp | 224 ++++++++++++----------- test/playground/test-playground.cpp | 2 +- 3 files changed, 164 insertions(+), 113 deletions(-) diff --git a/include/continuable/continuable-base.hpp b/include/continuable/continuable-base.hpp index 1c13e42..ca34fd9 100644 --- a/include/continuable/continuable-base.hpp +++ b/include/continuable/continuable-base.hpp @@ -33,7 +33,6 @@ #include #include -#include #include #include @@ -274,6 +273,52 @@ public: std::forward(executor)); } + /// A method which allows to use an overloaded callable for the error + /// as well as the valid result path. + /// + /// \param callback The callback which is used to process the current + /// asynchronous result and error on arrival. + /// + /// ```cpp + /// struct my_callable { + /// void operator() (std::string result) { + /// // ... + /// } + /// void operator() (cti::dispatch_error_tag, cti::error_type) { + /// // ... + /// } + /// + /// // Will receive errors and results + /// http_request("github.com") + /// .flow(my_callable{}); + /// ``` + /// + /// \param executor The optional executor which is used to dispatch + /// the callback. See the description in `then` above. + /// + /// \returns Returns a continuable_base with an asynchronous return type + /// depending on the current result type. + /// + /// \since version 2.0.0 + 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)); + } + + /// Ignores all error which ocured until the point the function was called + /// + /// \note This can be used to create a continuable which doesn't resolve + /// the continuation on errors. + /// + /// \since version 2.0.0 + /* TODO move to transforms + auto flat() && { + return std::move(*this).fail([](auto&&) {}); + }*/ + /// Invokes both continuable_base objects parallel and calls the /// callback with the result of both continuable_base objects. /// @@ -600,9 +645,11 @@ auto make_continuable(Continuation&& continuation) { /// }; /// /// // Will receive errors and results -/// continuable.then(my_callable{}); +/// continuable.flow(my_callable{}); /// ``` /// +/// \note see continuable::flow for details. +/// /// \since version 2.0.0 using detail::types::dispatch_error_tag; diff --git a/include/continuable/detail/base.hpp b/include/continuable/detail/base.hpp index 8f64afe..ed7fc35 100644 --- a/include/continuable/detail/base.hpp +++ b/include/continuable/detail/base.hpp @@ -162,8 +162,9 @@ invoker_of(traits::identity>) { return make_invoker( [](auto&& callback, auto&& next_callback, auto&&... args) { - auto continuation_ = std::forward(callback)( - std::forward(args)...); + auto continuation_ = + util::partial_invoke(std::forward(callback), + std::forward(args)...); attorney::invoke_continuation( std::move(continuation_), @@ -177,8 +178,9 @@ template constexpr auto invoker_of(traits::identity) { return make_invoker( [](auto&& callback, auto&& next_callback, auto&&... args) { - auto result = std::forward(callback)( - std::forward(args)...); + auto result = + util::partial_invoke(std::forward(callback), + std::forward(args)...); std::forward(next_callback)(std::move(result)); }, @@ -189,8 +191,8 @@ constexpr auto invoker_of(traits::identity) { inline auto invoker_of(traits::identity) { return make_invoker( [](auto&& callback, auto&& next_callback, auto&&... args) { - std::forward(callback)( - std::forward(args)...); + util::partial_invoke(std::forward(callback), + std::forward(args)...); std::forward(next_callback)(); }, @@ -201,8 +203,9 @@ inline auto invoker_of(traits::identity) { /// objects where std::get is applicable. inline auto sequenced_unpack_invoker() { return [](auto&& callback, auto&& next_callback, auto&&... args) { - auto result = std::forward(callback)( - std::forward(args)...); + auto result = + util::partial_invoke(std::forward(callback), + std::forward(args)...); traits::unpack(std::move(result), [&](auto&&... types) { /// TODO Add inplace resolution here @@ -259,12 +262,47 @@ void packed_dispatch(Executor&& executor, Invoker&& invoker, Args&&... args) { } namespace callbacks { +namespace proto { +template +struct accept_result_base; +template +struct accept_result_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 + // result type is. + auto result = traits::identity_of(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)...); + } +}; + +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)...); + } +}; + template -struct error_handler_base { +struct accept_error_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)); }; @@ -275,34 +313,34 @@ struct error_handler_base { } }; -template -struct result_callback_error_base { +template +struct reject_error_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)); } }; -template -struct result_callback_error_base< - Base, std::enable_if_t>::value>> - : error_handler_base { - using error_handler_base::operator(); -}; template -struct result_callback; + typename NextCallback, + template class ResultCallbackBase, + template class ErrorCallbackBase> +struct callback_base; template -struct result_callback, Callback, Executor, - NextCallback> - : result_callback_error_base< - result_callback, Callback, - Executor, NextCallback>> { + typename NextCallback, + template class ResultCallbackBase, + template class ErrorCallbackBase> +struct callback_base, Callback, Executor, + NextCallback, ResultCallbackBase, ErrorCallbackBase> + : ResultCallbackBase< + callback_base, Callback, Executor, + NextCallback, ResultCallbackBase, ErrorCallbackBase>, + hints::signature_hint_tag>, + ErrorCallbackBase< + callback_base, Callback, Executor, + NextCallback, ResultCallbackBase, ErrorCallbackBase>> { using CallbackT = Callback; @@ -310,29 +348,23 @@ struct result_callback, Callback, Executor, Executor executor_; NextCallback next_callback_; - explicit result_callback(Callback callback, Executor executor, - NextCallback next_callback) + explicit callback_base(Callback callback, Executor executor, + NextCallback next_callback) : callback_(std::move(callback)), executor_(std::move(executor)), next_callback_(std::move(next_callback)) { } - /// 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 - // result type is. - auto result = traits::identity_of(); + /// Pull the result handling operator() in + using ResultCallbackBase< + callback_base, Callback, Executor, + NextCallback, ResultCallbackBase, ErrorCallbackBase>, + hints::signature_hint_tag>::operator(); - // Pick the correct invoker that handles decorating of the result - auto invoker = decoration::invoker_of(result); - - // Invoke the callback - packed_dispatch(std::move(executor_), std::move(invoker), - std::move(callback_), std::move(next_callback_), - std::move(args)...); - } - - using result_callback_error_base::operator(); + /// Pull the error handling operator() in + using ErrorCallbackBase< + callback_base, Callback, Executor, + NextCallback, ResultCallbackBase, ErrorCallbackBase>>:: + operator(); /// Resolves the continuation with the given values void set_value(Args... args) { @@ -345,47 +377,18 @@ struct result_callback, Callback, Executor, } }; -template -struct error_callback; - -template -struct error_callback, Callback, Executor, - NextCallback> - : error_handler_base, - Callback, Executor, NextCallback>> { - - using CallbackT = Callback; - - Callback callback_; - Executor executor_; - NextCallback next_callback_; - - explicit error_callback(Callback callback, Executor executor, - NextCallback next_callback) - : callback_(std::move(callback)), executor_(std::move(executor)), - next_callback_(std::move(next_callback)) { - } - - /// The operator which is called when the result was provided - void operator()(Args... args) { - // Forward the arguments to the next callback - std::move(next_callback_)(std::move(args)...); - } - - using error_handler_base::operator(); - - /// Resolves the continuation with the given values - void set_value(Args... args) { - (*this)(std::move(args)...); - } - - /// Resolves the continuation with the given error variable. - void set_exception(types::error_type error) { - (*this)(types::dispatch_error_tag{}, std::move(error)); - } -}; +template class ResultCallbackBase, + template class ErrorCallbackBase, typename Callback, + typename Executor, typename NextCallback> +auto make_callback(Callback&& callback, Executor&& executor, + NextCallback&& next_callback) { + return callback_base, std::decay_t, + std::decay_t, ResultCallbackBase, + ErrorCallbackBase>{ + 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 @@ -414,9 +417,12 @@ struct final_callback { template constexpr auto next_hint_of(traits::identity /*callback*/, hints::signature_hint_tag /*current*/) { - return decoration::invoker_of(traits::identity_of()( - std::declval()...))>()) - .hint(); + // Partial Invoke the given callback + using Result = decltype( + util::partial_invoke(std::declval(), std::declval()...)); + + // Return the hint of thr given invoker + return decoration::invoker_of(traits::identity_of()).hint(); } /// Chains a callback together with a continuation and returns a continuation: @@ -434,15 +440,8 @@ auto chain_continuation(Continuation&& continuation, Callback&& callback, static_assert(is_continuation>{}, "Expected a continuation!"); - // Wrap the callback into a partial callable callback - auto partial_callable = [callback = std::forward(callback)]( - auto&&... args) mutable { - return util::partial_invoke(std::move(callback), - std::forward(args)...); - }; - auto hint = hint_of(traits::identity_of(continuation)); - auto next_hint = next_hint_of(traits::identity_of(partial_callable), hint); + auto next_hint = next_hint_of(traits::identity_of(callback), hint); // TODO consume only the data here so the freeze isn't needed auto ownership_ = attorney::ownership_of(continuation); @@ -451,7 +450,7 @@ auto chain_continuation(Continuation&& continuation, Callback&& callback, return attorney::create( [ continuation = std::forward(continuation), - partial_callable = std::move(partial_callable), + callback = std::forward(callback), executor = std::forward(executor) ](auto&& next_callback) mutable { @@ -464,12 +463,12 @@ auto chain_continuation(Continuation&& continuation, Callback&& callback, // - Callback: [](std::string) { } // - NextCallback: []() { } using Hint = decltype(hint_of(traits::identity_of(continuation))); - callbacks::result_callback, - std::decay_t, - std::decay_t> - proxy{std::move(partial_callable), std::move(executor), - std::forward(next_callback)}; + + 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)); // Invoke the continuation with a proxy callback. // The proxy callback is responsible for passing @@ -505,12 +504,12 @@ auto chain_error_handler(Continuation&& continuation, Callback&& callback, ](auto&& next_callback) mutable { using Hint = decltype(hint_of(traits::identity_of(continuation))); - callbacks::error_callback, - std::decay_t, - std::decay_t> - proxy{std::move(callback), std::move(executor), - std::forward(next_callback)}; + + 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)); @@ -518,6 +517,11 @@ auto chain_error_handler(Continuation&& continuation, Callback&& callback, hint, ownership_); } +template +auto chain_flow_handler(Continuation&& /*continuation*/, + Callback&& /*callback*/, Executor&& /*executor*/) { +} + /// Final invokes the given continuation chain: /// /// For example given: diff --git a/test/playground/test-playground.cpp b/test/playground/test-playground.cpp index 4ccd194..3471e97 100644 --- a/test/playground/test-playground.cpp +++ b/test/playground/test-playground.cpp @@ -67,7 +67,7 @@ struct my_callable { }; int main(int, char**) { - http_request("github.com").then(my_callable{}); + http_request("github.com").flow(my_callable{}); http_request("github.com") .then([](std::string) {