diff --git a/include/continuable/continuable-promise-base.hpp b/include/continuable/continuable-promise-base.hpp index 8702f81..f48a564 100644 --- a/include/continuable/continuable-promise-base.hpp +++ b/include/continuable/continuable-promise-base.hpp @@ -65,6 +65,11 @@ public: void operator()(Args... args) { data_(std::move(args)...); } + /// Resolves the continuation with the given exception + void operator()(detail::types::dispatch_error_tag tag, + detail::types::error_type exception) { + data_(tag, std::move(exception)); + } /// Resolves the continuation with the given values void set_value(Args... args) { diff --git a/include/continuable/detail/base.hpp b/include/continuable/detail/base.hpp index 0b43e73..3a569e2 100644 --- a/include/continuable/detail/base.hpp +++ b/include/continuable/detail/base.hpp @@ -259,6 +259,39 @@ void packed_dispatch(Executor&& executor, Invoker&& invoker, Args&&... args) { } namespace callbacks { +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) { + std::move(callback)(std::move(error)); + }; + + // Invoke the error handler + packed_dispatch( + std::move(static_cast(this)->executor_), std::move(invoker), + std::move(static_cast(this)->callback_), std::move(error)); + } +}; + +template +struct result_callback_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; @@ -266,11 +299,23 @@ struct result_callback; template struct result_callback, Callback, Executor, - NextCallback> { + NextCallback> + : result_callback_error_base< + result_callback, Callback, + Executor, NextCallback>> { + + using CallbackT = Callback; + Callback callback_; Executor executor_; NextCallback next_callback_; + explicit result_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) { // In order to retrieve the correct decorator we must know what the @@ -287,11 +332,7 @@ struct result_callback, Callback, Executor, std::move(args)...); } - /// 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(next_callback_)(tag, std::move(error)); - } + using result_callback_error_base::operator(); /// Resolves the continuation with the given values void set_value(Args... args) { @@ -311,29 +352,29 @@ struct error_callback; template struct error_callback, Callback, Executor, - NextCallback> { + 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)...); } - /// The operator which is called when an error occurred - void operator()(types::dispatch_error_tag /*tag*/, types::error_type error) { - - // Just invoke the error handler, cancel the calling hierarchy after - auto invoker = [](Callback&& callback, types::error_type&& error) { - std::move(callback)(std::move(error)); - }; - - // Invoke the error handler - packed_dispatch(std::move(executor_), std::move(invoker), - std::move(callback_), std::move(error)); - } + using error_handler_base::operator(); /// Resolves the continuation with the given values void set_value(Args... args) { diff --git a/include/continuable/detail/transforms.hpp b/include/continuable/detail/transforms.hpp index 4cb7bc3..956aa98 100644 --- a/include/continuable/detail/transforms.hpp +++ b/include/continuable/detail/transforms.hpp @@ -34,7 +34,9 @@ #include #include +#include #include +#include namespace cti { namespace detail { @@ -90,6 +92,14 @@ public: void operator()(Args... args) { this->resolve(promise_, std::move(args)...); } +#if !defined(CONTINUABLE_WITH_CUSTOM_ERROR_TYPE) && \ + !defined(CONTINUABLE_WITH_NO_EXCEPTIONS) + /// Resolves the promise through the exception + void operator()(types::dispatch_error_tag, types::error_type error) { + promise_.set_exception(error); + } +#endif + /// Returns the future from the promise auto get_future() { return promise_.get_future(); diff --git a/include/continuable/detail/util.hpp b/include/continuable/detail/util.hpp index 31b117e..dc5a57d 100644 --- a/include/continuable/detail/util.hpp +++ b/include/continuable/detail/util.hpp @@ -73,10 +73,10 @@ struct is_invokable_impl< /// arguments inside lambda closures. /// /// ```cpp -/// traits::is_invokable_t> +/// traits::is_invokable> /// ``` template -using is_invokable_t = typename detail::is_invokable_impl::type; +using is_invokable = typename detail::is_invokable_impl::type; namespace detail { /// Forwards every element in the tuple except the last one @@ -124,7 +124,7 @@ auto partial_invoke_impl(std::false_type, T&& callable, auto next = forward_except_last(std::move(args)); // Test whether we are able to call the function with the given tuple - is_invokable_t is_invokable; + is_invokable is_invokable; return partial_invoke_impl(is_invokable, std::forward(callable), std::move(next)); @@ -157,7 +157,7 @@ auto partial_invoke_impl_shortcut(std::false_type failed, T&& callable, template auto partial_invoke(T&& callable, Args&&... args) { // Test whether we are able to call the function with the given arguments. - is_invokable_t> is_invokable; + is_invokable> is_invokable; // The implementation is done in a shortcut way so there are less // type instantiations needed to call the callable with its full signature. diff --git a/test/playground/test-playground.cpp b/test/playground/test-playground.cpp index a6e361f..4ccd194 100644 --- a/test/playground/test-playground.cpp +++ b/test/playground/test-playground.cpp @@ -57,7 +57,18 @@ static cti::continuable http_request3(std::string url) { }; } +struct my_callable { + void operator()(std::string) { + // ... + } + void operator()(cti::dispatch_error_tag, cti::error_type) { + // ... + } +}; + int main(int, char**) { + http_request("github.com").then(my_callable{}); + http_request("github.com") .then([](std::string) { // ...