233 lines
11 KiB
Markdown
233 lines
11 KiB
Markdown
[exec.let]
|
||
|
||
# 33 Execution control library [[exec]](./#exec)
|
||
|
||
## 33.9 Senders [[exec.snd]](exec.snd#exec.let)
|
||
|
||
### 33.9.12 Sender adaptors [[exec.adapt]](exec.adapt#exec.let)
|
||
|
||
#### 33.9.12.10 execution::let_value, execution::let_error, execution::let_stopped [exec.let]
|
||
|
||
[1](#1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3847)
|
||
|
||
let_value, let_error, and let_stopped transform
|
||
a sender's value, error, and stopped completions, respectively,
|
||
into a new child asynchronous operation
|
||
by passing the sender's result datums to a user-specified callable,
|
||
which returns a new sender that is connected and started[.](#1.sentence-1)
|
||
|
||
[2](#2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3854)
|
||
|
||
For let_value, let_error, and let_stopped,
|
||
let *set-cpo* beset_value, set_error, and set_stopped, respectively[.](#2.sentence-1)
|
||
|
||
Let the expression *let-cpo* be one oflet_value, let_error, or let_stopped[.](#2.sentence-2)
|
||
|
||
For a subexpression sndr,
|
||
let *let-env*(sndr) be expression-equivalent to
|
||
the first well-formed expression below:
|
||
|
||
- [(2.1)](#2.1)
|
||
|
||
*SCHED-ENV*(get_completion_scheduler<*decayed-typeof*<*set-cpo*>>(get_env(sndr)))
|
||
|
||
- [(2.2)](#2.2)
|
||
|
||
*MAKE-ENV*(get_domain, get_domain(get_env(sndr)))
|
||
|
||
- [(2.3)](#2.3)
|
||
|
||
(void(sndr), env<>{})
|
||
|
||
[3](#3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3872)
|
||
|
||
The names let_value, let_error, and let_stopped denote
|
||
pipeable sender adaptor objects[.](#3.sentence-1)
|
||
|
||
For subexpressions sndr and f,
|
||
let F be the decayed type of f[.](#3.sentence-2)
|
||
|
||
If decltype((sndr)) does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") or
|
||
if decltype((f)) does not satisfy [*movable-value*](exec.general#concept:movable-value "33.1 General [exec.general]"),
|
||
the expression *let-cpo*(sndr, f) is ill-formed[.](#3.sentence-3)
|
||
|
||
If F does not satisfy [invocable](concept.invocable#concept:invocable "18.7.2 Concept invocable [concept.invocable]"),
|
||
the expression let_stopped(sndr, f) is ill-formed[.](#3.sentence-4)
|
||
|
||
[4](#4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3883)
|
||
|
||
Otherwise,
|
||
the expression *let-cpo*(sndr, f) is expression-equivalent to:transform_sender(*get-domain-early*(sndr), *make-sender*(*let-cpo*, f, sndr)) except that sndr is evaluated only once[.](#4.sentence-1)
|
||
|
||
[5](#5)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3891)
|
||
|
||
The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2 Exposition-only entities"))
|
||
is specialized for *let-cpo* as follows:
|
||
|
||
[ð](#lib:impls-for%3cdecayed-typeof%3clet-cpo%3e%3e)
|
||
|
||
namespace std::execution {template<class State, class Rcvr, class... Args>void *let-bind*(State& state, Rcvr& rcvr, Args&&... args); // *exposition only*template<>struct *impls-for*<*decayed-typeof*<*let-cpo*>> : *default-impls* {static constexpr auto *get-state* = *see below*; static constexpr auto *complete* = *see below*; template<class Sndr, class... Env>static consteval void *check-types*(); };}
|
||
|
||
[6](#6)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3911)
|
||
|
||
Let *receiver2* denote the following exposition-only class template:namespace std::execution {template<class Rcvr, class Env>struct *receiver2* {using receiver_concept = receiver_t; template<class... Args>void set_value(Args&&... args) && noexcept { execution::set_value(std::move(*rcvr*), std::forward<Args>(args)...); }template<class Error>void set_error(Error&& err) && noexcept { execution::set_error(std::move(*rcvr*), std::forward<Error>(err)); }void set_stopped() && noexcept { execution::set_stopped(std::move(*rcvr*)); }decltype(auto) get_env() const noexcept {return *see below*; } Rcvr& *rcvr*; // *exposition only* Env *env*; // *exposition only*};}
|
||
|
||
Invocation of the function *receiver2*::get_env returns an object e such that
|
||
|
||
- [(6.1)](#6.1)
|
||
|
||
decltype(e) models [*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]") and
|
||
|
||
- [(6.2)](#6.2)
|
||
|
||
given a query object q,
|
||
the expression e.query(q) is expression-equivalent
|
||
to *env*.query(q) if that expression is valid;
|
||
otherwise,
|
||
if the type of q satisfies [*forwarding-query*](execution.syn#concept:forwarding-query "33.4 Header <execution> synopsis [execution.syn]"),e.query(q) is expression-equivalent
|
||
to get_env(*rcvr*).query(q);
|
||
otherwise,e.query(q) is ill-formed[.](#6.sentence-2)
|
||
|
||
[ð](#lib:check-types,impls-for%3cdecayed-typeof%3clet-cpo%3e%3e)
|
||
|
||
`template<class Sndr, class... Env>
|
||
static consteval void check-types();
|
||
`
|
||
|
||
[7](#7)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3966)
|
||
|
||
*Effects*: Equivalent to:using LetFn = remove_cvref_t<*data-type*<Sndr>>;auto cs = get_completion_signatures<*child-type*<Sndr>, *FWD-ENV-T*(Env)...>();auto fn = []<class... Ts>(*decayed-typeof*<*set-cpo*>(*)(Ts...)) {if constexpr (!*is-valid-let-sender*) // *see below*throw *unspecified-exception*();};
|
||
cs.*for-each*(*overload-set*(fn, [](auto){})); where *unspecified-exception* is
|
||
a type derived from exception, and
|
||
where *is-valid-let-sender* is true if and only if
|
||
all of the following are true:
|
||
|
||
- [(7.1)](#7.1)
|
||
|
||
([constructible_from](concept.constructible#concept:constructible_from "18.4.11 Concept constructible_from [concept.constructible]")<decay_t<Ts>, Ts> &&...)
|
||
|
||
- [(7.2)](#7.2)
|
||
|
||
[invocable](concept.invocable#concept:invocable "18.7.2 Concept invocable [concept.invocable]")<LetFn, decay_t<Ts>&...>
|
||
|
||
- [(7.3)](#7.3)
|
||
|
||
[sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]")<invoke_result_t<LetFn, decay_t<Ts>&...>>
|
||
|
||
- [(7.4)](#7.4)
|
||
|
||
sizeof...(Env) == 0 || [sender_in](exec.snd.concepts#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]")<invoke_result_t<LetFn, decay_t<Ts>&...>, *env-t*
|
||
...>
|
||
|
||
where *env-t* is the packdecltype(*let-cpo*.transform_env(declval<Sndr>(), declval<Env>()))[.](#7.sentence-1)
|
||
|
||
[8](#8)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3993)
|
||
|
||
*impls-for*<*decayed-typeof*<*let-cpo*>>::*get-state* is initialized with a callable object equivalent to the following:[]<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) requires *see below* {auto& [_, fn, child] = sndr; using fn_t = decay_t<decltype(fn)>; using env_t = decltype(*let-env*(child)); using args_variant_t = *see below*; using ops2_variant_t = *see below*; struct *state-type* { fn_t *fn*; // *exposition only* env_t *env*; // *exposition only* args_variant_t *args*; // *exposition only* ops2_variant_t *ops2*; // *exposition only*}; return *state-type*{*allocator-aware-forward*(std::forward_like<Sndr>(fn), rcvr), *let-env*(child), {}, {}};}
|
||
|
||
[9](#9)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4015)
|
||
|
||
Let Sigs be a pack of the arguments
|
||
to the completion_signatures specialization named bycompletion_signatures_of_t<*child-type*<Sndr>, *FWD-ENV-T*(env_of_t<Rcvr>)>[.](#9.sentence-1)
|
||
|
||
Let LetSigs be a pack of those types in Sigs with a return type of *decayed-typeof*<*set-cpo*>[.](#9.sentence-2)
|
||
|
||
Let *as-tuple* be an alias template
|
||
such that *as-tuple*<Tag(Args...)> denotes
|
||
the type *decayed-tuple*<Args...>[.](#9.sentence-3)
|
||
|
||
Then args_variant_t denotes
|
||
the type variant<monostate, *as-tuple*<LetSigs>...> except with duplicate types removed[.](#9.sentence-4)
|
||
|
||
[10](#10)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4028)
|
||
|
||
Given a type Tag and a pack Args,
|
||
let *as-sndr2* be an alias template
|
||
such that *as-sndr2*<Tag(Args...)> denotes
|
||
the type *call-result-t*<F, decay_t<Args>&...>[.](#10.sentence-1)
|
||
|
||
Then ops2_variant_t denotes
|
||
the typevariant<monostate, connect_result_t<*as-sndr2*<LetSigs>, *receiver2*<Rcvr, env_t>>...> except with duplicate types removed[.](#10.sentence-2)
|
||
|
||
[11](#11)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4040)
|
||
|
||
The [*requires-clause*](temp.pre#nt:requires-clause "13.1 Preamble [temp.pre]") constraining the above lambda is satisfied
|
||
if and only if
|
||
the types args_variant_t and ops2_variant_t are well-formed[.](#11.sentence-1)
|
||
|
||
[12](#12)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4045)
|
||
|
||
The exposition-only function template *let-bind* has effects equivalent to:using args_t = *decayed-tuple*<Args...>;auto mkop2 = [&] {return connect( apply(std::move(state.fn),
|
||
state.args.template emplace<args_t>(std::forward<Args>(args)...)), *receiver2*{rcvr, std::move(state.env)});};
|
||
start(state.ops2.template emplace<decltype(mkop2())>(*emplace-from*{mkop2}));
|
||
|
||
[13](#13)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4059)
|
||
|
||
*impls-for*<*decayed-typeof*<let-cpo>>::*complete* is initialized with a callable object equivalent to the following:[]<class Tag, class... Args>(auto, auto& state, auto& rcvr, Tag, Args&&... args) noexcept -> void {if constexpr ([same_as](concept.same#concept:same_as "18.4.2 Concept same_as [concept.same]")<Tag, *decayed-typeof*<*set-cpo*>>) {*TRY-EVAL*(rcvr, *let-bind*(state, rcvr, std::forward<Args>(args)...)); } else { Tag()(std::move(rcvr), std::forward<Args>(args)...); }}
|
||
|
||
[14](#14)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4073)
|
||
|
||
Let sndr and env be subexpressions, and
|
||
let Sndr be decltype((sndr))[.](#14.sentence-1)
|
||
|
||
If[*sender-for*](exec.snd.concepts#concept:sender-for "33.9.3 Sender concepts [exec.snd.concepts]")<Sndr, *decayed-typeof*<*let-cpo*>> is false,
|
||
then the expression *let-cpo*.transform_env(sndr, env) is ill-formed[.](#14.sentence-2)
|
||
|
||
Otherwise, it is equal to:auto& [_, _, child] = sndr;return *JOIN-ENV*(*let-env*(child), *FWD-ENV*(env));
|
||
|
||
[15](#15)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4087)
|
||
|
||
Let the subexpression out_sndr denote
|
||
the result of the invocation *let-cpo*(sndr, f) or
|
||
an object equal to such, and
|
||
let the subexpression rcvr denote a receiver
|
||
such that the expression connect(out_sndr, rcvr) is well-formed[.](#15.sentence-1)
|
||
|
||
The expression connect(out_sndr, rcvr) has undefined behavior
|
||
unless it creates an asynchronous operation ([[exec.async.ops]](exec.async.ops "33.3 Asynchronous operations")) that,
|
||
when started:
|
||
|
||
- [(15.1)](#15.1)
|
||
|
||
invokes f when *set-cpo* is called
|
||
with sndr's result datums,
|
||
|
||
- [(15.2)](#15.2)
|
||
|
||
makes its completion dependent on
|
||
the completion of a sender returned by f, and
|
||
|
||
- [(15.3)](#15.3)
|
||
|
||
propagates the other completion operations sent by sndr[.](#15.sentence-2)
|