Fix the immediate return type of chained continuables

* Statically resolve ready continuables
This commit is contained in:
Denis Blank 2018-12-09 16:48:08 +01:00
parent 7273891a4c
commit bdada99096
4 changed files with 48 additions and 26 deletions

View File

@ -119,10 +119,13 @@ public:
/// Constructor accepting any object convertible to the data object,
/// while erasing the annotation
template <typename OData, std::enable_if_t<std::is_convertible<
std::decay_t<OData>, Data>::value>* = nullptr>
template <typename OData,
std::enable_if_t<detail::base::can_accept_continuation<
Data, Annotation, detail::traits::unrefcv_t<OData>>::value>* =
nullptr>
continuable_base(OData&& data) // NOLINT(misc-forwarding-reference-overload)
: data_(std::forward<OData>(data)) {
: data_(detail::base::proxy_continuable<Annotation, OData>(
std::forward<OData>(data))) {
}
/// Constructor taking the data of other continuable_base objects

View File

@ -714,15 +714,17 @@ auto strip_exception_arg(Callable&& callable) {
return proxy{std::forward<Callable>(callable)};
}
template <typename Hint, handle_results HandleResults,
template <typename Hint, typename NextHint, handle_results HandleResults,
handle_errors HandleErrors, typename Continuation, typename Callback,
typename Executor>
struct chained_continuation;
template <typename... Args, handle_results HandleResults,
template <typename... Args, typename... NextArgs, handle_results HandleResults,
handle_errors HandleErrors, typename Continuation, typename Callback,
typename Executor>
struct chained_continuation<traits::identity<Args...>, HandleResults,
struct chained_continuation<traits::identity<Args...>,
traits::identity<NextArgs...>, HandleResults,
HandleErrors, Continuation, Callback, Executor> {
Continuation continuation_;
Callback callback_;
Executor executor_;
@ -755,9 +757,9 @@ struct chained_continuation<traits::identity<Args...>, HandleResults,
std::move(callback_), std::move(executor_),
std::forward<decltype(next_callback)>(next_callback));
// TODO Detect statically whether we have a raw ready continuable here
// Check whether the continuation is ready
bool const is_ready = util::as_const(continuation_)(is_ready_arg_t{});
if (is_ready) {
// Invoke the proxy callback directly with the result to
// avoid a potential type erasure.
@ -774,16 +776,17 @@ struct chained_continuation<traits::identity<Args...>, HandleResults,
return false;
}
std::tuple<Args...> operator()(query_arg_t) {
std::tuple<NextArgs...> operator()(query_arg_t) {
util::unreachable();
}
};
// Specialization to unpack ready continuables directly
template <typename... Args, handle_results HandleResults,
template <typename... Args, typename... NextArgs, handle_results HandleResults,
handle_errors HandleErrors, typename Callback, typename Executor>
struct chained_continuation<traits::identity<Args...>, HandleResults,
HandleErrors, ready_continuation<Args...>, Callback,
Executor> {
struct chained_continuation<
traits::identity<Args...>, traits::identity<NextArgs...>, HandleResults,
HandleErrors, ready_continuation<Args...>, Callback, Executor> {
ready_continuation<Args...> continuation_;
Callback callback_;
Executor executor_;
@ -816,7 +819,7 @@ struct chained_continuation<traits::identity<Args...>, HandleResults,
return false;
}
std::tuple<Args...> operator()(query_arg_t) {
std::tuple<NextArgs...> operator()(query_arg_t) {
util::unreachable();
}
};
@ -848,10 +851,9 @@ auto chain_continuation(Continuation&& continuation, Callback&& callback,
auto data =
attorney::consume(std::forward<Continuation>(continuation).finish());
using continuation_t =
chained_continuation<Hint, HandleResults, HandleErrors, decltype(data),
traits::unrefcv_t<Callback>,
traits::unrefcv_t<Executor>>;
using continuation_t = chained_continuation<
Hint, traits::unrefcv_t<decltype(next_hint)>, HandleResults, HandleErrors,
decltype(data), traits::unrefcv_t<Callback>, traits::unrefcv_t<Executor>>;
return attorney::create_from_raw(
continuation_t(std::move(data), std::forward<Callback>(callback),
@ -869,6 +871,23 @@ void finalize_continuation(Continuation&& continuation) {
callbacks::final_callback{});
}
/// Deduces to a true type if the given callable data can be wrapped
/// with the given hint and converted to the given Data.
template <typename Data, typename Annotation, typename Continuation,
typename = void>
struct can_accept_continuation : std::false_type {};
template <typename Data, typename... Args, typename Continuation>
struct can_accept_continuation<
Data, traits::identity<Args...>, Continuation,
traits::void_t<
std::enable_if_t<traits::is_invocable<
Continuation, callbacks::final_callback>::value>,
std::enable_if_t<std::is_convertible<
proxy_continuable<traits::identity<Args...>, Continuation>,
Data>::value>>> : std::true_type
{};
/// Workaround for GCC bug:
/// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64095
template <typename T>

View File

@ -148,16 +148,16 @@ struct is_invokable_impl<
/// arguments inside lambda closures.
///
/// ```cpp
/// traits::is_invokable<object, std::tuple<Args...>>
/// traits::is_invocable<object, std::tuple<Args...>>
/// ```
template <typename T, typename Args>
using is_invokable_from_tuple =
using is_invocable_from_tuple =
typename detail::is_invokable_impl<T, Args>::type;
// Checks whether the given callable object is invocable with the given
// arguments. This doesn't take member functions into account!
template <typename T, typename... Args>
using is_invocable = is_invokable_from_tuple<T, std::tuple<Args...>>;
using is_invocable = is_invocable_from_tuple<T, std::tuple<Args...>>;
/// Deduces to a std::false_type
template <typename T>

View File

@ -95,10 +95,10 @@ struct invocation_env {
auto next = forward_except_last(std::move(args));
// Test whether we are able to call the function with the given tuple
traits::is_invokable_from_tuple<decltype(callable), decltype(next)>
is_invokable;
traits::is_invocable_from_tuple<decltype(callable), decltype(next)>
is_invocable;
return partial_invoke_impl(is_invokable, std::forward<T>(callable),
return partial_invoke_impl(is_invocable, std::forward<T>(callable),
std::move(next));
}
@ -132,15 +132,15 @@ template <std::size_t KeepArgs, typename T, typename... Args>
partial_invoke(std::integral_constant<std::size_t, KeepArgs>, T&& callable,
Args&&... args) {
// Test whether we are able to call the function with the given arguments.
constexpr traits::is_invokable_from_tuple<decltype(callable),
constexpr traits::is_invocable_from_tuple<decltype(callable),
std::tuple<Args...>>
is_invokable;
is_invocable;
// The implementation is done in a shortcut way so there are less
// type instantiations needed to call the callable with its full signature.
using env = detail::invocation_env<KeepArgs>;
return env::partial_invoke_impl_shortcut(
is_invokable, std::forward<T>(callable), std::forward<Args>(args)...);
is_invocable, std::forward<T>(callable), std::forward<Args>(args)...);
}
/// Invokes the given callable object with the given arguments