Files
cppdraft_translate/cppdraft/exec/when/all.md
2025-10-25 03:02:53 +03:00

13 KiB
Raw Blame History

[exec.when.all]

33 Execution control library [exec]

33.9 Senders [exec.snd]

33.9.12 Sender adaptors [exec.adapt]

33.9.12.12 execution::when_all [exec.when.all]

1

#

when_all and when_all_with_variant both adapt multiple input senders into a sender that completes when all input senders have completed.

when_all only accepts senders with a single value completion signature and on success concatenates all the input senders' value result datums into its own value completion operation.

when_all_with_variant(sndrs...) is semantically equivalent to when_all(into_variant(sndrs)...), where sndrs is a pack of subexpressions whose types model sender.

2

#

The names when_all and when_all_with_variant denote customization point objects.

Let sndrs be a pack of subexpressions, let Sndrs be a pack of the types decltype((sndrs))..., and let CD be the type common_type_t<decltype(get-domain-early(sndrs))...>.

Let CD2 be CD if CD is well-formed, anddefault_domain otherwise.

The expressions when_all(sndrs...) andwhen_all_with_variant(sndrs...) are ill-formed if any of the following is true:

sizeof...(sndrs) is 0, or

(sender && ...) is false.

3

#

The expression when_all(sndrs...) is expression-equivalent to:transform_sender(CD2(), make-sender(when_all, {}, sndrs...))

4

#

The exposition-only class template impls-for ([exec.snd.expos]) is specialized for when_all_t as follows:

🔗

namespace std::execution {template<>struct impls-for<when_all_t> : default-impls {static constexpr auto get-attrs = see below; static constexpr auto get-env = see below; static constexpr auto get-state = see below; static constexpr auto start = see below; static constexpr auto complete = see below; template<class Sndr, class... Env>static consteval void check-types(); };}

5

#

