[exec.coro.util] (original) (raw)
33 Execution control library [exec]
33.13 Coroutine utilities [exec.coro.util]
33.13.1 execution::as_awaitable [exec.as.awaitable]
as_awaitable transforms an object into one that is awaitable within a particular coroutine.
Subclause [exec.coro.util] makes use of the following exposition-only entities:namespace std::execution { template<class Sndr, class Promise> concept awaitable-sender = single-sender<Sndr, env_of_t<Promise>> && sender_to<Sndr, _awaitable-receiver_> && requires (Promise& p) { { p.unhandled_stopped() } -> convertible_to<coroutine_handle<>>;};template<class Sndr, class Promise> class sender-awaitable; }
The type sender-awaitable<Sndr, Promise> is equivalent to:
namespace std::execution { template<class Sndr, class Promise> class sender-awaitable { struct unit {}; using value-type = single-sender-value-type<Sndr, env_of_t<Promise>>;using result-type = conditional_t<is_void_v<_value-type_>, unit, _value-type_>;struct awaitable-receiver; variant<monostate, _result-type_, exception_ptr> result{}; connect_result_t<Sndr, _awaitable-receiver_> state; public: sender-awaitable(Sndr&& sndr, Promise& p);static constexpr bool await_ready() noexcept { return false; } void await_suspend(coroutine_handle<Promise>) noexcept { start(state); } value-type await_resume();};}
awaitable-receiver is equivalent to:struct awaitable-receiver { using receiver_concept = receiver_t; variant<monostate, _result-type_, exception_ptr>* result-ptr; coroutine_handle<Promise> continuation; };
Let rcvr be an rvalue expression of type awaitable-receiver, let crcvr be a const lvalue that refers to rcvr, let vs be a pack of subexpressions, and let err be an expression of type Err.
Then:
- If constructible_from<_result-type_, decltype((vs))...>is satisfied, the expression set_value(
rcvr, vs...) is equivalent to:try { rcvr._result-ptr_->template emplace<1>(vs...);} catch(...) { rcvr._result-ptr_->template emplace<2>(current_exception());}rcvr.continuation.resume();
Otherwise, set_value(rcvr, vs...) is ill-formed. - The expression set_error(rcvr, err) is equivalent to:rcvr._result-ptr_->template emplace<2>(AS-EXCEPT-PTR(err)); rcvr.continuation.resume();
- The expression set_stopped(rcvr) is equivalent to:static_cast<coroutine_handle<>>(rcvr.continuation.promise().unhandled_stopped()).resume();
- For any expression tagwhose type satisfies forwarding-query and for any pack of subexpressions as,get_env(crcvr).query(tag, as...) is expression-equivalent to:tag(get_env(as_const(crcvr.continuation.promise())), as...)
_sender-awaitable_(Sndr&& sndr, Promise& p);
Effects: Initializes state withconnect(std::forward<Sndr>(sndr),awaitable-receiver{addressof(result), coroutine_handle<Promise>::from_promise(p)})
_value-type_ await_resume();
Effects: Equivalent to:if (result.index() == 2) rethrow_exception(get<2>(result));if constexpr (!is_void_v<_value-type_>) return std::forward<_value-type_>(get<1>(result));
as_awaitable is a customization point object.
For subexpressions expr and pwhere p is an lvalue,Expr names the type decltype((expr)) andPromise names the type decay_t<decltype((p))>,as_awaitable(expr, p) is expression-equivalent to, except that the evaluations of expr and pare indeterminately sequenced:
- expr.as_awaitable(p) if that expression is well-formed.
Mandates: is-awaitable<A, Promise> is true, where A is the type of the expression above. - Otherwise, (void(p), expr)if is-awaitable<Expr, U> is true, where U is an unspecified class type that is not Promise and that lacks a member named await_transform.
Preconditions: is-awaitable<Expr, Promise> is true and the expression co_await exprin a coroutine with promise type U is expression-equivalent to the same expression in a coroutine with promise type Promise. - Otherwise, sender-awaitable{expr, p}if awaitable-sender<Expr, Promise> is true.
- Otherwise, (void(p), expr).
33.13.2 execution::with_awaitable_senders [exec.with.awaitable.senders]
with_awaitable_senders, when used as the base class of a coroutine promise type, makes senders awaitable in that coroutine type.
In addition, it provides a default implementation of unhandled_stoppedsuch that if a sender completes by calling set_stopped, it is treated as if an uncatchable "stopped" exception were thrown from the await-expression.
[Note 1:
The coroutine is never resumed, and the unhandled_stopped of the coroutine caller's promise type is called.
— _end note_]
namespace std::execution { template<class-type Promise> struct with_awaitable_senders { template<class OtherPromise> requires (<OtherPromise, void>) void set_continuation(coroutine_handle<OtherPromise> h) noexcept; coroutine_handle<> continuation() const noexcept { return continuation; } coroutine_handle<> unhandled_stopped() noexcept { return stopped-handler(continuation.address());} template<class Value> see below await_transform(Value&& value);private: [[noreturn]] static coroutine_handle<> default-unhandled-stopped(void*) noexcept { terminate();} coroutine_handle<> continuation{}; coroutine_handle<> (*stopped-handler)(void*) noexcept = &default-unhandled-stopped;};}
template<class OtherPromise> requires (<OtherPromise, void>) void set_continuation(coroutine_handle<OtherPromise> h) noexcept;
Effects: Equivalent to:continuation = h;if constexpr ( requires(OtherPromise& other) { other.unhandled_stopped(); } ) { stopped-handler = [](void* p) noexcept -> coroutine_handle<> { return coroutine_handle<OtherPromise>::from_address(p) .promise().unhandled_stopped();};} else { stopped-handler = &default-unhandled-stopped;}
template<class Value> _call-result-t_<as_awaitable_t, Value, Promise&> await_transform(Value&& value);
Effects: Equivalent to:return as_awaitable(std::forward<Value>(value), static_cast<Promise&>(*this));