Unify the then, fail and flow callback creation

This commit is contained in:
Denis Blank 2017-10-04 01:47:04 +02:00
parent dc205c8e51
commit bf1ac9daee
2 changed files with 110 additions and 114 deletions

View File

@ -196,9 +196,10 @@ public:
template <typename T, typename E = detail::types::this_thread_executor_tag> template <typename T, typename E = detail::types::this_thread_executor_tag>
auto then(T&& callback, auto then(T&& callback,
E&& executor = detail::types::this_thread_executor_tag{}) && { E&& executor = detail::types::this_thread_executor_tag{}) && {
return detail::base::chain_continuation(std::move(*this).materialize(), return detail::base::chain_continuation<detail::base::handle_results::yes,
std::forward<T>(callback), detail::base::handle_errors::no>(
std::forward<E>(executor)); std::move(*this).materialize(), std::forward<T>(callback),
std::forward<E>(executor));
} }
/// Additional overload of the continuable_base::then() method /// Additional overload of the continuable_base::then() method
@ -268,9 +269,10 @@ public:
template <typename T, typename E = detail::types::this_thread_executor_tag> template <typename T, typename E = detail::types::this_thread_executor_tag>
auto fail(T&& callback, auto fail(T&& callback,
E&& executor = detail::types::this_thread_executor_tag{}) && { E&& executor = detail::types::this_thread_executor_tag{}) && {
return detail::base::chain_error_handler(std::move(*this).materialize(), return detail::base::chain_continuation<detail::base::handle_results::no,
std::forward<T>(callback), detail::base::handle_errors::plain>(
std::forward<E>(executor)); std::move(*this).materialize(), std::forward<T>(callback),
std::forward<E>(executor));
} }
/// A method which allows to use an overloaded callable for the error /// A method which allows to use an overloaded callable for the error
@ -303,9 +305,11 @@ public:
template <typename T, typename E = detail::types::this_thread_executor_tag> template <typename T, typename E = detail::types::this_thread_executor_tag>
auto flow(T&& callback, auto flow(T&& callback,
E&& executor = detail::types::this_thread_executor_tag{}) && { E&& executor = detail::types::this_thread_executor_tag{}) && {
return detail::base::chain_flow_handler(std::move(*this).materialize(), return detail::base::chain_continuation<
std::forward<T>(callback), detail::base::handle_results::yes,
std::forward<E>(executor)); detail::base::handle_errors::forward>(std::move(*this).materialize(),
std::forward<T>(callback),
std::forward<E>(executor));
} }
/// Ignores all error which ocured until the point the function was called /// Ignores all error which ocured until the point the function was called

View File

