This commit is contained in:
2025-10-25 03:02:53 +03:00
commit 043225d523
3416 changed files with 681196 additions and 0 deletions

232
cppdraft/exec/let.md Normal file
View File

@@ -0,0 +1,232 @@
[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.3Sender concepts[exec.snd.concepts]") or
if decltype((f)) does not satisfy [*movable-value*](exec.general#concept:movable-value "33.1General[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.2Concept 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.2Exposition-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.2queryable 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.4Header <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.11Concept constructible_­from[concept.constructible]")<decay_t<Ts>, Ts> &&...)
- [(7.2)](#7.2)
[invocable](concept.invocable#concept:invocable "18.7.2Concept invocable[concept.invocable]")<LetFn, decay_t<Ts>&...>
- [(7.3)](#7.3)
[sender](exec.snd.concepts#concept:sender "33.9.3Sender 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.3Sender 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.1Preamble[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.2Concept 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.3Sender 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.3Asynchronous 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)