Implement make_plain to make it possible to not handle special objects in handlers

This commit is contained in:
Denis Blank 2019-01-14 21:02:22 +01:00
parent 4c807aec75
commit 7491022d0f
8 changed files with 105 additions and 12 deletions

View File

@ -228,15 +228,17 @@ continuable_base(continuable_base<OData, Annotation>&& other)
/// | `Arg` | `continuable_base with <Arg>` | /// | `Arg` | `continuable_base with <Arg>` |
/// | `std::pair<First, Second>` | `continuable_base with <First, Second>` | /// | `std::pair<First, Second>` | `continuable_base with <First, Second>` |
/// | `std::tuple<Args...>` | `continuable_base with <Args...>` | /// | `std::tuple<Args...>` | `continuable_base with <Args...>` |
/// | `result<Args...>` | `continuable_base with <Args...>` | /// | `cti::result<Args...>` | `continuable_base with <Args...>` |
/// | `continuable_base<Arg...>` | `continuable_base with <Args...>` | /// | `continuable_base<Arg...>` | `continuable_base with <Args...>` |
/// Which means the result type of the continuable_base is equal to /// Which means the result type of the continuable_base is equal to
/// the plain types the callback returns (`std::tuple` and /// the plain types the callback returns (`std::tuple` and
/// `std::pair` arguments are unwrapped). /// `std::pair` arguments are unwrapped).
/// A single continuable_base as argument is resolved and the result /// A single continuable_base as argument is resolved and the result
/// type is equal to the resolved continuable_base. /// type is equal to the resolved continuable_base.
/// A result<...> can be used to cancel the continuation or to /// A cti::result can be used to cancel the continuation or to
/// transition to the exception handler. /// transition to the exception handler.
/// The special unwrapping of types can be disabled through wrapping
/// such objects through a call to cti::make_plain.
/// Consider the following examples: /// Consider the following examples:
/// ```cpp /// ```cpp
/// http_request("github.com") /// http_request("github.com")
@ -864,8 +866,8 @@ constexpr auto make_continuable(Continuation&& continuation) {
/// \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) {
using detail::base::ready_continuation;
using detail::identity; using detail::identity;
using detail::base::ready_continuation;
using detail::traits::unrefcv_t; using detail::traits::unrefcv_t;
return detail::base::attorney::create_from_raw( return detail::base::attorney::create_from_raw(
ready_continuation<unrefcv_t<Args>...>{std::forward<Args>(args)...}, ready_continuation<unrefcv_t<Args>...>{std::forward<Args>(args)...},
@ -926,6 +928,33 @@ auto make_cancelling_continuable() {
return make_continuable<Signature...>([](auto&&) { /* ... */ }); return make_continuable<Signature...>([](auto&&) { /* ... */ });
} }
/// Can be used to disable the special meaning for a returned value in
/// asynchronous handler functions.
///
/// Several types have a special meaning when being returned from a callable
/// passed to asynchronous handler functions like:
/// - continuable_base::then
/// - continuable_base::fail
/// - continuable_base::next
///
/// For instance such types are std::tuple, std::pair and cti::result.
///
/// Wrapping such an object through a call to make_plain disables the special
/// meaning for such objects as shown below:
/// ```cpp
/// continuable<result<int, int> c = http_request("example.com")
/// .then([](std::string content) {
/// return make_plain(make_result(0, 1));
/// })
/// ```
///
/// \since 4.0.0
///
template <typename T>
auto make_plain(T&& value) {
return plain_t<detail::traits::unrefcv_t<T>>(std::forward<T>(value));
}
/// Can be used to recover to from a failure handler, /// Can be used to recover to from a failure handler,
/// the result handler which comes after will be called with the /// the result handler which comes after will be called with the
/// corresponding result. /// corresponding result.

View File

@ -62,6 +62,7 @@ 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...>;
@ -70,6 +71,7 @@ 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 query the continuation
@ -78,6 +80,7 @@ struct is_ready_arg_t {};
/// 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.
/// ///
/// \since 4.0.0 /// \since 4.0.0
///
struct query_arg_t {}; struct query_arg_t {};
/// Represents the tag type that is used to disambiguate the /// Represents the tag type that is used to disambiguate the
@ -86,6 +89,7 @@ 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
@ -107,6 +111,7 @@ struct exception_arg_t {};
/// 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
@ -118,6 +123,15 @@ 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
/// which are returned by a asynchronous result handler.
/// See cti::plain for details.
///
/// \since 4.0.0
///
template <typename T>
using plain_t = detail::types::plain_tag<T>;
/// \} /// \}
} // namespace cti } // namespace cti

View File

@ -257,6 +257,11 @@ public:
return result{init_arg_t{}, std::move(exception)}; return result{init_arg_t{}, std::move(exception)};
} }
/// Creates an empty result
static result empty() {
return result{empty_result{}};
}
private: private:
detail::container::flat_variant<surrogate_t, exception_t> variant_; detail::container::flat_variant<surrogate_t, exception_t> variant_;
}; };