@ -261,12 +261,34 @@ void packed_dispatch(Executor&& executor, Invoker&& invoker, Args&&... args) {
std::forward<Executor>(executor)(std::move(work)); std::forward<Executor>(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 callbacks {
namespace proto { namespace proto {
template <typename Base, typename Hint> template <handle_results HandleResults, typename Base, typename Hint>
struct accept_result_base; struct result_handler_base;
template <typename Base, typename... Args> template <typename Base, typename... Args>
struct accept_result_base<Base, hints::signature_hint_tag<Args...>> { struct result_handler_base<handle_results::no, Base,
hints::signature_hint_tag<Args...>> {
void operator()(Args... args) {
// Forward the arguments to the next callback
std::move(static_cast<Base*>(this)->next_callback_)(std::move(args)...);
}
};
template <typename Base, typename... Args>
struct result_handler_base<handle_results::yes, Base,
hints::signature_hint_tag<Args...>> {
/// The operator which is called when the result was provided /// The operator which is called when the result was provided
void operator()(Args... args) { void operator()(Args... args) {
// In order to retrieve the correct decorator we must know what the // In order to retrieve the correct decorator we must know what the
@ -286,25 +308,27 @@ struct accept_result_base<Base, hints::signature_hint_tag<Args...>> {
} }
}; };
template <typename Base, typename Hint> inline auto make_error_invoker(
struct reject_result_base; std::integral_constant<handle_errors, handle_errors::plain>) noexcept {
template <typename Base, typename... Args> return [](auto&& callback, types::error_type&& error) {
struct reject_result_base<Base, hints::signature_hint_tag<Args...>> { // Errors are not partial invoked
void operator()(Args... args) { std::move(callback)(std::move(error));
// Forward the arguments to the next callback };
std::move(static_cast<Base*>(this)->next_callback_)(std::move(args)...); }
} inline auto make_error_invoker(
}; std::integral_constant<handle_errors, handle_errors::forward>) noexcept {
return [](auto&& callback, types::error_type&& error) {
// Errors are not partial invoked
std::move(callback)(types::dispatch_error_tag{}, std::move(error));
};
}
template <typename Base> template <handle_errors HandleErrors /* = plain or forward*/, typename Base>
struct accept_error_base { struct error_handler_base {
void operator()(types::dispatch_error_tag, types::error_type error) { void operator()(types::dispatch_error_tag, types::error_type error) {
// Just invoke the error handler, cancel the calling hierarchy after // Just invoke the error handler, cancel the calling hierarchy after
auto invoker = [](typename Base::CallbackT&& callback, auto invoker = make_error_invoker(
types::error_type&& error) { std::integral_constant<handle_errors, HandleErrors>{});
// Errors are not partial invoked
std::move(callback)(std::move(error));
};
// Invoke the error handler // Invoke the error handler
packed_dispatch( packed_dispatch(
@ -312,35 +336,35 @@ struct accept_error_base {
std::move(static_cast<Base*>(this)->callback_), std::move(error)); std::move(static_cast<Base*>(this)->callback_), std::move(error));
} }
}; };
template <typename Base> template <typename Base>
struct reject_error_base { struct error_handler_base<handle_errors::no, Base> {
/// The operator which is called when an error occurred /// The operator which is called when an error occurred
void operator()(types::dispatch_error_tag tag, types::error_type error) { void operator()(types::dispatch_error_tag tag, types::error_type error) {
// Forward the error to the next callback // Forward the error to the next callback
std::move(static_cast<Base*>(this)->next_callback_)(tag, std::move(error)); std::move(static_cast<Base*>(this)->next_callback_)(tag, std::move(error));
} }
}; };
} // namespace proto
template <typename Hint, typename Callback, typename Executor, template <typename Hint, handle_results HandleResults,
typename NextCallback, handle_errors HandleErrors, typename Callback, typename Executor,
template <typename, typename> class ResultCallbackBase, typename NextCallback>
template <typename> class ErrorCallbackBase>
struct callback_base; struct callback_base;
template <typename... Args, typename Callback, typename Executor, template <typename... Args, handle_results HandleResults,
typename NextCallback, handle_errors HandleErrors, typename Callback, typename Executor,
template <typename, typename> class ResultCallbackBase, typename NextCallback>
template <typename> class ErrorCallbackBase> struct callback_base<hints::signature_hint_tag<Args...>, HandleResults,
struct callback_base<hints::signature_hint_tag<Args...>, Callback, Executor, HandleErrors, Callback, Executor, NextCallback>
NextCallback, ResultCallbackBase, ErrorCallbackBase> : proto::result_handler_base<
: ResultCallbackBase< HandleResults,
callback_base<hints::signature_hint_tag<Args...>, Callback, Executor, callback_base<hints::signature_hint_tag<Args...>, HandleResults,
NextCallback, ResultCallbackBase, ErrorCallbackBase>, HandleErrors, Callback, Executor, NextCallback>,
hints::signature_hint_tag<Args...>>, hints::signature_hint_tag<Args...>>,
ErrorCallbackBase< proto::error_handler_base<
callback_base<hints::signature_hint_tag<Args...>, Callback, Executor, HandleErrors,
NextCallback, ResultCallbackBase, ErrorCallbackBase>> { callback_base<hints::signature_hint_tag<Args...>, HandleResults,
HandleErrors, Callback, Executor, NextCallback>> {
using CallbackT = Callback; using CallbackT = Callback;
@ -355,15 +379,17 @@ struct callback_base<hints::signature_hint_tag<Args...>, Callback, Executor,
} }
/// Pull the result handling operator() in /// Pull the result handling operator() in
using ResultCallbackBase< using proto::result_handler_base<
callback_base<hints::signature_hint_tag<Args...>, Callback, Executor, HandleResults,
NextCallback, ResultCallbackBase, ErrorCallbackBase>, callback_base<hints::signature_hint_tag<Args...>, HandleResults,
HandleErrors, Callback, Executor, NextCallback>,
hints::signature_hint_tag<Args...>>::operator(); hints::signature_hint_tag<Args...>>::operator();
/// Pull the error handling operator() in /// Pull the error handling operator() in
using ErrorCallbackBase< using proto::error_handler_base<
callback_base<hints::signature_hint_tag<Args...>, Callback, Executor, HandleErrors,
NextCallback, ResultCallbackBase, ErrorCallbackBase>>:: callback_base<hints::signature_hint_tag<Args...>, HandleResults,
HandleErrors, Callback, Executor, NextCallback>>::
operator(); operator();
/// Resolves the continuation with the given values /// Resolves the continuation with the given values
@ -377,18 +403,17 @@ struct callback_base<hints::signature_hint_tag<Args...>, Callback, Executor,
} }
}; };
template <typename Hint, template <typename, typename> class ResultCallbackBase, template <typename Hint, handle_results HandleResults,
template <typename> class ErrorCallbackBase, typename Callback, handle_errors HandleErrors, typename Callback, typename Executor,
typename Executor, typename NextCallback> typename NextCallback>
auto make_callback(Callback&& callback, Executor&& executor, auto make_callback(Callback&& callback, Executor&& executor,
NextCallback&& next_callback) { NextCallback&& next_callback) {
return callback_base<Hint, std::decay_t<Callback>, std::decay_t<Executor>, return callback_base<Hint, HandleResults, HandleErrors,
std::decay_t<NextCallback>, ResultCallbackBase, std::decay_t<Callback>, std::decay_t<Executor>,
ErrorCallbackBase>{ std::decay_t<NextCallback>>{
std::forward<Callback>(callback), std::forward<Executor>(executor), std::forward<Callback>(callback), std::forward<Executor>(executor),
std::forward<NextCallback>(next_callback)}; std::forward<NextCallback>(next_callback)};
} }
} // namespace proto
/// Once this was a workaround for GCC bug: /// Once this was a workaround for GCC bug:
/// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64095 /// 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 /// Returns the next hint when the callback is invoked with the given hint
template <typename T, typename... Args> template <typename T, typename... Args>
constexpr auto next_hint_of(traits::identity<T> /*callback*/, constexpr auto
hints::signature_hint_tag<Args...> /*current*/) { next_hint_of(std::integral_constant<handle_results, handle_results::yes>,
traits::identity<T> /*callback*/,
hints::signature_hint_tag<Args...> /*current*/) {
// Partial Invoke the given callback // Partial Invoke the given callback
using Result = decltype( using Result = decltype(
util::partial_invoke(std::declval<T>(), std::declval<Args>()...)); util::partial_invoke(std::declval<T>(), std::declval<Args>()...));
@ -424,6 +451,14 @@ constexpr auto next_hint_of(traits::identity<T> /*callback*/,
// Return the hint of thr given invoker // Return the hint of thr given invoker
return decoration::invoker_of(traits::identity_of<Result>()).hint(); return decoration::invoker_of(traits::identity_of<Result>()).hint();
} }
/// Don't progress the hint when we don't continue
template <typename T, typename... Args>
constexpr auto
next_hint_of(std::integral_constant<handle_results, handle_results::no>,
traits::identity<T> /*callback*/,
hints::signature_hint_tag<Args...> current) {
return current;
}
/// Chains a callback together with a continuation and returns a continuation: /// Chains a callback together with a continuation and returns a continuation:
/// ///
@ -434,14 +469,17 @@ constexpr auto next_hint_of(traits::identity<T> /*callback*/,
/// This function returns a function accepting the next callback in the chain: /// This function returns a function accepting the next callback in the chain:
/// - Result: continuation<[](auto&& callback) { /*...*/ }> /// - Result: continuation<[](auto&& callback) { /*...*/ }>
/// ///
template <typename Continuation, typename Callback, typename Executor> template <handle_results HandleResults, handle_errors HandleErrors,
typename Continuation, typename Callback, typename Executor>
auto chain_continuation(Continuation&& continuation, Callback&& callback, auto chain_continuation(Continuation&& continuation, Callback&& callback,
Executor&& executor) { Executor&& executor) {
static_assert(is_continuation<std::decay_t<Continuation>>{}, static_assert(is_continuation<std::decay_t<Continuation>>{},
"Expected a continuation!"); "Expected a continuation!");
auto hint = hint_of(traits::identity_of(continuation)); using Hint = decltype(hint_of(traits::identity_of(continuation)));
auto next_hint = next_hint_of(traits::identity_of(callback), hint); auto next_hint =
next_hint_of(std::integral_constant<handle_results, HandleResults>{},
traits::identity_of(callback), Hint{});
// TODO consume only the data here so the freeze isn't needed // TODO consume only the data here so the freeze isn't needed
auto ownership_ = attorney::ownership_of(continuation); auto ownership_ = attorney::ownership_of(continuation);
@ -462,13 +500,10 @@ auto chain_continuation(Continuation&& continuation, Callback&& callback,
// - Continuation: continuation<[](auto&& callback) { callback("hi"); }> // - Continuation: continuation<[](auto&& callback) { callback("hi"); }>
// - Callback: [](std::string) { } // - Callback: [](std::string) { }
// - NextCallback: []() { } // - NextCallback: []() { }
using Hint = decltype(hint_of(traits::identity_of(continuation))); auto proxy =
callbacks::make_callback<Hint, HandleResults, HandleErrors>(
auto proxy = callbacks::proto::make_callback< std::move(callback), std::move(executor),
Hint, callbacks::proto::accept_result_base, std::forward<decltype(next_callback)>(next_callback));
callbacks::proto::reject_error_base>(
std::move(callback), std::move(executor),
std::forward<decltype(next_callback)>(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
@ -479,49 +514,6 @@ auto chain_continuation(Continuation&& continuation, Callback&& callback,
next_hint, ownership_); 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 <typename Continuation, typename Callback, typename Executor>
auto chain_error_handler(Continuation&& continuation, Callback&& callback,
Executor&& executor) {
static_assert(is_continuation<std::decay_t<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>(continuation),
callback = std::forward<Callback>(callback),
executor = std::forward<Executor>(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<decltype(next_callback)>(next_callback));
attorney::invoke_continuation(std::move(continuation),
std::move(proxy));
},
hint, ownership_);
}
template <typename Continuation, typename Callback, typename Executor>
auto chain_flow_handler(Continuation&& /*continuation*/,
Callback&& /*callback*/, Executor&& /*executor*/) {
}
/// Final invokes the given continuation chain: /// Final invokes the given continuation chain:
/// ///
/// For example given: /// For example given: