diff --git a/include/continuable/continuable-promise-base.hpp b/include/continuable/continuable-promise-base.hpp index 4ebe29f..d70e7f9 100644 --- a/include/continuable/continuable-promise-base.hpp +++ b/include/continuable/continuable-promise-base.hpp @@ -67,19 +67,19 @@ public: /// Resolves the continuation with the given values template void set_value(Args&&... args) { - data_(detail::base::dispatch_result{}, std::forward(args)...); + data_(detail::base::dispatch_result_tag{}, std::forward(args)...); } /// Resolves the continuation with the given values template void operator()(Args&&... args) { - data_(detail::base::dispatch_result{}, std::forward(args)...); + data_(detail::base::dispatch_result_tag{}, std::forward(args)...); } /// Resolves the continuation with the given error variable. template void set_error(Arg&& arg) { - data_(detail::base::dispatch_error{}, std::forward(arg)); + data_(detail::base::dispatch_error_tag{}, std::forward(arg)); } }; } // end namespace cti diff --git a/include/continuable/detail/base.hpp b/include/continuable/detail/base.hpp index 105a4fb..c4039d2 100644 --- a/include/continuable/detail/base.hpp +++ b/include/continuable/detail/base.hpp @@ -35,6 +35,12 @@ #include #include +#ifndef CONTINUABLE_WITH_NO_EXCEPTIONS +#include +#else // CONTINUABLE_WITH_NO_EXCEPTIONS +#include +#endif // CONTINUABLE_WITH_NO_EXCEPTIONS + #include #include #include @@ -60,9 +66,17 @@ namespace base { /// A tag which is used to execute the continuation inside the current thread struct this_thread_executor_tag {}; /// A tag which is used to continue with a real result -struct dispatch_result {}; +struct dispatch_result_tag {}; /// A tag which is used to continue with an error -struct dispatch_error {}; +struct dispatch_error_tag {}; + +#ifndef CONTINUABLE_WITH_NO_EXCEPTIONS +/// Represents the error type when exceptions are enabled +using error_type = std::exception_ptr; +#else // CONTINUABLE_WITH_NO_EXCEPTIONS +/// Represents the error type when exceptions are disabled +using error_type = std::error_category; +#endif // CONTINUABLE_WITH_NO_EXCEPTIONS /// Returns the signature hint of the given continuable template @@ -125,23 +139,23 @@ struct attorney { // and the arguments of the previous continuation. // // The return type of the invokerOf function matches a functional of: -// void(auto&& callback, auto&& nextCallback, auto&&... args) +// void(auto&& callback, auto&& next_callback, auto&&... args) // // The invoker decorates the result type in the following way -// - void -> nextCallback() -// - ? -> nextCallback(?) -// - std::pair -> nextCallback(?, ?) -// - std::tuple -> nextCallback(?...) +// - void -> next_callback() +// - ? -> next_callback(?) +// - std::pair -> next_callback(?, ?) +// - std::tuple -> next_callback(?...) // // When the result is a continuation itself pass the callback to it -// - continuation -> result(nextCallback); +// - continuation -> result(next_callback); namespace decoration { /// Helper class wrapping the underlaying unwrapping lambda /// in order to extend it with a hint method. template class invoker : public T { public: - explicit invoker(T invoke) : T(std::move(invoke)) { + constexpr explicit invoker(T invoke) : T(std::move(invoke)) { } using T::operator(); @@ -158,7 +172,7 @@ constexpr auto make_invoker(T&& invoke, hints::signature_hint_tag) { std::forward(invoke)); } -/// - continuable -> result(nextCallback); +/// - continuable -> result(next_callback); template constexpr auto invoker_of(traits::identity>) { @@ -167,66 +181,68 @@ invoker_of(traits::identity>) { std::declval>())); return make_invoker( - [](auto&& callback, auto&& nextCallback, auto&&... args) { + [](auto&& callback, auto&& next_callback, auto&&... args) { auto continuation_ = std::forward(callback)( std::forward(args)...); attorney::invoke_continuation( std::move(continuation_), - std::forward(nextCallback)); + std::forward(next_callback)); }, hint_of(traits::identity_of())); } -/// - ? -> nextCallback(?) +/// - ? -> next_callback(?) template -auto invoker_of(traits::identity) { +constexpr auto invoker_of(traits::identity) { return make_invoker( - [](auto&& callback, auto&& nextCallback, auto&&... args) { + [](auto&& callback, auto&& next_callback, auto&&... args) { auto result = std::forward(callback)( std::forward(args)...); - std::forward(nextCallback)(std::move(result)); + std::forward(next_callback)( + dispatch_result_tag{}, std::move(result)); }, traits::identity_of()); } -/// - void -> nextCallback() -inline auto invoker_of(traits::identity) { +/// - void -> next_callback() +constexpr auto invoker_of(traits::identity) { return make_invoker( - [](auto&& callback, auto&& nextCallback, auto&&... args) { + [](auto&& callback, auto&& next_callback, auto&&... args) { std::forward(callback)( std::forward(args)...); - std::forward(nextCallback)(); + std::forward(next_callback)( + dispatch_result_tag{}); }, traits::identity<>{}); } /// Returns a sequenced invoker which is able to invoke /// objects where std::get is applicable. -inline auto sequenced_unpack_invoker() { - return [](auto&& callback, auto&& nextCallback, auto&&... args) { +constexpr auto sequenced_unpack_invoker() { + return [](auto&& callback, auto&& next_callback, auto&&... args) { auto result = std::forward(callback)( std::forward(args)...); traits::unpack(std::move(result), [&](auto&&... types) { /// TODO Add inplace resolution here - std::forward(nextCallback)( - std::forward(types)...); + std::forward(next_callback)( + dispatch_result_tag{}, std::forward(types)...); }); }; } -// - std::pair -> nextCallback(?, ?) +// - std::pair -> next_callback(?, ?) template constexpr auto invoker_of(traits::identity>) { return make_invoker(sequenced_unpack_invoker(), traits::identity{}); } -// - std::tuple -> nextCallback(?...) +// - std::tuple -> next_callback(?...) template constexpr auto invoker_of(traits::identity>) { return make_invoker(sequenced_unpack_invoker(), traits::identity{}); @@ -237,12 +253,12 @@ constexpr auto invoker_of(traits::identity>) { template void packed_dispatch(this_thread_executor_tag, Invoker&& invoker, - Callback&& callback, NextCallback&& nextCallback, + Callback&& callback, NextCallback&& next_callback, Args&&... args) { // Invoke the callback with the decorated invoker immediately std::forward(invoker)(std::forward(callback), - std::forward(nextCallback), + std::forward(next_callback), std::forward(args)...); } @@ -250,7 +266,7 @@ void packed_dispatch(this_thread_executor_tag, Invoker&& invoker, template void packed_dispatch(Executor&& executor, Invoker&& invoker, - Callback&& callback, NextCallback&& nextCallback, + Callback&& callback, NextCallback&& next_callback, Args&&... args) { // Create a worker object which when invoked calls the callback with the @@ -258,14 +274,14 @@ void packed_dispatch(Executor&& executor, Invoker&& invoker, auto work = [ invoker = std::forward(invoker), callback = std::forward(callback), - nextCallback = std::forward(nextCallback), + next_callback = std::forward(next_callback), args = std::make_tuple(std::forward(args)...) ]() mutable { traits::unpack(std::move(args), [&](auto&&... captured_args) { // Just use the packed dispatch method which dispatches the work on // the current thread. packed_dispatch(this_thread_executor_tag{}, std::move(invoker), - std::move(callback), std::move(nextCallback), + std::move(callback), std::move(next_callback), std::forward(captured_args)...); }); }; @@ -274,6 +290,35 @@ void packed_dispatch(Executor&& executor, Invoker&& invoker, std::forward(executor)(std::move(work)); } +template +struct result_proxy { + Callback callback_; + Executor executor_; + NextCallback next_callback_; + + /// The operator which is called when the result was provided + void operator()(dispatch_result_tag, Args... args) { + // In order to retrieve the correct decorator we must know what the + // result type is. + auto result = traits::identity_of(); + + // 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)...); + } + + /// The operator which is called when an error occurred + void operator()(dispatch_error_tag tag, error_type error) { + // TODO forward the error + } +}; + /// Invokes a continuation with a given callback. /// Passes the next callback to the resulting continuable or /// invokes the next callback directly if possible. @@ -287,30 +332,18 @@ template void invoke_proxy(hints::signature_hint_tag, Continuation&& continuation, Callback&& callback, - Executor&& executor, NextCallback&& nextCallback) { + Executor&& executor, NextCallback&& next_callback) { + + result_proxy, std::decay_t, + std::decay_t, Args...> + proxy{std::forward(callback), std::forward(executor), + std::forward(next_callback)}; // Invoke the continuation with a proxy callback. // The proxy callback is responsible for passing // the result to the callback as well as decorating it. - attorney::invoke_continuation(std::forward(continuation), [ - callback = std::forward(callback), - executor = std::forward(executor), - nextCallback = std::forward(nextCallback) - ](Args... args) mutable { - - // In order to retrieve the correct decorator we must know what the - // result type is. - auto result = traits::identity_of(); - - // 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(nextCallback), - std::move(args)...); - }); + attorney::invoke_continuation(std::forward(continuation), + std::move(proxy)); } /// Returns the next hint when the callback is invoked with the given hint