View File

@ -329,6 +329,23 @@ constexpr auto invoker_of(identity<T>) {
identify<T>{}); identify<T>{});
} }
/// - plain_tag<?> -> next_callback(?)
template <typename T>
constexpr auto invoker_of(identity<types::plain_tag<T>>) {
return make_invoker(
[](auto&& callback, auto&& next_callback, auto&&... args) {
CONTINUABLE_BLOCK_TRY_BEGIN
types::plain_tag<T> result =
invoke_callback(std::forward<decltype(callback)>(callback),
std::forward<decltype(args)>(args)...);
invoke_no_except(std::forward<decltype(next_callback)>(next_callback),
std::move(result).consume());
CONTINUABLE_BLOCK_TRY_END
},
identify<T>{});
}
/// - void -> next_callback() /// - void -> next_callback()
inline auto invoker_of(identity<void>) { inline auto invoker_of(identity<void>) {
return make_invoker( return make_invoker(
@ -469,10 +486,8 @@ void on_executor(Executor&& executor, Invoker&& invoker, Args&&... args) {
// Create a worker object which when invoked calls the callback with the // Create a worker object which when invoked calls the callback with the
// the returned arguments. // the returned arguments.
auto work = [ auto work = [invoker = std::forward<Invoker>(invoker),
invoker = std::forward<Invoker>(invoker), args = std::make_tuple(std::forward<Args>(args)...)]() mutable {
args = std::make_tuple(std::forward<Args>(args)...)
]() mutable {
traits::unpack( traits::unpack(
[&](auto&&... captured_args) { [&](auto&&... captured_args) {
// Just use the packed dispatch method which dispatches the work on // Just use the packed dispatch method which dispatches the work on

View File

@ -77,6 +77,20 @@ public:
/// Tag for constructing an empty promise_base . /// Tag for constructing an empty promise_base .
struct promise_no_init_arg_t {}; struct promise_no_init_arg_t {};
/// Marks a given callable object as transformation
template <typename T>
class plain_tag {
T value_;
public:
explicit plain_tag(T value) : value_(std::move(value)) {
}
T&& consume() && {
return std::move(value_);
}
};
} // namespace types } // namespace types
} // namespace detail } // namespace detail
} // namespace cti } // namespace cti

View File

@ -160,12 +160,12 @@ auto make_range_looper(Callable&& callable, Begin&& begin, End&& end) {
begin = std::forward<Begin>(begin), begin = std::forward<Begin>(begin),
end = std::forward<End>(end)]() mutable { end = std::forward<End>(end)]() mutable {
return util::invoke(callable, begin) return util::invoke(callable, begin)
.then([&begin, &end]() mutable -> std::tuple<result<>> { .then([&begin, &end]() mutable -> plain_t<result<>> {
// begin and end stays valid over the `then` here // begin and end stays valid over the `then` here
if (++begin != end) { if (++begin != end) {
return std::make_tuple(result<>(empty_result{})); return make_plain(result<>::empty());
} else { } else {
return std::make_tuple(make_result()); return make_plain(make_result());
} }
}); });
}; };

View File

@ -60,6 +60,8 @@ namespace cti {
/// \returns A continuable_base which asynchronous result type will /// \returns A continuable_base which asynchronous result type will
/// be computated with the same rules as continuable_base::then . /// be computated with the same rules as continuable_base::then .
/// ///
/// \since 4.0.0
///
template <typename Callable, typename... Args> template <typename Callable, typename... Args>
auto async(Callable&& callable, Args&&... args) { auto async(Callable&& callable, Args&&... args) {
return detail::operations::async(std::forward<Callable>(callable), return detail::operations::async(std::forward<Callable>(callable),

View File

@ -38,12 +38,26 @@
namespace cti { namespace cti {
/// \ingroup Operations /// \ingroup Operations
/// \{ /// \{
/// Can be used to create an asynchronous loop.
///
/// The callable will be called repeatedly until it returns a
/// cti::continuable_base which then resolves to present cti::result.
///
/// \param callable The callable type which must return a cti::continuable_base
/// which then resolves to a cti::result of arbitrary values.
///
/// \since 4.0.0
///
template <typename Callable, typename... Args> template <typename Callable, typename... Args>
auto loop(Callable&& callable, Args&&... args) { auto loop(Callable&& callable, Args&&... args) {
return detail::operations::loop(std::forward<Callable>(callable), return detail::operations::loop(std::forward<Callable>(callable),
std::forward<Args>(args)...); std::forward<Args>(args)...);
} }
///
/// \since 4.0.0
///
template <typename Callable, typename Iterator> template <typename Callable, typename Iterator>
auto range_loop(Callable&& callable, Iterator begin, Iterator end) { auto range_loop(Callable&& callable, Iterator begin, Iterator end) {
return detail::operations::loop( // return detail::operations::loop( //