Let make-when-all-env be the following exposition-only function template:templateconstexpr auto make-when-all-env(inplace_stop_source& stop_src, // exposition only Env&& env) noexcept {return see below;}

Returns an object e such that

decltype(e) models queryable, and

e.query(get_stop_token) is expression-equivalent tostate.stop-src.get_token(), and

given a query object q with type other than cv stop_token_t and whose type satisfies forwarding-query,e.query(q) is expression-equivalent to get_env(rcvr).query(q).

6

#

Let when-all-env be an alias template such thatwhen-all-env denotes the typedecltype(make-
when-all-env
(declval<inplace_stop_source&>(), declval())).

🔗

template<class Sndr, class... Env> static consteval void check-types();

7

#

Let Is be the pack of integral template arguments of the integer_sequence specialization denoted byindices-for.

8

#

Effects: Equivalent to:auto fn = []() {auto cs = get_completion_signatures<Child, when-all-env...>(); if constexpr (cs.count-of(set_value) >= 2)throw unspecified-exception(); decay-copyable-result-datums(cs); // see [exec.snd.expos]};(fn.template operator()<child-type<Sndr, Is>>(), ...); where unspecified-exception is a type derived from exception.

9

#

Throws: Any exception thrown as a result of evaluating the Effects, or an exception of an unspecified type derived from exception when CD is ill-formed.

10

#

The member impls-for<when_all_t>::get-attrs is initialized with a callable object equivalent to the following lambda expression:[](auto&&, auto&&... child) noexcept {if constexpr (same_as<CD, default_domain>) {return env<>(); } else {return MAKE-ENV(get_domain, CD()); }}

11

#

The member impls-for<when_all_t>::get-env is initialized with a callable object equivalent to the following lambda expression:[]<class State, class Rcvr>(auto&&, State& state, const Receiver& rcvr) noexcept {return make-when-all-env(state.stop-src, get_env(rcvr));}

12

#

The member impls-for<when_all_t>::get-state is initialized with a callable object equivalent to the following lambda expression:[]<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept(noexcept(e)) -> decltype(e) {return e;} where e is the expressionstd::forward(sndr).apply(make-state()) and where make-state is the following exposition-only class template:enum class disposition { started, error, stopped }; // exposition onlytemplatestruct make-state {template<class... Sndrs>auto operator()(auto, auto, Sndrs&&... sndrs) const {using values_tuple = see below; using errors_variant = see below; using stop_callback = stop_callback_for_t<stop_token_of_t<env_of_t>, on-stop-request>; struct state-type {void arrive(Rcvr& rcvr) noexcept { // exposition onlyif (0 == --count) {complete(rcvr); }}void complete(Rcvr& rcvr) noexcept; // exposition only atomic<size_t> count{sizeof...(sndrs)}; // exposition only inplace_stop_source stop_src{}; // exposition only atomic<disposition> disp{disposition::started}; // exposition only errors_variant errors{}; // exposition only values_tuple values{}; // exposition only optional<stop_callback> on_stop{nullopt}; // exposition only}; return state-type{}; }};

13

#

Let copy-fail be exception_ptr if decay-copying any of the child senders' result datums can potentially throw; otherwise, none-such, where none-such is an unspecified empty class type.

14

#

The alias values_tuple denotes the typetuple<value_types_of_t<Sndrs, FWD-ENV-T(env_of_t), decayed-tuple, optional>...> if that type is well-formed; otherwise, tuple<>.

15

#

The alias errors_variant denotes the type variant<none-such, copy-fail, Es...> with duplicate types removed, where Es is the pack of the decayed types of all the child senders' possible error result datums.

16

#

The membervoid state-type::complete(Rcvr& rcvr) noexcept behaves as follows:

If disp is equal to disposition::started, evaluates:auto tie = []<class... T>(tuple<T...>& t) noexcept { return tuple<T&...>(t); };auto set = [&](auto&... t) noexcept { set_value(std::move(rcvr), std::move(t)...); };

*on_stop*.reset();

apply([&](auto&... opts) noexcept { apply(set, tuple_cat(tie(*opts)...)); }, values);

Otherwise, if disp is equal to disposition::error, evaluates:on_stop.reset(); visit([&](Error& error) noexcept {if constexpr (same_as<Error, none-such>) { set_error(std::move(rcvr), std::move(error)); }}, errors);

Otherwise, evaluates:on_stop.reset(); set_stopped(std::move(rcvr));

17

#

The member impls-for<when_all_t>::start is initialized with a callable object equivalent to the following lambda expression:[]<class State, class Rcvr, class... Ops>( State& state, Rcvr& rcvr, Ops&... ops) noexcept -> void { state.on_stop.emplace( get_stop_token(get_env(rcvr)), on-stop-request{state.stop_src}); if (state.stop_src.stop_requested()) { state.*on_stop.*reset(); set_stopped(std::move(rcvr)); } else {(start(ops), ...); }}

18

#

The member impls-for<when_all_t>::complete** is initialized with a callable object equivalent to the following lambda expression:[]<class Index, class State, class Rcvr, class Set, class... Args>(this auto& complete, Index, State& state, Rcvr& rcvr, Set, Args&&... args) noexcept -> void {if constexpr (same_as<Set, set_error_t>) {if (disposition::error != state.disp.exchange(disposition::error)) { state.stop_src.request_stop(); TRY-EMPLACE-ERROR(state.errors, std::forward(args)...); }} else if constexpr (same_as<Set, set_stopped_t>) {auto expected = disposition::started; if (state.disp.compare_exchange_strong(expected, disposition::stopped)) { state.stop_src.request_stop(); }} else if constexpr (same_as<decltype(State::values), tuple<>>) {if (state.disp == disposition::started) {auto& opt = getIndex::value(state.values); TRY-EMPLACE-VALUE(complete, opt, std::forward(args)...); }} state.arrive(rcvr);} where TRY-EMPLACE-ERROR(v, e), for subexpressions v and e, is equivalent to:try { v.template emplace<decltype(auto(e))>(e);} catch (...) { v.template emplace<exception_ptr>(current_exception());} if the expression decltype(auto(e))(e) is potentially throwing; otherwise, v.template emplace<decltype(auto(e))>(e); and where TRY-EMPLACE-VALUE(c, o, as...), for subexpressions c, o, and pack of subexpressions as, is equivalent to:try { o.emplace(as...);} catch (...) { c(Index(), state, rcvr, set_error, current_exception()); return;} if the expression decayed-tuple<decltype(as)...>{as...} is potentially throwing; otherwise, o.emplace(as...).

19

#

The expression when_all_with_variant(sndrs...) is expression-equivalent to:transform_sender(CD2(), make-sender(when_all_with_variant, {}, sndrs...));

20

#

Given subexpressions sndr and env, ifsender-for<decltype((sndr)), when_all_with_variant_t> is false, then the expression when_all_with_variant.transform_sender(sndr, env) is ill-formed; otherwise, it is equivalent to:auto&& [_, _, ...child] = sndr;return when_all(into_variant(std::forward_like<decltype((sndr))>(child))...);

[Note 1:

This causes the when_all_with_variant(sndrs...) sender to become when_all(into_variant(sndrs)...) when it is connected with a receiver whose execution domain does not customize when_all_with_variant.

— end note]