mirror of
https://github.com/Naios/continuable.git
synced 2025-12-06 16:56:44 +08:00
Add continuable_base::is_ready and continuable_base::unpack.
* Can be used to specialize the asynchronous control flow
on immediate available asynchronous results mostly returned by:
- make_ready_continuable
- make_exceptional_continuable
* Usable to avoid longer unnecessary synchronous callback nestings.
* cti::query_arg_t was renamed into cti::unpack_arg_t.
* The continuation overload nowreturns result<Args...> rather
than std::tuple<Args...>.
* Add is_ready optimizations to make_exceptional_continuable.
This commit is contained in:
parent
117a716de1
commit
0b1b284e3a
@ -138,7 +138,7 @@ public:
|
|||||||
std::enable_if_t<std::is_convertible<
|
std::enable_if_t<std::is_convertible<
|
||||||
detail::traits::unrefcv_t<OData>, Data>::value>* = nullptr>
|
detail::traits::unrefcv_t<OData>, Data>::value>* = nullptr>
|
||||||
/* implicit */ continuable_base(continuable_base<OData, Annotation>&& other)
|
/* implicit */ continuable_base(continuable_base<OData, Annotation>&& other)
|
||||||
: continuable_base(std::move(other).consume()) {
|
: data_(std::move(other).consume()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Constructor taking the data of other continuable_base objects
|
/// Constructor taking the data of other continuable_base objects
|
||||||
@ -627,6 +627,35 @@ public:
|
|||||||
return annotation_trait::finish(std::move(*this));
|
return annotation_trait::finish(std::move(*this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns true when the continuable can provide its result immediately,
|
||||||
|
/// and its lazy invocation would be side-effect free.
|
||||||
|
///
|
||||||
|
/// \since 4.0.0
|
||||||
|
bool is_ready() const noexcept {
|
||||||
|
return annotation_trait::is_ready(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Invalidates the continuable and returns its immediate invocation result.
|
||||||
|
///
|
||||||
|
/// This method can be used to specialize the asynchronous control flow
|
||||||
|
/// based on whether the continuable ìs_ready at every time,
|
||||||
|
/// which is true for a continuable created through the following functions:
|
||||||
|
/// - make_ready_continuable
|
||||||
|
/// - make_exceptional_continuable
|
||||||
|
///
|
||||||
|
/// \returns A result<Args...> where Args... represent the current
|
||||||
|
/// asynchronous parameters or the currently stored exception.
|
||||||
|
///
|
||||||
|
/// \attention unpack requires that continuable_base::is_ready returned true
|
||||||
|
/// in a previous check, otherwise its behaviour is unspecified.
|
||||||
|
///
|
||||||
|
/// \since 4.0.0
|
||||||
|
auto unpack() && {
|
||||||
|
assert(ownership_.is_acquired());
|
||||||
|
assert(is_ready());
|
||||||
|
return detail::base::attorney::query(std::move(*this).finish());
|
||||||
|
}
|
||||||
|
|
||||||
/// Predicate to check whether the cti::continuable_base is frozen or not.
|
/// Predicate to check whether the cti::continuable_base is frozen or not.
|
||||||
///
|
///
|
||||||
/// \returns Returns true when the continuable_base is frozen.
|
/// \returns Returns true when the continuable_base is frozen.
|
||||||
@ -860,15 +889,16 @@ constexpr auto make_continuable(Continuation&& continuation) {
|
|||||||
/// \attention Usually using this function isn't needed at all since
|
/// \attention Usually using this function isn't needed at all since
|
||||||
/// the continuable library is capable of working with
|
/// the continuable library is capable of working with
|
||||||
/// plain values in most cases.
|
/// plain values in most cases.
|
||||||
/// Try not to use it since it causes unneccessary recursive
|
/// Try not to use it since it causes unnecessary recursive
|
||||||
/// function calls.
|
/// function calls.
|
||||||
///
|
///
|
||||||
/// \since 3.0.0
|
/// \since 3.0.0
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
auto make_ready_continuable(Args&&... args) {
|
auto make_ready_continuable(Args&&... args) {
|
||||||
return detail::base::attorney::create_from_raw(
|
return detail::base::attorney::create_from_raw(
|
||||||
detail::base::ready_continuation<detail::traits::unrefcv_t<Args>...>{
|
detail::base::ready_continuation<detail::traits::unrefcv_t<Args>...>(
|
||||||
std::forward<Args>(args)...},
|
result<detail::traits::unrefcv_t<Args>...>::from(
|
||||||
|
std::forward<Args>(args)...)),
|
||||||
detail::identity<detail::traits::unrefcv_t<Args>...>{},
|
detail::identity<detail::traits::unrefcv_t<Args>...>{},
|
||||||
detail::util::ownership{});
|
detail::util::ownership{});
|
||||||
}
|
}
|
||||||
@ -883,19 +913,22 @@ auto make_ready_continuable(Args&&... args) {
|
|||||||
/// auto ct = cti::make_exceptional_continuable<int>(ptr);
|
/// auto ct = cti::make_exceptional_continuable<int>(ptr);
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// \tparam Signature The fake signature of the returned continuable.
|
/// \tparam Args The fake signature of the returned continuable.
|
||||||
///
|
///
|
||||||
/// \since 3.0.0
|
/// \since 3.0.0
|
||||||
template <typename... Signature, typename Exception>
|
template <typename... Args, typename Exception>
|
||||||
constexpr auto make_exceptional_continuable(Exception&& exception) {
|
constexpr auto make_exceptional_continuable(Exception&& exception) {
|
||||||
static_assert(sizeof...(Signature) > 0,
|
static_assert(sizeof...(Args) > 0,
|
||||||
"Requires at least one type for the fake signature!");
|
"Requires at least one type for the fake signature!");
|
||||||
|
|
||||||
return make_continuable<Signature...>( // ...
|
using hint_t = typename detail::hints::from_args<Args...>::type;
|
||||||
[exception = std::forward<Exception>(exception)](auto&& promise) mutable {
|
using ready_continuation_t =
|
||||||
std::forward<decltype(promise)>(promise).set_exception(
|
typename detail::base::ready_continuation_from_hint<hint_t>::type;
|
||||||
std::move(exception));
|
using result_t = typename detail::base::result_from_hint<hint_t>::type;
|
||||||
});
|
return detail::base::attorney::create_from_raw(
|
||||||
|
ready_continuation_t(result_t::from(exception_arg_t{},
|
||||||
|
std::forward<Exception>(exception))),
|
||||||
|
hint_t{}, detail::util::ownership{});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a continuable_base with the parameterized result which never
|
/// Returns a continuable_base with the parameterized result which never
|
||||||
|
|||||||
@ -40,13 +40,13 @@ namespace cti {
|
|||||||
/// and continuations.
|
/// and continuations.
|
||||||
///
|
///
|
||||||
/// For the callback and the continuation `Args...` represents the
|
/// For the callback and the continuation `Args...` represents the
|
||||||
/// asynchronous results:
|
/// asynchronous result:
|
||||||
/// ```cpp
|
/// ```cpp
|
||||||
/// template<typename... Args>
|
/// template<typename... Args>
|
||||||
/// struct continuation {
|
/// struct continuation {
|
||||||
/// void operator() (callback<Args...>);
|
/// void operator() (callback<Args...>);
|
||||||
/// bool operator() (cti::is_ready_arg_t) const;
|
/// bool operator() (cti::is_ready_arg_t) const;
|
||||||
/// std::tuple<Args...> operator() (cti::query_arg_t);
|
/// result<Args...> operator() (cti::unpack_arg_t);
|
||||||
/// };
|
/// };
|
||||||
/// ```
|
/// ```
|
||||||
/// ```cpp
|
/// ```cpp
|
||||||
@ -62,7 +62,6 @@ namespace cti {
|
|||||||
/// of a continuable_base or promise_base.
|
/// of a continuable_base or promise_base.
|
||||||
///
|
///
|
||||||
/// \since 4.0.0
|
/// \since 4.0.0
|
||||||
///
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
using signature_arg_t = detail::identity<Args...>;
|
using signature_arg_t = detail::identity<Args...>;
|
||||||
|
|
||||||
@ -71,17 +70,24 @@ using signature_arg_t = detail::identity<Args...>;
|
|||||||
/// without having side effects.
|
/// without having side effects.
|
||||||
///
|
///
|
||||||
/// \since 4.0.0
|
/// \since 4.0.0
|
||||||
///
|
|
||||||
struct is_ready_arg_t {};
|
struct is_ready_arg_t {};
|
||||||
|
|
||||||
/// Represents the tag type that is used to query the continuation
|
/// Represents the tag type that is used to unpack the result of a continuation.
|
||||||
/// for its arguments when resolves the callback instantly
|
///
|
||||||
/// without having side effects.
|
/// \attention It's required that the query of is_ready_arg_t returns true,
|
||||||
/// It's required that the query of is_ready_arg_t returns true.
|
/// otherwise the behaviour when unpacking is unspecified.
|
||||||
///
|
///
|
||||||
/// \since 4.0.0
|
/// \since 4.0.0
|
||||||
|
struct unpack_arg_t {};
|
||||||
|
|
||||||
|
/// \copydoc unpack_arg_t
|
||||||
///
|
///
|
||||||
struct query_arg_t {};
|
/// \deprecated The query_arg_t was deprecated because of
|
||||||
|
/// its new naming unpack_arg_t.
|
||||||
|
///
|
||||||
|
[[deprecated("The dispatch_error_tag was replaced by unpack_arg_t and will "
|
||||||
|
"be removed in a later major version!")]] //
|
||||||
|
typedef unpack_arg_t query_arg_t;
|
||||||
|
|
||||||
/// Represents the tag type that is used to disambiguate the
|
/// Represents the tag type that is used to disambiguate the
|
||||||
/// callback operator() in order to take the exception asynchronous chain.
|
/// callback operator() in order to take the exception asynchronous chain.
|
||||||
@ -89,7 +95,6 @@ struct query_arg_t {};
|
|||||||
/// \note see continuable::next for details.
|
/// \note see continuable::next for details.
|
||||||
///
|
///
|
||||||
/// \since 4.0.0
|
/// \since 4.0.0
|
||||||
///
|
|
||||||
struct exception_arg_t {};
|
struct exception_arg_t {};
|
||||||
|
|
||||||
/// \copydoc exception_arg_t
|
/// \copydoc exception_arg_t
|
||||||
@ -100,7 +105,7 @@ struct exception_arg_t {};
|
|||||||
///
|
///
|
||||||
[[deprecated("The dispatch_error_tag was replaced by exception_arg_t and will "
|
[[deprecated("The dispatch_error_tag was replaced by exception_arg_t and will "
|
||||||
"be removed in a later major version!")]] //
|
"be removed in a later major version!")]] //
|
||||||
typedef exception_arg_t dispatch_error_tag;
|
typedef exception_arg_t dispatch_error_tag;
|
||||||
|
|
||||||
/// Represents the type that is used as exception type
|
/// Represents the type that is used as exception type
|
||||||
///
|
///
|
||||||
@ -111,7 +116,6 @@ typedef exception_arg_t dispatch_error_tag;
|
|||||||
/// defining `CONTINUABLE_WITH_CUSTOM_ERROR_TYPE`.
|
/// defining `CONTINUABLE_WITH_CUSTOM_ERROR_TYPE`.
|
||||||
///
|
///
|
||||||
/// \since 4.0.0
|
/// \since 4.0.0
|
||||||
///
|
|
||||||
using exception_t = detail::types::exception_t;
|
using exception_t = detail::types::exception_t;
|
||||||
|
|
||||||
/// \copydoc exception_t
|
/// \copydoc exception_t
|
||||||
@ -122,14 +126,13 @@ using exception_t = detail::types::exception_t;
|
|||||||
///
|
///
|
||||||
[[deprecated("The error_type was replaced by exception_t and will "
|
[[deprecated("The error_type was replaced by exception_t and will "
|
||||||
"be removed in a later major version!")]] //
|
"be removed in a later major version!")]] //
|
||||||
typedef exception_t error_type;
|
typedef exception_t error_type;
|
||||||
|
|
||||||
/// Represents the type that is used to disable the special meaning of types
|
/// Represents the type that is used to disable the special meaning of types
|
||||||
/// which are returned by a asynchronous result handler.
|
/// which are returned by a asynchronous result handler.
|
||||||
/// See cti::plain for details.
|
/// See cti::plain for details.
|
||||||
///
|
///
|
||||||
/// \since 4.0.0
|
/// \since 4.0.0
|
||||||
///
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
using plain_t = detail::types::plain_tag<T>;
|
using plain_t = detail::types::plain_tag<T>;
|
||||||
/// \}
|
/// \}
|
||||||
|
|||||||
@ -248,7 +248,7 @@ public:
|
|||||||
return result{detail::init_arg_t{}, std::move(values)...};
|
return result{detail::init_arg_t{}, std::move(values)...};
|
||||||
}
|
}
|
||||||
/// Creates a present result from the given exception
|
/// Creates a present result from the given exception
|
||||||
static result from(exception_t exception) {
|
static result from(exception_arg_t, exception_t exception) {
|
||||||
return result{detail::init_arg_t{}, std::move(exception)};
|
return result{detail::init_arg_t{}, std::move(exception)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -35,6 +35,7 @@
|
|||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
#include <continuable/continuable-result.hpp>
|
||||||
#include <continuable/continuable-traverse.hpp>
|
#include <continuable/continuable-traverse.hpp>
|
||||||
#include <continuable/detail/core/base.hpp>
|
#include <continuable/detail/core/base.hpp>
|
||||||
#include <continuable/detail/utility/flat-variant.hpp>
|
#include <continuable/detail/utility/flat-variant.hpp>
|
||||||
|
|||||||
@ -157,21 +157,21 @@ struct connection_finalizer<connection_strategy_all_tag> {
|
|||||||
template <typename Connection>
|
template <typename Connection>
|
||||||
static auto finalize(Connection&& connection, util::ownership ownership) {
|
static auto finalize(Connection&& connection, util::ownership ownership) {
|
||||||
// Create the target result from the connection
|
// Create the target result from the connection
|
||||||
auto result =
|
auto res =
|
||||||
aggregated::box_continuables(std::forward<Connection>(connection));
|
aggregated::box_continuables(std::forward<Connection>(connection));
|
||||||
|
|
||||||
auto signature = aggregated::hint_of_data<decltype(result)>();
|
auto signature = aggregated::hint_of_data<decltype(res)>();
|
||||||
|
|
||||||
return base::attorney::create_from(
|
return base::attorney::create_from(
|
||||||
[result = std::move(result)](auto&& callback) mutable {
|
[res = std::move(res)](auto&& callback) mutable {
|
||||||
using submitter_t =
|
using submitter_t =
|
||||||
all::result_submitter<std::decay_t<decltype(callback)>,
|
all::result_submitter<std::decay_t<decltype(callback)>,
|
||||||
std::decay_t<decltype(result)>>;
|
std::decay_t<decltype(res)>>;
|
||||||
|
|
||||||
// Create the shared state which holds the result
|
// Create the shared state which holds the result
|
||||||
// and the final callback
|
// and the final callback
|
||||||
auto state = std::make_shared<submitter_t>(
|
auto state = std::make_shared<submitter_t>(
|
||||||
std::forward<decltype(callback)>(callback), std::move(result));
|
std::forward<decltype(callback)>(callback), std::move(res));
|
||||||
|
|
||||||
// Dispatch the continuables and store its partial result
|
// Dispatch the continuables and store its partial result
|
||||||
// in the whole result
|
// in the whole result
|
||||||
|
|||||||
@ -138,6 +138,11 @@ struct connection_annotation_trait {
|
|||||||
// Return a new continuable which
|
// Return a new continuable which
|
||||||
return finalizer::finalize(std::move(connection), std::move(ownership));
|
return finalizer::finalize(std::move(connection), std::move(ownership));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename Continuable>
|
||||||
|
static bool is_ready(Continuable const& /*continuable*/) noexcept {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class prepare_continuables {
|
class prepare_continuables {
|
||||||
|
|||||||
@ -37,18 +37,6 @@
|
|||||||
|
|
||||||
namespace cti {
|
namespace cti {
|
||||||
namespace detail {
|
namespace detail {
|
||||||
template <typename Annotation>
|
|
||||||
struct annotation_trait;
|
|
||||||
|
|
||||||
/// Specialization for a present signature hint
|
|
||||||
template <typename... Args>
|
|
||||||
struct annotation_trait<identity<Args...>> {
|
|
||||||
template <typename Continuable>
|
|
||||||
static Continuable&& finish(Continuable&& continuable) {
|
|
||||||
return std::forward<Continuable>(continuable);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
namespace hints {
|
namespace hints {
|
||||||
/// Extracts the signature we pass to the internal continuable
|
/// Extracts the signature we pass to the internal continuable
|
||||||
/// from an argument pack as specified by make_continuable.
|
/// from an argument pack as specified by make_continuable.
|
||||||
|
|||||||
@ -71,7 +71,9 @@ struct is_continuable<continuable_base<Data, Annotation>> : std::true_type {};
|
|||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
struct ready_continuation {
|
struct ready_continuation {
|
||||||
std::tuple<Args...> values_;
|
explicit ready_continuation(result<Args...> result)
|
||||||
|
: result_(std::move(result)) {
|
||||||
|
}
|
||||||
|
|
||||||
ready_continuation() = delete;
|
ready_continuation() = delete;
|
||||||
~ready_continuation() = default;
|
~ready_continuation() = default;
|
||||||
@ -80,43 +82,42 @@ struct ready_continuation {
|
|||||||
ready_continuation& operator=(ready_continuation&&) = default;
|
ready_continuation& operator=(ready_continuation&&) = default;
|
||||||
ready_continuation& operator=(ready_continuation const&) = delete;
|
ready_continuation& operator=(ready_continuation const&) = delete;
|
||||||
|
|
||||||
explicit ready_continuation(Args... values) : values_(std::move(values)...) {
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename Callback>
|
template <typename Callback>
|
||||||
void operator()(Callback&& callback) {
|
void operator()(Callback&& callback) {
|
||||||
traits::unpack(std::forward<Callback>(callback), std::move(values_));
|
if (result_.is_value()) {
|
||||||
|
traits::unpack(std::forward<Callback>(callback), std::move(result_));
|
||||||
|
} else if (result_.is_exception()) {
|
||||||
|
util::invoke(std::forward<Callback>(callback), exception_arg_t{},
|
||||||
|
result_.get_exception());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator()(is_ready_arg_t) const noexcept {
|
bool operator()(is_ready_arg_t) const noexcept {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::tuple<Args...> operator()(query_arg_t) {
|
result<Args...> operator()(unpack_arg_t) {
|
||||||
return std::move(values_);
|
return std::move(result_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
result<Args...> result_;
|
||||||
};
|
};
|
||||||
template <>
|
|
||||||
struct ready_continuation<> {
|
|
||||||
ready_continuation() = default;
|
|
||||||
~ready_continuation() = default;
|
|
||||||
ready_continuation(ready_continuation&&) = default;
|
|
||||||
ready_continuation(ready_continuation const&) = delete;
|
|
||||||
ready_continuation& operator=(ready_continuation&&) = default;
|
|
||||||
ready_continuation& operator=(ready_continuation const&) = delete;
|
|
||||||
|
|
||||||
template <typename Callback>
|
template <typename T>
|
||||||
void operator()(Callback&& callback) {
|
struct ready_continuation_from_hint;
|
||||||
util::invoke(std::forward<Callback>(callback));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator()(is_ready_arg_t) const noexcept {
|
template <typename... Args>
|
||||||
return true;
|
struct ready_continuation_from_hint<identity<Args...>> {
|
||||||
}
|
using type = ready_continuation<Args...>;
|
||||||
|
};
|
||||||
|
|
||||||
std::tuple<> operator()(query_arg_t) {
|
template <typename T>
|
||||||
return std::make_tuple();
|
struct result_from_hint;
|
||||||
}
|
|
||||||
|
template <typename... Args>
|
||||||
|
struct result_from_hint<identity<Args...>> {
|
||||||
|
using type = result<Args...>;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename Hint, typename Continuation>
|
template <typename Hint, typename Continuation>
|
||||||
@ -139,7 +140,7 @@ struct proxy_continuable<identity<Args...>, Continuation> : Continuation {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::tuple<Args...> operator()(query_arg_t) {
|
result<Args...> operator()(unpack_arg_t) {
|
||||||
CTI_DETAIL_UNREACHABLE();
|
CTI_DETAIL_UNREACHABLE();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -185,10 +186,29 @@ struct attorney {
|
|||||||
|
|
||||||
template <typename Data, typename Annotation>
|
template <typename Data, typename Annotation>
|
||||||
static auto query(continuable_base<Data, Annotation>&& continuation) {
|
static auto query(continuable_base<Data, Annotation>&& continuation) {
|
||||||
return std::move(continuation).consume()(query_arg_t{});
|
return std::move(continuation).consume()(unpack_arg_t{});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace base
|
||||||
|
|
||||||
|
template <typename Annotation>
|
||||||
|
struct annotation_trait;
|
||||||
|
|
||||||
|
/// Specialization for a present signature hint
|
||||||
|
template <typename... Args>
|
||||||
|
struct annotation_trait<identity<Args...>> {
|
||||||
|
template <typename Continuable>
|
||||||
|
static Continuable&& finish(Continuable&& continuable) {
|
||||||
|
return std::forward<Continuable>(continuable);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Continuable>
|
||||||
|
static bool is_ready(Continuable const& continuable) noexcept {
|
||||||
|
return base::attorney::is_ready(continuable);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
namespace base {
|
||||||
/// Returns the signature hint of the given continuable
|
/// Returns the signature hint of the given continuable
|
||||||
template <typename Data, typename... Args>
|
template <typename Data, typename... Args>
|
||||||
constexpr identity<Args...>
|
constexpr identity<Args...>
|
||||||
@ -777,6 +797,7 @@ namespace detail {
|
|||||||
template <typename Callable>
|
template <typename Callable>
|
||||||
struct exception_stripper_proxy {
|
struct exception_stripper_proxy {
|
||||||
Callable callable_;
|
Callable callable_;
|
||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
auto operator()(exception_arg_t, Args&&... args)
|
auto operator()(exception_arg_t, Args&&... args)
|
||||||
-> decltype(util::invoke(std::declval<Callable>(), //
|
-> decltype(util::invoke(std::declval<Callable>(), //
|
||||||
@ -843,7 +864,14 @@ struct chained_continuation<identity<Args...>, identity<NextArgs...>,
|
|||||||
if (is_ready) {
|
if (is_ready) {
|
||||||
// Invoke the proxy callback directly with the result to
|
// Invoke the proxy callback directly with the result to
|
||||||
// avoid a potential type erasure.
|
// avoid a potential type erasure.
|
||||||
traits::unpack(std::move(proxy), std::move(continuation_)(query_arg_t{}));
|
auto result = std::move(continuation_)(unpack_arg_t{});
|
||||||
|
|
||||||
|
if (result.is_value()) {
|
||||||
|
traits::unpack(std::move(proxy), std::move(result));
|
||||||
|
} else if (result.is_exception()) {
|
||||||
|
util::invoke(std::move(proxy), exception_arg_t{},
|
||||||
|
std::move(result.get_exception()));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// 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
|
||||||
@ -856,7 +884,7 @@ struct chained_continuation<identity<Args...>, identity<NextArgs...>,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::tuple<NextArgs...> operator()(query_arg_t) {
|
result<NextArgs...> operator()(unpack_arg_t) {
|
||||||
CTI_DETAIL_UNREACHABLE();
|
CTI_DETAIL_UNREACHABLE();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -892,14 +920,20 @@ struct chained_continuation<identity<Args...>, identity<NextArgs...>,
|
|||||||
std::forward<decltype(next_callback)>(next_callback));
|
std::forward<decltype(next_callback)>(next_callback));
|
||||||
|
|
||||||
// Extract the result out of the ready continuable
|
// Extract the result out of the ready continuable
|
||||||
traits::unpack(std::move(proxy), std::move(continuation_)(query_arg_t{}));
|
auto result = std::move(continuation_)(unpack_arg_t{});
|
||||||
|
if (result.is_value()) {
|
||||||
|
traits::unpack(std::move(proxy), std::move(result));
|
||||||
|
} else if (result.is_exception()) {
|
||||||
|
util::invoke(std::move(proxy), exception_arg_t{},
|
||||||
|
std::move(result.get_exception()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator()(is_ready_arg_t) const noexcept {
|
bool operator()(is_ready_arg_t) const noexcept {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::tuple<NextArgs...> operator()(query_arg_t) {
|
result<NextArgs...> operator()(unpack_arg_t) {
|
||||||
CTI_DETAIL_UNREACHABLE();
|
CTI_DETAIL_UNREACHABLE();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -79,18 +79,15 @@ public:
|
|||||||
: continuable_(std::move(continuable)) {
|
: continuable_(std::move(continuable)) {
|
||||||
|
|
||||||
// If the continuable is ready resolve the result from the
|
// If the continuable is ready resolve the result from the
|
||||||
// continuable immediatly.
|
// continuable immediately.
|
||||||
if (base::attorney::is_ready(continuable_)) {
|
if (base::attorney::is_ready(continuable_)) {
|
||||||
traits::unpack(
|
assert(result_.is_empty());
|
||||||
[&](auto&&... args) {
|
result_ = base::attorney::query(std::move(continuable_));
|
||||||
resolve(std::forward<decltype(args)>(args)...);
|
|
||||||
},
|
|
||||||
base::attorney::query(std::move(continuable_)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Since continuables are evaluated lazily we are not
|
/// Return whether the continuable can provide its result instantly,
|
||||||
/// capable to say whether the resumption will be instantly.
|
/// which also means its execution is side-effect free.
|
||||||
bool await_ready() const noexcept {
|
bool await_ready() const noexcept {
|
||||||
return !result_.is_empty();
|
return !result_.is_empty();
|
||||||
}
|
}
|
||||||
@ -102,7 +99,8 @@ public:
|
|||||||
// Forward every result to the current awaitable
|
// Forward every result to the current awaitable
|
||||||
std::move(continuable_)
|
std::move(continuable_)
|
||||||
.next([h, this](auto&&... args) mutable {
|
.next([h, this](auto&&... args) mutable {
|
||||||
resolve(std::forward<decltype(args)>(args)...);
|
assert(result_.is_empty());
|
||||||
|
result_ = result_t::from(std::forward<decltype(args)>(args)...);
|
||||||
h.resume();
|
h.resume();
|
||||||
})
|
})
|
||||||
.done();
|
.done();
|
||||||
@ -122,20 +120,6 @@ public:
|
|||||||
CTI_DETAIL_TRAP();
|
CTI_DETAIL_TRAP();
|
||||||
#endif // CONTINUABLE_HAS_EXCEPTIONS
|
#endif // CONTINUABLE_HAS_EXCEPTIONS
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
/// Resolve the continuation through the result
|
|
||||||
template <typename... Args>
|
|
||||||
void resolve(Args&&... args) {
|
|
||||||
assert(result_.is_empty());
|
|
||||||
result_.set_value(std::forward<Args>(args)...);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resolve the continuation through an error
|
|
||||||
void resolve(exception_arg_t, exception_t exception) {
|
|
||||||
assert(result_.is_empty());
|
|
||||||
result_.set_exception(std::move(exception));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Converts a continuable into an awaitable object as described by
|
/// Converts a continuable into an awaitable object as described by
|
||||||
|
|||||||
@ -173,7 +173,7 @@ template <typename... Args>
|
|||||||
using continuation_erasure_t = fu2::function_base<
|
using continuation_erasure_t = fu2::function_base<
|
||||||
true, false, continuation_capacity<Args...>, true, false,
|
true, false, continuation_capacity<Args...>, true, false,
|
||||||
void(promise_base<callback<Args...>, signature_arg_t<Args...>>),
|
void(promise_base<callback<Args...>, signature_arg_t<Args...>>),
|
||||||
bool(is_ready_arg_t) const, std::tuple<Args...>(query_arg_t)>;
|
bool(is_ready_arg_t) const, result<Args...>(unpack_arg_t)>;
|
||||||
|
|
||||||
#ifdef CONTINUABLE_HAS_IMMEDIATE_TYPES
|
#ifdef CONTINUABLE_HAS_IMMEDIATE_TYPES
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
@ -229,7 +229,7 @@ public:
|
|||||||
return erasure_(is_ready_arg);
|
return erasure_(is_ready_arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::tuple<Args...> operator()(query_arg_t query_arg) {
|
result<Args...> operator()(unpack_arg_t query_arg) {
|
||||||
return erasure_(query_arg);
|
return erasure_(query_arg);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -100,10 +100,10 @@ template <typename C, typename V>
|
|||||||
void assert_async_validation(C&& continuable, V&& validator) {
|
void assert_async_validation(C&& continuable, V&& validator) {
|
||||||
assert_async_completion(
|
assert_async_completion(
|
||||||
std::forward<C>(continuable)
|
std::forward<C>(continuable)
|
||||||
.then(
|
.then([validator =
|
||||||
[validator = std::forward<V>(validator)](auto&&... args) mutable {
|
std::forward<V>(validator)](auto&&... args) mutable {
|
||||||
validator(std::forward<decltype(args)>(args)...);
|
validator(std::forward<decltype(args)>(args)...);
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Expects that the continuable is finished with the given arguments
|
/// Expects that the continuable is finished with the given arguments
|
||||||
@ -113,17 +113,17 @@ void assert_async_binary_validation(V&& validator, C&& continuable,
|
|||||||
|
|
||||||
using size = std::integral_constant<std::size_t, sizeof...(expected)>;
|
using size = std::integral_constant<std::size_t, sizeof...(expected)>;
|
||||||
|
|
||||||
assert_async_validation(
|
assert_async_validation(std::forward<C>(continuable), [
|
||||||
std::forward<C>(continuable),
|
expected_pack = std::make_tuple(std::forward<Args>(expected)...),
|
||||||
[expected_pack = std::make_tuple(std::forward<Args>(expected)...),
|
validator = std::forward<V>(validator)
|
||||||
validator = std::forward<V>(validator)](auto&&... args) mutable {
|
](auto&&... args) mutable {
|
||||||
static_assert(size::value == sizeof...(args),
|
static_assert(size::value == sizeof...(args),
|
||||||
"Async completion handler called with a different count "
|
"Async completion handler called with a different count "
|
||||||
"of arguments!");
|
"of arguments!");
|
||||||
|
|
||||||
validator(std::make_tuple(std::forward<decltype(args)>(args)...),
|
validator(std::make_tuple(std::forward<decltype(args)>(args)...),
|
||||||
expected_pack);
|
expected_pack);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Expects that the continuable is finished with the given arguments
|
/// Expects that the continuable is finished with the given arguments
|
||||||
@ -136,19 +136,20 @@ void assert_async_binary_exception_validation(V&& validator, C&& continuable,
|
|||||||
// Workaround for our known GCC bug.
|
// Workaround for our known GCC bug.
|
||||||
util::unused(std::forward<decltype(args)>(args)...);
|
util::unused(std::forward<decltype(args)>(args)...);
|
||||||
|
|
||||||
// ...
|
// The exception was not thrown!
|
||||||
FAIL();
|
FAIL();
|
||||||
})
|
})
|
||||||
.fail([called, validator = std::forward<decltype(validator)>(validator),
|
.fail([
|
||||||
expected = std::forward<decltype(expected)>(expected)](
|
called, validator = std::forward<decltype(validator)>(validator),
|
||||||
exception_t error) {
|
expected = std::forward<decltype(expected)>(expected)
|
||||||
|
](exception_t error) {
|
||||||
ASSERT_FALSE(*called);
|
ASSERT_FALSE(*called);
|
||||||
*called = true;
|
*called = true;
|
||||||
|
|
||||||
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||||
try {
|
try {
|
||||||
std::rethrow_exception(error);
|
std::rethrow_exception(error);
|
||||||
} catch (std::decay_t<decltype(expected)>& exception) {
|
} catch (std::decay_t<decltype(expected)> const& exception) {
|
||||||
validator(exception, expected);
|
validator(exception, expected);
|
||||||
} catch (...) {
|
} catch (...) {
|
||||||
FAIL();
|
FAIL();
|
||||||
|
|||||||
@ -21,6 +21,7 @@ add_executable(test-continuable-single
|
|||||||
${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-forward-decl.cpp
|
${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-forward-decl.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-flat-variant.cpp
|
${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-flat-variant.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-result.cpp
|
${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-result.cpp
|
||||||
|
${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-ready.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-promisify.cpp
|
${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-promisify.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-erasure.cpp
|
${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-erasure.cpp
|
||||||
${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-traverse.cpp
|
${CMAKE_CURRENT_LIST_DIR}/single/test-continuable-traverse.cpp
|
||||||
|
|||||||
@ -44,8 +44,8 @@ struct my_continuation {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::tuple<> operator()(query_arg_t) {
|
result<> operator()(unpack_arg_t) {
|
||||||
return std::make_tuple();
|
return make_result();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
181
test/unit-test/single/test-continuable-ready.cpp
Normal file
181
test/unit-test/single/test-continuable-ready.cpp
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
|
||||||
|
/*
|
||||||
|
Copyright(c) 2015 - 2019 Denis Blank <denis.blank at outlook dot com>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files(the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions :
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <utility>
|
||||||
|
#include <test-continuable.hpp>
|
||||||
|
|
||||||
|
using namespace cti;
|
||||||
|
|
||||||
|
TEST(single_ready_test, is_not_ready_non_immediate) {
|
||||||
|
auto c = async([] {
|
||||||
|
// ...
|
||||||
|
});
|
||||||
|
|
||||||
|
ASSERT_FALSE(c.is_ready());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(single_ready_test, is_not_ready_non_immediate_erasure) {
|
||||||
|
continuable<> c = async([] {
|
||||||
|
// ...
|
||||||
|
});
|
||||||
|
|
||||||
|
ASSERT_FALSE(c.is_ready());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(single_ready_test, is_ready_immediate) {
|
||||||
|
{
|
||||||
|
auto c = make_ready_continuable();
|
||||||
|
ASSERT_TRUE(c.is_ready());
|
||||||
|
|
||||||
|
result<> res = std::move(c).unpack();
|
||||||
|
|
||||||
|
ASSERT_TRUE(res.is_value());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto c = make_ready_continuable(22);
|
||||||
|
ASSERT_TRUE(c.is_ready());
|
||||||
|
|
||||||
|
result<int> res = std::move(c).unpack();
|
||||||
|
|
||||||
|
ASSERT_EQ(get<0>(res), 22);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto c = make_ready_continuable(33, 44);
|
||||||
|
ASSERT_TRUE(c.is_ready());
|
||||||
|
|
||||||
|
result<int, int> res = std::move(c).unpack();
|
||||||
|
|
||||||
|
ASSERT_EQ(get<0>(res), 33);
|
||||||
|
ASSERT_EQ(get<1>(res), 44);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto c = make_ready_continuable(55, 66, 77);
|
||||||
|
ASSERT_TRUE(c.is_ready());
|
||||||
|
|
||||||
|
result<int, int, int> res = std::move(c).unpack();
|
||||||
|
|
||||||
|
ASSERT_EQ(get<0>(res), 55);
|
||||||
|
ASSERT_EQ(get<1>(res), 66);
|
||||||
|
ASSERT_EQ(get<2>(res), 77);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(single_ready_test, is_ready_immediate_erasure) {
|
||||||
|
{
|
||||||
|
continuable<> c = make_ready_continuable();
|
||||||
|
|
||||||
|
ASSERT_TRUE(c.is_ready());
|
||||||
|
|
||||||
|
result<> res = std::move(c).unpack();
|
||||||
|
|
||||||
|
ASSERT_TRUE(res.is_value());
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
continuable<int> c = make_ready_continuable(22);
|
||||||
|
ASSERT_TRUE(c.is_ready());
|
||||||
|
|
||||||
|
result<int> res = std::move(c).unpack();
|
||||||
|
|
||||||
|
ASSERT_EQ(get<0>(res), 22);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
continuable<int, int> c = make_ready_continuable(33, 44);
|
||||||
|
ASSERT_TRUE(c.is_ready());
|
||||||
|
|
||||||
|
result<int, int> res = std::move(c).unpack();
|
||||||
|
|
||||||
|
ASSERT_EQ(get<0>(res), 33);
|
||||||
|
ASSERT_EQ(get<1>(res), 44);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
continuable<int, int, int> c = make_ready_continuable(55, 66, 77);
|
||||||
|
ASSERT_TRUE(c.is_ready());
|
||||||
|
|
||||||
|
result<int, int, int> res = std::move(c).unpack();
|
||||||
|
|
||||||
|
ASSERT_EQ(get<0>(res), 55);
|
||||||
|
ASSERT_EQ(get<1>(res), 66);
|
||||||
|
ASSERT_EQ(get<2>(res), 77);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(single_ready_test, is_ready_exception) {
|
||||||
|
{
|
||||||
|
auto c = make_exceptional_continuable<void>(supply_test_exception());
|
||||||
|
ASSERT_TRUE(c.is_ready());
|
||||||
|
|
||||||
|
result<> res = std::move(c).unpack();
|
||||||
|
|
||||||
|
ASSERT_TRUE(res.is_exception());
|
||||||
|
|
||||||
|
ASSERT_ASYNC_EXCEPTION_RESULT(
|
||||||
|
make_exceptional_continuable<void>(res.get_exception()),
|
||||||
|
get_test_exception_proto())
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
auto c = make_exceptional_continuable<int, int>(supply_test_exception());
|
||||||
|
ASSERT_TRUE(c.is_ready());
|
||||||
|
|
||||||
|
result<int, int> res = std::move(c).unpack();
|
||||||
|
|
||||||
|
ASSERT_ASYNC_EXCEPTION_RESULT(
|
||||||
|
make_exceptional_continuable<void>(res.get_exception()),
|
||||||
|
get_test_exception_proto())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(single_ready_test, is_ready_exception_erasure) {
|
||||||
|
{
|
||||||
|
continuable<> c =
|
||||||
|
make_exceptional_continuable<void>(supply_test_exception());
|
||||||
|
ASSERT_TRUE(c.is_ready());
|
||||||
|
|
||||||
|
result<> res = std::move(c).unpack();
|
||||||
|
|
||||||
|
ASSERT_TRUE(res.is_exception());
|
||||||
|
|
||||||
|
ASSERT_ASYNC_EXCEPTION_RESULT(
|
||||||
|
make_exceptional_continuable<void>(res.get_exception()),
|
||||||
|
get_test_exception_proto())
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
continuable<int, int> c =
|
||||||
|
make_exceptional_continuable<int, int>(supply_test_exception());
|
||||||
|
ASSERT_TRUE(c.is_ready());
|
||||||
|
|
||||||
|
result<int, int> res = std::move(c).unpack();
|
||||||
|
|
||||||
|
ASSERT_ASYNC_EXCEPTION_RESULT(
|
||||||
|
make_exceptional_continuable<void>(res.get_exception()),
|
||||||
|
get_test_exception_proto())
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user