More work on supporting multiple flow paths

This commit is contained in:
Denis Blank 2017-09-27 06:47:45 +02:00
parent 0aa42d5b1a
commit ff91ff7fc3
2 changed files with 87 additions and 54 deletions

View File

@ -67,19 +67,19 @@ public:
/// Resolves the continuation with the given values /// Resolves the continuation with the given values
template <typename... Args> template <typename... Args>
void set_value(Args&&... args) { void set_value(Args&&... args) {
data_(detail::base::dispatch_result{}, std::forward<Args>(args)...); data_(detail::base::dispatch_result_tag{}, std::forward<Args>(args)...);
} }
/// Resolves the continuation with the given values /// Resolves the continuation with the given values
template <typename... Args> template <typename... Args>
void operator()(Args&&... args) { void operator()(Args&&... args) {
data_(detail::base::dispatch_result{}, std::forward<Args>(args)...); data_(detail::base::dispatch_result_tag{}, std::forward<Args>(args)...);
} }
/// Resolves the continuation with the given error variable. /// Resolves the continuation with the given error variable.
template <typename Arg> template <typename Arg>
void set_error(Arg&& arg) { void set_error(Arg&& arg) {
data_(detail::base::dispatch_error{}, std::forward<Arg>(arg)); data_(detail::base::dispatch_error_tag{}, std::forward<Arg>(arg));
} }
}; };
} // end namespace cti } // end namespace cti

View File

