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:
Denis Blank 2019-12-26 05:27:13 +01:00
parent 117a716de1
commit 0b1b284e3a
14 changed files with 352 additions and 121 deletions

View File

@ -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

View File

@ -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
@ -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
@ -129,7 +133,6 @@ typedef exception_t error_type;
/// 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>;
/// \} /// \}

View File

@ -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)};
} }

View File

@ -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>

View File

@ -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

View File

@ -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 {

View File

@ -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.

View File

@ -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();
} }
}; };

View File

@ -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

View File

@ -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);
} }
}; };

View File

@ -100,8 +100,8 @@ 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)...);
})); }));
} }
@ -113,10 +113,10 @@ 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!");
@ -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();

View File

@ -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

View File

@ -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();
} }
}; };

View 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())
}
}