@ -35,6 +35,12 @@
#include <type_traits> #include <type_traits>
#include <utility> #include <utility>
#ifndef CONTINUABLE_WITH_NO_EXCEPTIONS
#include <exception>
#else // CONTINUABLE_WITH_NO_EXCEPTIONS
#include <error>
#endif // CONTINUABLE_WITH_NO_EXCEPTIONS
#include <continuable/detail/api.hpp> #include <continuable/detail/api.hpp>
#include <continuable/detail/hints.hpp> #include <continuable/detail/hints.hpp>
#include <continuable/detail/traits.hpp> #include <continuable/detail/traits.hpp>
@ -60,9 +66,17 @@ namespace base {
/// A tag which is used to execute the continuation inside the current thread /// A tag which is used to execute the continuation inside the current thread
struct this_thread_executor_tag {}; struct this_thread_executor_tag {};
/// A tag which is used to continue with a real result /// 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 /// 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 /// Returns the signature hint of the given continuable
template <typename T> template <typename T>
@ -125,23 +139,23 @@ struct attorney {
// and the arguments of the previous continuation. // and the arguments of the previous continuation.
// //
// The return type of the invokerOf function matches a functional of: // 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 // The invoker decorates the result type in the following way
// - void -> nextCallback() // - void -> next_callback()
// - ? -> nextCallback(?) // - ? -> next_callback(?)
// - std::pair<?, ?> -> nextCallback(?, ?) // - std::pair<?, ?> -> next_callback(?, ?)
// - std::tuple<?...> -> nextCallback(?...) // - std::tuple<?...> -> next_callback(?...)
// //
// When the result is a continuation itself pass the callback to it // When the result is a continuation itself pass the callback to it
// - continuation<?...> -> result(nextCallback); // - continuation<?...> -> result(next_callback);
namespace decoration { namespace decoration {
/// Helper class wrapping the underlaying unwrapping lambda /// Helper class wrapping the underlaying unwrapping lambda
/// in order to extend it with a hint method. /// in order to extend it with a hint method.
template <typename T, typename Hint> template <typename T, typename Hint>
class invoker : public T { class invoker : public T {
public: public:
explicit invoker(T invoke) : T(std::move(invoke)) { constexpr explicit invoker(T invoke) : T(std::move(invoke)) {
} }
using T::operator(); using T::operator();
@ -158,7 +172,7 @@ constexpr auto make_invoker(T&& invoke, hints::signature_hint_tag<Args...>) {
std::forward<T>(invoke)); std::forward<T>(invoke));
} }
/// - continuable<?...> -> result(nextCallback); /// - continuable<?...> -> result(next_callback);
template <typename Data, typename Annotation> template <typename Data, typename Annotation>
constexpr auto constexpr auto
invoker_of(traits::identity<continuable_base<Data, Annotation>>) { invoker_of(traits::identity<continuable_base<Data, Annotation>>) {
@ -167,66 +181,68 @@ invoker_of(traits::identity<continuable_base<Data, Annotation>>) {
std::declval<continuable_base<Data, Annotation>>())); std::declval<continuable_base<Data, Annotation>>()));
return make_invoker( return make_invoker(
[](auto&& callback, auto&& nextCallback, auto&&... args) { [](auto&& callback, auto&& next_callback, auto&&... args) {
auto continuation_ = std::forward<decltype(callback)>(callback)( auto continuation_ = std::forward<decltype(callback)>(callback)(
std::forward<decltype(args)>(args)...); std::forward<decltype(args)>(args)...);
attorney::invoke_continuation( attorney::invoke_continuation(
std::move(continuation_), std::move(continuation_),
std::forward<decltype(nextCallback)>(nextCallback)); std::forward<decltype(next_callback)>(next_callback));
}, },
hint_of(traits::identity_of<Type>())); hint_of(traits::identity_of<Type>()));
} }
/// - ? -> nextCallback(?) /// - ? -> next_callback(?)
template <typename T> template <typename T>
auto invoker_of(traits::identity<T>) { constexpr auto invoker_of(traits::identity<T>) {
return make_invoker( return make_invoker(
[](auto&& callback, auto&& nextCallback, auto&&... args) { [](auto&& callback, auto&& next_callback, auto&&... args) {
auto result = std::forward<decltype(callback)>(callback)( auto result = std::forward<decltype(callback)>(callback)(
std::forward<decltype(args)>(args)...); std::forward<decltype(args)>(args)...);
std::forward<decltype(nextCallback)>(nextCallback)(std::move(result)); std::forward<decltype(next_callback)>(next_callback)(
dispatch_result_tag{}, std::move(result));
}, },
traits::identity_of<T>()); traits::identity_of<T>());
} }
/// - void -> nextCallback() /// - void -> next_callback()
inline auto invoker_of(traits::identity<void>) { constexpr auto invoker_of(traits::identity<void>) {
return make_invoker( return make_invoker(
[](auto&& callback, auto&& nextCallback, auto&&... args) { [](auto&& callback, auto&& next_callback, auto&&... args) {
std::forward<decltype(callback)>(callback)( std::forward<decltype(callback)>(callback)(
std::forward<decltype(args)>(args)...); std::forward<decltype(args)>(args)...);
std::forward<decltype(nextCallback)>(nextCallback)(); std::forward<decltype(next_callback)>(next_callback)(
dispatch_result_tag{});
}, },
traits::identity<>{}); traits::identity<>{});
} }
/// Returns a sequenced invoker which is able to invoke /// Returns a sequenced invoker which is able to invoke
/// objects where std::get is applicable. /// objects where std::get is applicable.
inline auto sequenced_unpack_invoker() { constexpr auto sequenced_unpack_invoker() {
return [](auto&& callback, auto&& nextCallback, auto&&... args) { return [](auto&& callback, auto&& next_callback, auto&&... args) {
auto result = std::forward<decltype(callback)>(callback)( auto result = std::forward<decltype(callback)>(callback)(
std::forward<decltype(args)>(args)...); std::forward<decltype(args)>(args)...);
traits::unpack(std::move(result), [&](auto&&... types) { traits::unpack(std::move(result), [&](auto&&... types) {
/// TODO Add inplace resolution here /// TODO Add inplace resolution here
std::forward<decltype(nextCallback)>(nextCallback)( std::forward<decltype(next_callback)>(next_callback)(
std::forward<decltype(types)>(types)...); dispatch_result_tag{}, std::forward<decltype(types)>(types)...);
}); });
}; };
} }
// - std::pair<?, ?> -> nextCallback(?, ?) // - std::pair<?, ?> -> next_callback(?, ?)
template <typename First, typename Second> template <typename First, typename Second>
constexpr auto invoker_of(traits::identity<std::pair<First, Second>>) { constexpr auto invoker_of(traits::identity<std::pair<First, Second>>) {
return make_invoker(sequenced_unpack_invoker(), return make_invoker(sequenced_unpack_invoker(),
traits::identity<First, Second>{}); traits::identity<First, Second>{});
} }
// - std::tuple<?...> -> nextCallback(?...) // - std::tuple<?...> -> next_callback(?...)
template <typename... Args> template <typename... Args>
constexpr auto invoker_of(traits::identity<std::tuple<Args...>>) { constexpr auto invoker_of(traits::identity<std::tuple<Args...>>) {
return make_invoker(sequenced_unpack_invoker(), traits::identity<Args...>{}); return make_invoker(sequenced_unpack_invoker(), traits::identity<Args...>{});
@ -237,12 +253,12 @@ constexpr auto invoker_of(traits::identity<std::tuple<Args...>>) {
template <typename Invoker, typename Callback, typename NextCallback, template <typename Invoker, typename Callback, typename NextCallback,
typename... Args> typename... Args>
void packed_dispatch(this_thread_executor_tag, Invoker&& invoker, void packed_dispatch(this_thread_executor_tag, Invoker&& invoker,
Callback&& callback, NextCallback&& nextCallback, Callback&& callback, NextCallback&& next_callback,
Args&&... args) { Args&&... args) {
// Invoke the callback with the decorated invoker immediately // Invoke the callback with the decorated invoker immediately
std::forward<Invoker>(invoker)(std::forward<Callback>(callback), std::forward<Invoker>(invoker)(std::forward<Callback>(callback),
std::forward<NextCallback>(nextCallback), std::forward<NextCallback>(next_callback),
std::forward<Args>(args)...); std::forward<Args>(args)...);
} }
@ -250,7 +266,7 @@ void packed_dispatch(this_thread_executor_tag, Invoker&& invoker,
template <typename Executor, typename Invoker, typename Callback, template <typename Executor, typename Invoker, typename Callback,
typename NextCallback, typename... Args> typename NextCallback, typename... Args>
void packed_dispatch(Executor&& executor, Invoker&& invoker, void packed_dispatch(Executor&& executor, Invoker&& invoker,
Callback&& callback, NextCallback&& nextCallback, Callback&& callback, NextCallback&& next_callback,
Args&&... args) { Args&&... args) {
// Create a worker object which when invoked calls the callback with the // 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 = [ auto work = [
invoker = std::forward<Invoker>(invoker), invoker = std::forward<Invoker>(invoker),
callback = std::forward<Callback>(callback), callback = std::forward<Callback>(callback),
nextCallback = std::forward<NextCallback>(nextCallback), next_callback = std::forward<NextCallback>(next_callback),
args = std::make_tuple(std::forward<Args>(args)...) args = std::make_tuple(std::forward<Args>(args)...)
]() mutable { ]() mutable {
traits::unpack(std::move(args), [&](auto&&... captured_args) { traits::unpack(std::move(args), [&](auto&&... captured_args) {
// Just use the packed dispatch method which dispatches the work on // Just use the packed dispatch method which dispatches the work on
// the current thread. // the current thread.
packed_dispatch(this_thread_executor_tag{}, std::move(invoker), packed_dispatch(this_thread_executor_tag{}, std::move(invoker),
std::move(callback), std::move(nextCallback), std::move(callback), std::move(next_callback),
std::forward<decltype(captured_args)>(captured_args)...); std::forward<decltype(captured_args)>(captured_args)...);
}); });
}; };
@ -274,6 +290,35 @@ void packed_dispatch(Executor&& executor, Invoker&& invoker,
std::forward<Executor>(executor)(std::move(work)); std::forward<Executor>(executor)(std::move(work));
} }
template <typename Callback, typename Executor, typename NextCallback,
typename... Args>
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<decltype(
std::move(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(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. /// Invokes a continuation with a given callback.
/// Passes the next callback to the resulting continuable or /// Passes the next callback to the resulting continuable or
/// invokes the next callback directly if possible. /// invokes the next callback directly if possible.
@ -287,30 +332,18 @@ template <typename... Args, typename Continuation, typename Callback,
typename Executor, typename NextCallback> typename Executor, typename NextCallback>
void invoke_proxy(hints::signature_hint_tag<Args...>, void invoke_proxy(hints::signature_hint_tag<Args...>,
Continuation&& continuation, Callback&& callback, Continuation&& continuation, Callback&& callback,
Executor&& executor, NextCallback&& nextCallback) { Executor&& executor, NextCallback&& next_callback) {
result_proxy<std::decay_t<Callback>, std::decay_t<Executor>,
std::decay_t<NextCallback>, Args...>
proxy{std::forward<Callback>(callback), std::forward<Executor>(executor),
std::forward<NextCallback>(next_callback)};
// Invoke the continuation with a proxy callback. // Invoke the continuation with a proxy callback.
// The proxy callback is responsible for passing // The proxy callback is responsible for passing
// the result to the callback as well as decorating it. // the result to the callback as well as decorating it.
attorney::invoke_continuation(std::forward<Continuation>(continuation), [ attorney::invoke_continuation(std::forward<Continuation>(continuation),
callback = std::forward<Callback>(callback), std::move(proxy));
executor = std::forward<Executor>(executor),
nextCallback = std::forward<NextCallback>(nextCallback)
](Args... args) mutable {
// In order to retrieve the correct decorator we must know what the
// result type is.
auto result = traits::identity_of<decltype(
std::move(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(executor), std::move(invoker),
std::move(callback), std::move(nextCallback),
std::move(args)...);
});
} }
/// Returns the next hint when the callback is invoked with the given hint /// Returns the next hint when the callback is invoked with the given hint