2143 lines
110 KiB
Markdown
2143 lines
110 KiB
Markdown
[exec.adapt]
|
||
|
||
# 33 Execution control library [[exec]](./#exec)
|
||
|
||
## 33.9 Senders [[exec.snd]](exec.snd#exec.adapt)
|
||
|
||
### 33.9.12 Sender adaptors [exec.adapt]
|
||
|
||
#### [33.9.12.1](#general) General [[exec.adapt.general]](exec.adapt.general)
|
||
|
||
[1](#general-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3002)
|
||
|
||
Subclause [exec.adapt] specifies a set of sender adaptors[.](#general-1.sentence-1)
|
||
|
||
[2](#general-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3005)
|
||
|
||
The bitwise inclusive or operator is overloaded
|
||
for the purpose of creating sender chains[.](#general-2.sentence-1)
|
||
|
||
The adaptors also support function call syntax with equivalent semantics[.](#general-2.sentence-2)
|
||
|
||
[3](#general-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3010)
|
||
|
||
Unless otherwise specified:
|
||
|
||
- [(3.1)](#general-3.1)
|
||
|
||
A sender adaptor is prohibited from causing observable effects,
|
||
apart from moving and copying its arguments,
|
||
before the returned sender is connected with a receiver using connect,
|
||
and start is called on the resulting operation state[.](#general-3.1.sentence-1)
|
||
|
||
- [(3.2)](#general-3.2)
|
||
|
||
A parent sender ([[exec.async.ops]](exec.async.ops "33.3 Asynchronous operations")) with a single child sender sndr has
|
||
an associated attribute object equal to*FWD-ENV*(get_env(sndr)) ([[exec.fwd.env]](exec.fwd.env "33.5.1 forwarding_query"))[.](#general-3.2.sentence-1)
|
||
|
||
- [(3.3)](#general-3.3)
|
||
|
||
A parent sender with more than one child sender has
|
||
an associated attributes object equal to env<>{}[.](#general-3.3.sentence-1)
|
||
|
||
- [(3.4)](#general-3.4)
|
||
|
||
When a parent sender is connected to a receiver rcvr,
|
||
any receiver used to connect a child sender has
|
||
an associated environment equal to *FWD-ENV*(get_env(rcvr))[.](#general-3.4.sentence-1)
|
||
|
||
- [(3.5)](#general-3.5)
|
||
|
||
An adaptor whose child senders are all non-dependent ([[exec.async.ops]](exec.async.ops "33.3 Asynchronous operations"))
|
||
is itself non-dependent[.](#general-3.5.sentence-1)
|
||
|
||
- [(3.6)](#general-3.6)
|
||
|
||
These requirements apply to any function
|
||
that is selected by the implementation of the sender adaptor[.](#general-3.6.sentence-1)
|
||
|
||
- [(3.7)](#general-3.7)
|
||
|
||
*Recommended practice*: Implementations should use
|
||
the completion signatures of the adaptors
|
||
to communicate type errors to users and
|
||
to propagate any such type errors from child senders[.](#general-3.7.sentence-1)
|
||
|
||
[4](#general-4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3043)
|
||
|
||
If a sender returned from a sender adaptor specified in [exec.adapt]
|
||
is specified to include set_error_t(Err) among its set of completion signatures
|
||
where decay_t<Err> denotes the type exception_ptr,
|
||
but the implementation does not potentially evaluate
|
||
an error completion operation with an exception_ptr argument,
|
||
the implementation is allowed to omit
|
||
the exception_ptr error completion signature from the set[.](#general-4.sentence-1)
|
||
|
||
#### [33.9.12.2](#obj) Closure objects [[exec.adapt.obj]](exec.adapt.obj)
|
||
|
||
[1](#obj-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3055)
|
||
|
||
A [*pipeable sender adaptor closure object*](#def:sender_adaptor_closure_object,pipeable "33.9.12.2 Closure objects [exec.adapt.obj]") is a function object
|
||
that accepts one or more [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") arguments and returns a [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]")[.](#obj-1.sentence-1)
|
||
|
||
For a pipeable sender adaptor closure object c and
|
||
an expression sndr such that decltype((sndr)) models [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"),
|
||
the following expressions are equivalent and yield a [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"):c(sndr) sndr | c
|
||
|
||
Given an additional pipeable sender adaptor closure object d,
|
||
the expression c | d produces
|
||
another pipeable sender adaptor closure object e:
|
||
|
||
e is a perfect forwarding call wrapper ([[func.require]](func.require "22.10.4 Requirements"))
|
||
with the following properties:
|
||
|
||
- [(1.1)](#obj-1.1)
|
||
|
||
Its target object is an object d2 of type decltype(auto(d)) direct-non-list-initialized with d[.](#obj-1.1.sentence-1)
|
||
|
||
- [(1.2)](#obj-1.2)
|
||
|
||
It has one bound argument entity,
|
||
an object c2 of type decltype(auto(c)) direct-non-list-initialized with c[.](#obj-1.2.sentence-1)
|
||
|
||
- [(1.3)](#obj-1.3)
|
||
|
||
Its call pattern is d2(c2(arg)),
|
||
where arg is the argument used in a function call expression of e[.](#obj-1.3.sentence-1)
|
||
|
||
The expression c | d is well-formed if and only if
|
||
the initializations of the state entities ([[func.def]](func.def "22.10.3 Definitions")) of e are all well-formed[.](#obj-1.sentence-5)
|
||
|
||
[2](#obj-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3088)
|
||
|
||
An object t of type T is
|
||
a pipeable sender adaptor closure object
|
||
if T models [derived_from](concept.derived#concept:derived_from "18.4.3 Concept derived_from [concept.derived]")<sender_adaptor_closure<T>>,T has no other base classes
|
||
of type sender_adaptor_closure<U> for any other type U, andT does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]")[.](#obj-2.sentence-1)
|
||
|
||
[3](#obj-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3096)
|
||
|
||
The template parameter D for sender_adaptor_closure can be
|
||
an incomplete type[.](#obj-3.sentence-1)
|
||
|
||
Before any expression of type cv D appears as
|
||
an operand to the | operator,D shall be complete and
|
||
model [derived_from](concept.derived#concept:derived_from "18.4.3 Concept derived_from [concept.derived]")<sender_adaptor_closure<D>>[.](#obj-3.sentence-2)
|
||
|
||
The behavior of an expression involving an object of type cv D as an operand to the | operator is undefined
|
||
if overload resolution selects a program-defined operator| function[.](#obj-3.sentence-3)
|
||
|
||
[4](#obj-4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3107)
|
||
|
||
A [*pipeable sender adaptor object*](#def:sender_adaptor_object,pipeable "33.9.12.2 Closure objects [exec.adapt.obj]") is a customization point object
|
||
that accepts a [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") as its first argument and
|
||
returns a [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]")[.](#obj-4.sentence-1)
|
||
|
||
If a pipeable sender adaptor object accepts only one argument,
|
||
then it is a pipeable sender adaptor closure object[.](#obj-4.sentence-2)
|
||
|
||
[5](#obj-5)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3114)
|
||
|
||
If a pipeable sender adaptor object adaptor accepts more than one argument,
|
||
then let sndr be an expression
|
||
such that decltype((sndr)) models [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"),
|
||
let args... be arguments
|
||
such that adaptor(sndr, args...) is a well-formed expression
|
||
as specified below, and
|
||
let BoundArgs be a pack that denotes decltype(auto(args))...[.](#obj-5.sentence-1)
|
||
|
||
The expression adaptor(args...) produces
|
||
a pipeable sender adaptor closure object f that is a perfect forwarding call wrapper with the following properties:
|
||
|
||
- [(5.1)](#obj-5.1)
|
||
|
||
Its target object is a copy of adaptor[.](#obj-5.1.sentence-1)
|
||
|
||
- [(5.2)](#obj-5.2)
|
||
|
||
Its bound argument entities bound_args consist of
|
||
objects of types BoundArgs... direct-non-list-initialized withstd::forward<decltype((args))>(args)..., respectively[.](#obj-5.2.sentence-1)
|
||
|
||
- [(5.3)](#obj-5.3)
|
||
|
||
Its call pattern is adaptor(rcvr, bound_args...),
|
||
where rcvr is
|
||
the argument used in a function call expression of f[.](#obj-5.3.sentence-1)
|
||
|
||
The expression adaptor(args...) is well-formed if and only if
|
||
the initializations of the bound argument entities of the result,
|
||
as specified above, are all well-formed[.](#obj-5.sentence-3)
|
||
|
||
#### [33.9.12.3](#exec.write.env) execution::write_env [[exec.write.env]](exec.write.env)
|
||
|
||
[1](#exec.write.env-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3143)
|
||
|
||
write_env is a sender adaptor
|
||
that accepts a sender and a queryable object, and
|
||
that returns a sender that,
|
||
when connected with a receiver rcvr,
|
||
connects the adapted sender with a receiver
|
||
whose execution environment is the result of
|
||
joining the [*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]") object
|
||
to the result of get_env(rcvr)[.](#exec.write.env-1.sentence-1)
|
||
|
||
[2](#exec.write.env-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3153)
|
||
|
||
write_env is a customization point object[.](#exec.write.env-2.sentence-1)
|
||
|
||
For some subexpressions sndr and env,
|
||
if decltype((sndr)) does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") or
|
||
if decltype((env)) does not satisfy [*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]"),
|
||
the expression write_env(sndr, env) is ill-formed[.](#exec.write.env-2.sentence-2)
|
||
|
||
Otherwise, it is expression-equivalent to*make-sender*(write_env, env, sndr)[.](#exec.write.env-2.sentence-3)
|
||
|
||
[3](#exec.write.env-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3162)
|
||
|
||
Let *write-env-t* denote the type decltype(auto(write_env))[.](#exec.write.env-3.sentence-1)
|
||
|
||
The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2 Exposition-only entities"))
|
||
is specialized for *write-env-t* as follows:
|
||
|
||
[ð](#lib:impls-for%3cwrite-env-t%3e)
|
||
|
||
template<>struct *impls-for*<*write-env-t*> : *default-impls* {static constexpr auto *join-env*(const auto& state, const auto& env) noexcept {return *see below*; }static constexpr auto *get-env* =[](auto, const auto& state, const auto& rcvr) noexcept {return *join-env*(state, *FWD-ENV*(get_env(rcvr))); }; template<class Sndr, class... Env>static consteval void *check-types*();};
|
||
|
||
[4](#exec.write.env-4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3184)
|
||
|
||
Invocation of*impls-for*<*write-env-t*>::*join-env* returns an object e such that
|
||
|
||
- [(4.1)](#exec.write.env-4.1)
|
||
|
||
decltype(e) models [*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]") and
|
||
|
||
- [(4.2)](#exec.write.env-4.2)
|
||
|
||
given a query object q,
|
||
the expression e.query(q) is expression-equivalent
|
||
to state.query(q) if that expression is valid,
|
||
otherwise, e.query(q) is expression-equivalent
|
||
to env.query(q)[.](#exec.write.env-4.sentence-1)
|
||
|
||
[5](#exec.write.env-5)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3199)
|
||
|
||
For a type Sndr and a pack of types Env,
|
||
let State be *data-type*<Sndr> and
|
||
let JoinEnv be the packdecltype(*join-env*(declval<State>(), *FWD-ENV*(declval<Env>())))[.](#exec.write.env-5.sentence-1)
|
||
|
||
Then *impls-for*<*write-env-t*>::*check-types*<Sndr, Env...>() is expression-equivalent toget_completion_signatures<*child-
|
||
type*<Sndr>, JoinEnv...>()[.](#exec.write.env-5.sentence-2)
|
||
|
||
#### [33.9.12.4](#exec.unstoppable) execution::unstoppable [[exec.unstoppable]](exec.unstoppable)
|
||
|
||
[1](#exec.unstoppable-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3210)
|
||
|
||
unstoppable is a sender adaptor
|
||
that connects its inner sender
|
||
with a receiver that has the execution environment of the outer receiver
|
||
but with an object of type never_stop_token as the result of the get_stop_token query[.](#exec.unstoppable-1.sentence-1)
|
||
|
||
[2](#exec.unstoppable-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3217)
|
||
|
||
For a subexpression sndr,unstoppable(sndr) is expression-equivalent towrite_env(sndr, prop(get_stop_token, never_stop_token{}))[.](#exec.unstoppable-2.sentence-1)
|
||
|
||
#### [33.9.12.5](#exec.starts.on) execution::starts_on [[exec.starts.on]](exec.starts.on)
|
||
|
||
[1](#exec.starts.on-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3224)
|
||
|
||
starts_on adapts an input sender into a sender
|
||
that will start on an execution agent belonging to
|
||
a particular scheduler's associated execution resource[.](#exec.starts.on-1.sentence-1)
|
||
|
||
[2](#exec.starts.on-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3229)
|
||
|
||
The name starts_on denotes a customization point object[.](#exec.starts.on-2.sentence-1)
|
||
|
||
For subexpressions sch and sndr,
|
||
if decltype((
|
||
sch)) does not satisfy [scheduler](exec.sched#concept:scheduler "33.6 Schedulers [exec.sched]"), ordecltype((sndr)) does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"),starts_on(sch, sndr) is ill-formed[.](#exec.starts.on-2.sentence-2)
|
||
|
||
[3](#exec.starts.on-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3236)
|
||
|
||
Otherwise,
|
||
the expression starts_on(sch, sndr) is expression-equivalent to:transform_sender(*query-with-default*(get_domain, sch, default_domain()), *make-sender*(starts_on, sch, sndr)) except that sch is evaluated only once[.](#exec.starts.on-3.sentence-1)
|
||
|
||
[4](#exec.starts.on-4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3246)
|
||
|
||
Let out_sndr and env be subexpressions
|
||
such that OutSndr is decltype((out_sndr))[.](#exec.starts.on-4.sentence-1)
|
||
|
||
If [*sender-for*](exec.snd.concepts#concept:sender-for "33.9.3 Sender concepts [exec.snd.concepts]")<OutSndr, starts_on_t> is false,
|
||
then the expressions starts_on.transform_env(out_sndr, env) andstarts_on.transform_sender(out_sndr, env) are ill-formed; otherwise
|
||
|
||
- [(4.1)](#exec.starts.on-4.1)
|
||
|
||
starts_on.transform_env(out_sndr, env) is equivalent to:auto&& [_, sch, _] = out_sndr;return *JOIN-ENV*(*SCHED-ENV*(sch), *FWD-ENV*(env));
|
||
|
||
- [(4.2)](#exec.starts.on-4.2)
|
||
|
||
starts_on.transform_sender(out_sndr, env) is equivalent to:auto&& [_, sch, sndr] = out_sndr;return let_value( schedule(sch), [sndr = std::forward_like<OutSndr>(sndr)]() mutablenoexcept(is_nothrow_move_constructible_v<decay_t<OutSndr>>) {return std::move(sndr); });
|
||
|
||
[5](#exec.starts.on-5)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3272)
|
||
|
||
Let out_sndr be a subexpression denoting
|
||
a sender returned from starts_on(sch, sndr) or one equal to such, and
|
||
let OutSndr be the type decltype((out_sndr))[.](#exec.starts.on-5.sentence-1)
|
||
|
||
Let out_rcvr be a subexpression denoting a receiver
|
||
that has an environment of type Env such that [sender_in](exec.snd.concepts#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]")<OutSndr, Env> is true[.](#exec.starts.on-5.sentence-2)
|
||
|
||
Let op be an lvalue referring to the operation state
|
||
that results from connecting out_sndr with out_rcvr[.](#exec.starts.on-5.sentence-3)
|
||
|
||
Calling start(op) shall start sndr on an execution agent of the associated execution resource of sch[.](#exec.starts.on-5.sentence-4)
|
||
|
||
If scheduling onto sch fails,
|
||
an error completion on out_rcvr shall be executed
|
||
on an unspecified execution agent[.](#exec.starts.on-5.sentence-5)
|
||
|
||
#### [33.9.12.6](#exec.continues.on) execution::continues_on [[exec.continues.on]](exec.continues.on)
|
||
|
||
[1](#exec.continues.on-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3289)
|
||
|
||
continues_on adapts a sender into one
|
||
that completes on the specified scheduler[.](#exec.continues.on-1.sentence-1)
|
||
|
||
[2](#exec.continues.on-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3293)
|
||
|
||
The name continues_on denotes a pipeable sender adaptor object[.](#exec.continues.on-2.sentence-1)
|
||
|
||
For subexpressions sch and sndr,
|
||
if decltype((sch)) does not satisfy [scheduler](exec.sched#concept:scheduler "33.6 Schedulers [exec.sched]"), ordecltype((sndr)) does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"),continues_on(sndr, sch) is ill-formed[.](#exec.continues.on-2.sentence-2)
|
||
|
||
[3](#exec.continues.on-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3300)
|
||
|
||
Otherwise,
|
||
the expression continues_on(sndr, sch) is expression-equivalent to:transform_sender(*get-domain-early*(sndr), *make-sender*(continues_on, sch, sndr)) except that sndr is evaluated only once[.](#exec.continues.on-3.sentence-1)
|
||
|
||
[4](#exec.continues.on-4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3308)
|
||
|
||
The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2 Exposition-only entities"))
|
||
is specialized for continues_on_t as follows:namespace std::execution {template<>struct *impls-for*<continues_on_t> : *default-impls* {static constexpr auto *get-attrs* =[](const auto& data, const auto& child) noexcept -> decltype(auto) {return *JOIN-ENV*(*SCHED-ATTRS*(data), *FWD-ENV*(get_env(child))); }; };}
|
||
|
||
[5](#exec.continues.on-5)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3323)
|
||
|
||
Let sndr and env be subexpressions
|
||
such that Sndr is decltype((sndr))[.](#exec.continues.on-5.sentence-1)
|
||
|
||
If [*sender-for*](exec.snd.concepts#concept:sender-for "33.9.3 Sender concepts [exec.snd.concepts]")<Sndr, continues_on_t> is false,
|
||
then
|
||
the expression continues_on.transform_sender(sndr, env) is ill-formed;
|
||
otherwise, it is equal to:auto [_, data, child] = sndr;return schedule_from(std::move(data), std::move(child));
|
||
|
||
[*Note [1](#exec.continues.on-note-1)*:
|
||
|
||
This causes the continues_on(sndr, sch) sender to becomeschedule_from(sch, sndr) when it is connected with a receiver
|
||
whose execution domain does not customize continues_on[.](#exec.continues.on-5.sentence-2)
|
||
|
||
â *end note*]
|
||
|
||
[6](#exec.continues.on-6)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3340)
|
||
|
||
Let out_sndr be a subexpression denoting
|
||
a sender returned from continues_on(sndr, sch) or one equal to such, and
|
||
let OutSndr be the type decltype((out_sndr))[.](#exec.continues.on-6.sentence-1)
|
||
|
||
Let out_rcvr be a subexpression denoting a receiver
|
||
that has an environment of type Env such that [sender_in](exec.snd.concepts#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]")<OutSndr, Env> is true[.](#exec.continues.on-6.sentence-2)
|
||
|
||
Let op be an lvalue referring to the operation state
|
||
that results from connecting out_sndr with out_rcvr[.](#exec.continues.on-6.sentence-3)
|
||
|
||
Calling start(op) shall
|
||
start sndr on the current execution agent and
|
||
execute completion operations on out_rcvr on an execution agent of the execution resource associated with sch[.](#exec.continues.on-6.sentence-4)
|
||
|
||
If scheduling onto sch fails,
|
||
an error completion on out_rcvr shall be executed
|
||
on an unspecified execution agent[.](#exec.continues.on-6.sentence-5)
|
||
|
||
#### [33.9.12.7](#exec.schedule.from) execution::schedule_from [[exec.schedule.from]](exec.schedule.from)
|
||
|
||
[1](#exec.schedule.from-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3359)
|
||
|
||
schedule_from schedules work dependent on the completion of a sender
|
||
onto a scheduler's associated execution resource[.](#exec.schedule.from-1.sentence-1)
|
||
|
||
[*Note [1](#exec.schedule.from-note-1)*:
|
||
|
||
schedule_from is not meant to be used in user code;
|
||
it is used in the implementation of continues_on[.](#exec.schedule.from-1.sentence-2)
|
||
|
||
â *end note*]
|
||
|
||
[2](#exec.schedule.from-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3367)
|
||
|
||
The name schedule_from denotes a customization point object[.](#exec.schedule.from-2.sentence-1)
|
||
|
||
For some subexpressions sch and sndr,
|
||
let Sch be decltype((sch)) andSndr be decltype((sndr))[.](#exec.schedule.from-2.sentence-2)
|
||
|
||
If Sch does not satisfy [scheduler](exec.sched#concept:scheduler "33.6 Schedulers [exec.sched]"), orSndr does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"),schedule_from(sch, sndr) is ill-formed[.](#exec.schedule.from-2.sentence-3)
|
||
|
||
[3](#exec.schedule.from-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3376)
|
||
|
||
Otherwise,
|
||
the expression schedule_from(sch, sndr) is expression-equivalent to:transform_sender(*query-with-default*(get_domain, sch, default_domain()), *make-sender*(schedule_from, sch, sndr)) except that sch is evaluated only once[.](#exec.schedule.from-3.sentence-1)
|
||
|
||
[4](#exec.schedule.from-4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3386)
|
||
|
||
The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2 Exposition-only entities"))
|
||
is specialized for schedule_from_t as follows:
|
||
|
||
[ð](#lib:impls-for%3cschedule_from_t%3e)
|
||
|
||
namespace std::execution {template<>struct *impls-for*<schedule_from_t> : *default-impls* {static constexpr auto *get-attrs* = *see below*; static constexpr auto *get-state* = *see below*; static constexpr auto *complete* = *see below*; template<class Sndr, class... Env>static consteval void *check-types*(); };}
|
||
|
||
[5](#exec.schedule.from-5)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3404)
|
||
|
||
The member *impls-for*<schedule_from_t>::*get-attrs* is initialized with a callable object equivalent to the following lambda:[](const auto& data, const auto& child) noexcept -> decltype(auto) {return *JOIN-ENV*(*SCHED-ATTRS*(data), *FWD-ENV*(get_env(child)));}
|
||
|
||
[6](#exec.schedule.from-6)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3413)
|
||
|
||
The member *impls-for*<schedule_from_t>::*get-state* is initialized with a callable object equivalent to the following lambda:[]<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept(*see below*)requires [sender_in](exec.snd.concepts#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]")<*child-type*<Sndr>, *FWD-ENV-T*(env_of_t<Rcvr>)> {auto& [_, sch, child] = sndr; using sched_t = decltype(auto(sch)); using variant_t = *see below*; using receiver_t = *see below*; using operation_t = connect_result_t<schedule_result_t<sched_t>, receiver_t>; constexpr bool nothrow = noexcept(connect(schedule(sch), receiver_t{nullptr})); struct *state-type* { Rcvr& *rcvr*; // *exposition only* variant_t *async-result*; // *exposition only* operation_t *op-state*; // *exposition only*explicit *state-type*(sched_t sch, Rcvr& rcvr) noexcept(nothrow): *rcvr*(rcvr), *op-state*(connect(schedule(sch), receiver_t{this})) {}}; return *state-type*{sch, rcvr};}
|
||
|
||
[ð](#lib:check-types,impls-for%3cschedule_from_t%3e)
|
||
|
||
`template<class Sndr, class... Env>
|
||
static consteval void check-types();
|
||
`
|
||
|
||
[7](#exec.schedule.from-7)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3448)
|
||
|
||
*Effects*: Equivalent to:get_completion_signatures<schedule_result_t<*data-type*<Sndr>>, *FWD-ENV-T*(Env)...>();auto cs = get_completion_signatures<*child-type*<Sndr>, *FWD-ENV-T*(Env)...>();*decay-copyable-result-datums*(cs); // see [[exec.snd.expos]](exec.snd.expos "33.9.2 Exposition-only entities")
|
||
|
||
[8](#exec.schedule.from-8)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3458)
|
||
|
||
Objects of the local class *state-type* can be used
|
||
to initialize a structured binding[.](#exec.schedule.from-8.sentence-1)
|
||
|
||
[9](#exec.schedule.from-9)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3462)
|
||
|
||
Let Sigs be
|
||
a pack of the arguments to the completion_signatures specialization
|
||
named by completion_signatures_of_t<*child-type*<Sndr>, *FWD-ENV-T*(env_of_t<Rcvr>)>[.](#exec.schedule.from-9.sentence-1)
|
||
|
||
Let *as-tuple* be an alias template such that*as-tuple*<Tag(Args...)> denotes
|
||
the type *decayed-tuple*<Tag, Args...>, and
|
||
let *is-nothrow-decay-copy-sig* be a variable template such thatauto(*is-nothrow-decay-copy-sig*<Tag(Args...
|
||
)>) is
|
||
a constant expression of type bool and
|
||
equal to (is_nothrow_constructible_v<decay_t<Args>, Args> && ...)[.](#exec.schedule.from-9.sentence-2)
|
||
|
||
Let *error-completion* be a pack consisting of
|
||
the type set_error_t(exception_ptr) if (*is-nothrow-decay-copy-sig*<Sigs> &&...) is false,
|
||
and an empty pack otherwise[.](#exec.schedule.from-9.sentence-3)
|
||
|
||
Then variant_t denotes
|
||
the type variant<monostate, *as-tuple*<Sigs>..., *error-completion*...>,
|
||
except with duplicate types removed[.](#exec.schedule.from-9.sentence-4)
|
||
|
||
[10](#exec.schedule.from-10)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3481)
|
||
|
||
receiver_t is an alias for the following exposition-only class:namespace std::execution {struct *receiver-type* {using receiver_concept = receiver_t; *state-type** *state*; // *exposition only*void set_value() && noexcept { visit([this]<class Tuple>(Tuple& result) noexcept -> void {if constexpr (<monostate, Tuple>) {auto& [tag, ...args] = result;
|
||
tag(std::move(*state*->*rcvr*), std::move(args)...); }}, *state*->*async-result*); }template<class Error>void set_error(Error&& err) && noexcept { execution::set_error(std::move(*state*->*rcvr*), std::forward<Error>(err)); }void set_stopped() && noexcept { execution::set_stopped(std::move(*state*->*rcvr*)); }decltype(auto) get_env() const noexcept {return *FWD-ENV*(execution::get_env(*state*->*rcvr*)); }};}
|
||
|
||
[11](#exec.schedule.from-11)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3516)
|
||
|
||
The expression in the noexcept clause of the lambda is true if the construction of the returned *state-type* object
|
||
is not potentially throwing;
|
||
otherwise, false[.](#exec.schedule.from-11.sentence-1)
|
||
|
||
[12](#exec.schedule.from-12)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3522)
|
||
|
||
The member *impls-for*<schedule_from_t>::*complete* is initialized with a callable object equivalent to the following lambda:[]<class Tag, class... Args>(auto, auto& state, auto& rcvr, Tag, Args&&... args) noexcept-> void {using result_t = *decayed-tuple*<Tag, Args...>; constexpr bool nothrow = (is_nothrow_constructible_v<decay_t<Args>, Args> && ...); try { state.*async-result*.template emplace<result_t>(Tag(), std::forward<Args>(args)...); } catch (...) {if constexpr (!nothrow) state.*async-result*.template emplace<tuple<set_error_t,
|
||
exception_ptr>>(set_error, current_exception()); } start(state.*op-state*);};
|
||
|
||
[13](#exec.schedule.from-13)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3542)
|
||
|
||
Let out_sndr be a subexpression denoting
|
||
a sender returned from schedule_from(sch, sndr) or one equal to such,
|
||
and let OutSndr be the type decltype((out_sndr))[.](#exec.schedule.from-13.sentence-1)
|
||
|
||
Let out_rcvr be a subexpression denoting a receiver
|
||
that has an environment of type Env such that [sender_in](exec.snd.concepts#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]")<OutSndr, Env> is true[.](#exec.schedule.from-13.sentence-2)
|
||
|
||
Let op be an lvalue referring to the operation state
|
||
that results from connecting out_sndr with out_rcvr[.](#exec.schedule.from-13.sentence-3)
|
||
|
||
Calling start(op) shall
|
||
start sndr on the current execution agent and
|
||
execute completion operations on out_rcvr on an execution agent of the execution resource associated with sch[.](#exec.schedule.from-13.sentence-4)
|
||
|
||
If scheduling onto sch fails,
|
||
an error completion on out_rcvr shall be executed
|
||
on an unspecified execution agent[.](#exec.schedule.from-13.sentence-5)
|
||
|
||
#### [33.9.12.8](#exec.on) execution::on [[exec.on]](exec.on)
|
||
|
||
[1](#exec.on-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3561)
|
||
|
||
The on sender adaptor has two forms:
|
||
|
||
- [(1.1)](#exec.on-1.1)
|
||
|
||
on(sch, sndr),
|
||
which starts a sender sndr on an execution agent
|
||
belonging to a scheduler sch's associated execution resource and
|
||
that, upon sndr's completion,
|
||
transfers execution back to the execution resource
|
||
on which the on sender was started[.](#exec.on-1.1.sentence-1)
|
||
|
||
- [(1.2)](#exec.on-1.2)
|
||
|
||
on(sndr, sch, closure),
|
||
which upon completion of a sender sndr,
|
||
transfers execution to an execution agent
|
||
belonging to a scheduler sch's associated execution resource,
|
||
then executes a sender adaptor closure closure with the async results of the sender, and
|
||
that then transfers execution back to the execution resource
|
||
on which sndr completed[.](#exec.on-1.2.sentence-1)
|
||
|
||
[2](#exec.on-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3582)
|
||
|
||
The name on denotes a pipeable sender adaptor object[.](#exec.on-2.sentence-1)
|
||
|
||
For subexpressions sch and sndr,on(sch, sndr) is ill-formed if any of the following is true:
|
||
|
||
- [(2.1)](#exec.on-2.1)
|
||
|
||
decltype((sch)) does not satisfy [scheduler](exec.sched#concept:scheduler "33.6 Schedulers [exec.sched]"), or
|
||
|
||
- [(2.2)](#exec.on-2.2)
|
||
|
||
decltype((sndr)) does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") andsndr is not
|
||
a pipeable sender adaptor closure object ([[exec.adapt.obj]](#obj "33.9.12.2 Closure objects")), or
|
||
|
||
- [(2.3)](#exec.on-2.3)
|
||
|
||
decltype((sndr)) satisfies [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") andsndr is also a pipeable sender adaptor closure object[.](#exec.on-2.sentence-2)
|
||
|
||
[3](#exec.on-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3598)
|
||
|
||
Otherwise, if decltype((sndr)) satisfies [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"),
|
||
the expression on(sch, sndr) is expression-equivalent to:transform_sender(*query-with-default*(get_domain, sch, default_domain()), *make-sender*(on, sch, sndr)) except that sch is evaluated only once[.](#exec.on-3.sentence-1)
|
||
|
||
[4](#exec.on-4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3608)
|
||
|
||
For subexpressions sndr, sch, and closure, if
|
||
|
||
- [(4.1)](#exec.on-4.1)
|
||
|
||
decltype((sch)) does not satisfy [scheduler](exec.sched#concept:scheduler "33.6 Schedulers [exec.sched]"), or
|
||
|
||
- [(4.2)](#exec.on-4.2)
|
||
|
||
decltype((sndr)) does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"), or
|
||
|
||
- [(4.3)](#exec.on-4.3)
|
||
|
||
closure is not a pipeable sender adaptor closure object ([[exec.adapt.obj]](#obj "33.9.12.2 Closure objects")),
|
||
|
||
the expression on(sndr, sch, closure) is ill-formed;
|
||
otherwise, it is expression-equivalent to:transform_sender(*get-domain-early*(sndr), *make-sender*(on, *product-type*{sch, closure}, sndr)) except that sndr is evaluated only once[.](#exec.on-4.sentence-1)
|
||
|
||
[5](#exec.on-5)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3627)
|
||
|
||
Let out_sndr and env be subexpressions,
|
||
let OutSndr be decltype((out_sndr)), and
|
||
let Env be decltype((env))[.](#exec.on-5.sentence-1)
|
||
|
||
If [*sender-for*](exec.snd.concepts#concept:sender-for "33.9.3 Sender concepts [exec.snd.concepts]")<OutSndr, on_t> is false,
|
||
then the expressions on.transform_env(out_sndr, env) andon.transform_sender(out_sndr, env) are ill-formed[.](#exec.on-5.sentence-2)
|
||
|
||
[6](#exec.on-6)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3635)
|
||
|
||
Otherwise:
|
||
Let *not-a-scheduler* be an unspecified empty class type[.](#exec.on-6.sentence-1)
|
||
|
||
[7](#exec.on-7)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3639)
|
||
|
||
The expression on.transform_env(out_sndr, env) has effects equivalent to:auto&& [_, data, _] = out_sndr;if constexpr ([scheduler](exec.sched#concept:scheduler "33.6 Schedulers [exec.sched]")<decltype(data)>) {return *JOIN-ENV*(*SCHED-ENV*(std::forward_like<OutSndr>(data)), *FWD-ENV*(std::forward<Env>(env)));} else {return std::forward<Env>(env);}
|
||
|
||
[8](#exec.on-8)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3651)
|
||
|
||
The expression on.transform_sender(out_sndr, env) has effects equivalent to:auto&& [_, data, child] = out_sndr;if constexpr ([scheduler](exec.sched#concept:scheduler "33.6 Schedulers [exec.sched]")<decltype(data)>) {auto orig_sch =*query-with-default*(get_scheduler, env, *not-a-scheduler*()); if constexpr ([same_as](concept.same#concept:same_as "18.4.2 Concept same_as [concept.same]")<decltype(orig_sch), *not-a-scheduler*>) {return *not-a-sender*{}; } else {return continues_on( starts_on(std::forward_like<OutSndr>(data), std::forward_like<OutSndr>(child)),
|
||
std::move(orig_sch)); }} else {auto& [sch, closure] = data; auto orig_sch = *query-with-default*( get_completion_scheduler<set_value_t>,
|
||
get_env(child), *query-with-default*(get_scheduler, env, *not-a-scheduler*())); if constexpr ([same_as](concept.same#concept:same_as "18.4.2 Concept same_as [concept.same]")<decltype(orig_sch), *not-a-scheduler*>) {return *not-a-sender*{}; } else {return write_env( continues_on( std::forward_like<OutSndr>(closure)( continues_on( write_env(std::forward_like<OutSndr>(child), *SCHED-ENV*(orig_sch)),
|
||
sch)),
|
||
orig_sch), *SCHED-ENV*(sch)); }}
|
||
|
||
[9](#exec.on-9)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3689)
|
||
|
||
Let out_sndr be a subexpression denoting
|
||
a sender returned from on(sch, sndr) or one equal to such, and
|
||
let OutSndr be the type decltype((out_sndr))[.](#exec.on-9.sentence-1)
|
||
|
||
Let out_rcvr be a subexpression denoting a receiver
|
||
that has an environment of type Env such that [sender_in](exec.snd.concepts#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]")<OutSndr, Env> is true[.](#exec.on-9.sentence-2)
|
||
|
||
Let op be an lvalue referring to the operation state
|
||
that results from connecting out_sndr with out_rcvr[.](#exec.on-9.sentence-3)
|
||
|
||
Calling start(op) shall
|
||
|
||
- [(9.1)](#exec.on-9.1)
|
||
|
||
remember the current scheduler, get_scheduler(get_env(rcvr));
|
||
|
||
- [(9.2)](#exec.on-9.2)
|
||
|
||
start sndr on an execution agent belonging tosch's associated execution resource;
|
||
|
||
- [(9.3)](#exec.on-9.3)
|
||
|
||
upon sndr's completion,
|
||
transfer execution back to the execution resource
|
||
associated with the scheduler remembered in step 1; and
|
||
|
||
- [(9.4)](#exec.on-9.4)
|
||
|
||
forward sndr's async result to out_rcvr[.](#exec.on-9.sentence-4)
|
||
|
||
If any scheduling operation fails,
|
||
an error completion on out_rcvr shall be executed
|
||
on an unspecified execution agent[.](#exec.on-9.sentence-5)
|
||
|
||
[10](#exec.on-10)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3716)
|
||
|
||
Let out_sndr be a subexpression denoting
|
||
a sender returned from on(sndr, sch, closure) or one equal to such, and
|
||
let OutSndr be the type decltype((out_sndr))[.](#exec.on-10.sentence-1)
|
||
|
||
Let out_rcvr be a subexpression denoting a receiver
|
||
that has an environment of type Env such that [sender_in](exec.snd.concepts#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]")<OutSndr, Env> is true[.](#exec.on-10.sentence-2)
|
||
|
||
Let op be an lvalue referring to the operation state
|
||
that results from connecting out_sndr with out_rcvr[.](#exec.on-10.sentence-3)
|
||
|
||
Calling start(op) shall
|
||
|
||
- [(10.1)](#exec.on-10.1)
|
||
|
||
remember the current scheduler,
|
||
which is the first of the following expressions that is well-formed:
|
||
* [(10.1.1)](#exec.on-10.1.1)
|
||
|
||
get_completion_scheduler<set_value_t>(get_env(sndr))
|
||
|
||
* [(10.1.2)](#exec.on-10.1.2)
|
||
|
||
get_scheduler(get_env(rcvr));
|
||
|
||
- [(10.2)](#exec.on-10.2)
|
||
|
||
start sndr on the current execution agent;
|
||
|
||
- [(10.3)](#exec.on-10.3)
|
||
|
||
upon sndr's completion,
|
||
transfer execution to an agent
|
||
owned by sch's associated execution resource;
|
||
|
||
- [(10.4)](#exec.on-10.4)
|
||
|
||
forward sndr's async result as if by
|
||
connecting and starting a sender closure(S),
|
||
where S is a sender
|
||
that completes synchronously with sndr's async result; and
|
||
|
||
- [(10.5)](#exec.on-10.5)
|
||
|
||
upon completion of the operation started in the previous step,
|
||
transfer execution back to the execution resource
|
||
associated with the scheduler remembered in step 1 and
|
||
forward the operation's async result to out_rcvr[.](#exec.on-10.sentence-4)
|
||
|
||
If any scheduling operation fails,
|
||
an error completion on out_rcvr shall be executed on
|
||
an unspecified execution agent[.](#exec.on-10.sentence-5)
|
||
|
||
#### [33.9.12.9](#exec.then) execution::then, execution::upon_error, execution::upon_stopped [[exec.then]](exec.then)
|
||
|
||
[1](#exec.then-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3757)
|
||
|
||
then attaches an invocable as a continuation
|
||
for an input sender's value completion operation[.](#exec.then-1.sentence-1)
|
||
|
||
upon_error and upon_stopped do the same
|
||
for the error and stopped completion operations, respectively,
|
||
sending the result of the invocable as a value completion[.](#exec.then-1.sentence-2)
|
||
|
||
[2](#exec.then-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3764)
|
||
|
||
The names then, upon_error, and upon_stopped denote pipeable sender adaptor objects[.](#exec.then-2.sentence-1)
|
||
|
||
Let the expression *then-cpo* be one ofthen, upon_error, or upon_stopped[.](#exec.then-2.sentence-2)
|
||
|
||
For subexpressions sndr and f,
|
||
if decltype((sndr)) does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"), ordecltype((f)) does not satisfy [*movable-value*](exec.general#concept:movable-value "33.1 General [exec.general]"),*then-cpo*(sndr, f) is ill-formed[.](#exec.then-2.sentence-3)
|
||
|
||
[3](#exec.then-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3774)
|
||
|
||
Otherwise,
|
||
the expression *then-cpo*(sndr, f) is expression-equivalent to:transform_sender(*get-domain-early*(sndr), *make-sender*(*then-cpo*, f, sndr)) except that sndr is evaluated only once[.](#exec.then-3.sentence-1)
|
||
|
||
[4](#exec.then-4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3782)
|
||
|
||
For then, upon_error, and upon_stopped,
|
||
let *set-cpo* beset_value, set_error, and set_stopped, respectively[.](#exec.then-4.sentence-1)
|
||
|
||
The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2 Exposition-only entities"))
|
||
is specialized for *then-cpo* as follows:
|
||
|
||
[ð](#lib:impls-for%3cdecayed-typeof%3cthen-cpo%3e%3e)
|
||
|
||
namespace std::execution {template<>struct *impls-for*<*decayed-typeof*<*then-cpo*>> : *default-impls* {static constexpr auto *complete* =[]<class Tag, class... Args>(auto, auto& fn, 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-SET-VALUE*(rcvr,
|
||
invoke(std::move(fn), std::forward<Args>(args)...)); } else { Tag()(std::move(rcvr), std::forward<Args>(args)...); }}; template<class Sndr, class... Env>static consteval void *check-types*(); };}
|
||
|
||
[ð](#lib:check-types,impls-for%3cdecayed-typeof%3cthen-cpo%3e%3e)
|
||
|
||
`template<class Sndr, class... Env>
|
||
static consteval void check-types();
|
||
`
|
||
|
||
[5](#exec.then-5)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3817)
|
||
|
||
*Effects*: Equivalent to:auto cs = get_completion_signatures<*child-type*<Sndr>, *FWD-ENV-T*(Env)...>();auto fn = []<class... Ts>(set_value_t(*)(Ts...)) {if constexpr (<remove_cvref_t<*data-type*<Sndr>>, Ts...>)throw *unspecified-exception*();};
|
||
cs.*for-each*(*overload-set*{fn, [](auto){}}); where *unspecified-exception* is
|
||
a type derived from exception[.](#exec.then-5.sentence-1)
|
||
|
||
[6](#exec.then-6)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3832)
|
||
|
||
The expression *then-cpo*(sndr, f) has undefined behavior
|
||
unless it returns a sender out_sndr that
|
||
|
||
- [(6.1)](#exec.then-6.1)
|
||
|
||
invokes f or a copy of such
|
||
with the value, error, or stopped result datums of sndr for then, upon_error, and upon_stopped, respectively,
|
||
using the result value of f as out_sndr's value completion, and
|
||
|
||
- [(6.2)](#exec.then-6.2)
|
||
|
||
forwards all other completion operations unchanged[.](#exec.then-6.sentence-1)
|
||
|
||
#### [33.9.12.10](#exec.let) execution::let_value, execution::let_error, execution::let_stopped [[exec.let]](exec.let)
|
||
|
||
[1](#exec.let-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[.](#exec.let-1.sentence-1)
|
||
|
||
[2](#exec.let-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[.](#exec.let-2.sentence-1)
|
||
|
||
Let the expression *let-cpo* be one oflet_value, let_error, or let_stopped[.](#exec.let-2.sentence-2)
|
||
|
||
For a subexpression sndr,
|
||
let *let-env*(sndr) be expression-equivalent to
|
||
the first well-formed expression below:
|
||
|
||
- [(2.1)](#exec.let-2.1)
|
||
|
||
*SCHED-ENV*(get_completion_scheduler<*decayed-typeof*<*set-cpo*>>(get_env(sndr)))
|
||
|
||
- [(2.2)](#exec.let-2.2)
|
||
|
||
*MAKE-ENV*(get_domain, get_domain(get_env(sndr)))
|
||
|
||
- [(2.3)](#exec.let-2.3)
|
||
|
||
(void(sndr), env<>{})
|
||
|
||
[3](#exec.let-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[.](#exec.let-3.sentence-1)
|
||
|
||
For subexpressions sndr and f,
|
||
let F be the decayed type of f[.](#exec.let-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[.](#exec.let-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[.](#exec.let-3.sentence-4)
|
||
|
||
[4](#exec.let-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[.](#exec.let-4.sentence-1)
|
||
|
||
[5](#exec.let-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](#exec.let-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)](#exec.let-6.1)
|
||
|
||
decltype(e) models [*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]") and
|
||
|
||
- [(6.2)](#exec.let-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[.](#exec.let-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](#exec.let-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)](#exec.let-7.1)
|
||
|
||
([constructible_from](concept.constructible#concept:constructible_from "18.4.11 Concept constructible_from [concept.constructible]")<decay_t<Ts>, Ts> &&...)
|
||
|
||
- [(7.2)](#exec.let-7.2)
|
||
|
||
[invocable](concept.invocable#concept:invocable "18.7.2 Concept invocable [concept.invocable]")<LetFn, decay_t<Ts>&...>
|
||
|
||
- [(7.3)](#exec.let-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)](#exec.let-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>()))[.](#exec.let-7.sentence-1)
|
||
|
||
[8](#exec.let-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](#exec.let-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>)>[.](#exec.let-9.sentence-1)
|
||
|
||
Let LetSigs be a pack of those types in Sigs with a return type of *decayed-typeof*<*set-cpo*>[.](#exec.let-9.sentence-2)
|
||
|
||
Let *as-tuple* be an alias template
|
||
such that *as-tuple*<Tag(Args...)> denotes
|
||
the type *decayed-tuple*<Args...>[.](#exec.let-9.sentence-3)
|
||
|
||
Then args_variant_t denotes
|
||
the type variant<monostate, *as-tuple*<LetSigs>...> except with duplicate types removed[.](#exec.let-9.sentence-4)
|
||
|
||
[10](#exec.let-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>&...>[.](#exec.let-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[.](#exec.let-10.sentence-2)
|
||
|
||
[11](#exec.let-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[.](#exec.let-11.sentence-1)
|
||
|
||
[12](#exec.let-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](#exec.let-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](#exec.let-14)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4073)
|
||
|
||
Let sndr and env be subexpressions, and
|
||
let Sndr be decltype((sndr))[.](#exec.let-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[.](#exec.let-14.sentence-2)
|
||
|
||
Otherwise, it is equal to:auto& [_, _, child] = sndr;return *JOIN-ENV*(*let-env*(child), *FWD-ENV*(env));
|
||
|
||
[15](#exec.let-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[.](#exec.let-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)](#exec.let-15.1)
|
||
|
||
invokes f when *set-cpo* is called
|
||
with sndr's result datums,
|
||
|
||
- [(15.2)](#exec.let-15.2)
|
||
|
||
makes its completion dependent on
|
||
the completion of a sender returned by f, and
|
||
|
||
- [(15.3)](#exec.let-15.3)
|
||
|
||
propagates the other completion operations sent by sndr[.](#exec.let-15.sentence-2)
|
||
|
||
#### [33.9.12.11](#exec.bulk) execution::bulk, execution::bulk_chunked, and execution::bulk_unchunked [[exec.bulk]](exec.bulk)
|
||
|
||
[1](#exec.bulk-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4109)
|
||
|
||
bulk, bulk_chunked, and bulk_unchunked run a task repeatedly for every index in an index space[.](#exec.bulk-1.sentence-1)
|
||
|
||
[2](#exec.bulk-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4113)
|
||
|
||
The names bulk, bulk_chunked, and bulk_unchunked denote pipeable sender adaptor objects[.](#exec.bulk-2.sentence-1)
|
||
|
||
Let *bulk-algo* be eitherbulk, bulk_chunked, or bulk_unchunked[.](#exec.bulk-2.sentence-2)
|
||
|
||
For subexpressions sndr, policy, shape, and f,
|
||
letPolicy be remove_cvref_t<decltype(policy)>,Shape be decltype(auto(shape)), andFunc be decay_t<decltype((f))>[.](#exec.bulk-2.sentence-3)
|
||
|
||
If
|
||
|
||
- [(2.1)](#exec.bulk-2.1)
|
||
|
||
decltype((sndr)) does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"), or
|
||
|
||
- [(2.2)](#exec.bulk-2.2)
|
||
|
||
is_execution_policy_v<Policy> is false, or
|
||
|
||
- [(2.3)](#exec.bulk-2.3)
|
||
|
||
Shape does not satisfy [integral](concepts.arithmetic#concept:integral "18.4.7 Arithmetic concepts [concepts.arithmetic]"), or
|
||
|
||
- [(2.4)](#exec.bulk-2.4)
|
||
|
||
Func does not model [copy_constructible](concept.copyconstructible#concept:copy_constructible "18.4.14 Concept copy_constructible [concept.copyconstructible]"),
|
||
|
||
*bulk-algo*(sndr, policy, shape, f) is ill-formed[.](#exec.bulk-2.sentence-4)
|
||
|
||
[3](#exec.bulk-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4136)
|
||
|
||
Otherwise,
|
||
the expression *bulk-algo*(sndr, policy, shape, f) is expression-equivalent to:
|
||
|
||
transform_sender(*get-domain-early*(sndr), *make-sender*(*bulk-algo*, *product-type*<*see below*, Shape, Func>{policy, shape, f}, sndr)) except that sndr is evaluated only once[.](#exec.bulk-3.sentence-2)
|
||
|
||
The first template argument of *product-type* is Policy if Policy models [copy_constructible](concept.copyconstructible#concept:copy_constructible "18.4.14 Concept copy_constructible [concept.copyconstructible]"), andconst Policy& otherwise[.](#exec.bulk-3.sentence-3)
|
||
|
||
[4](#exec.bulk-4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4150)
|
||
|
||
Let sndr and env be subexpressions such thatSndr is decltype((sndr))[.](#exec.bulk-4.sentence-1)
|
||
|
||
If [*sender-for*](exec.snd.concepts#concept:sender-for "33.9.3 Sender concepts [exec.snd.concepts]")<Sndr, bulk_t> is false, then
|
||
the expression bulk.transform_sender(sndr, env) is ill-formed;
|
||
otherwise, it is equivalent to:auto [_, data, child] = sndr;auto& [policy, shape, f] = data;auto new_f = [func = std::move(f)](Shape begin, Shape end, auto&&... vs)noexcept(noexcept(f(begin, vs...))) {while (begin != end) func(begin++, vs...);}return bulk_chunked(std::move(child), policy, shape, std::move(new_f));
|
||
|
||
[*Note [1](#exec.bulk-note-1)*:
|
||
|
||
This causes the bulk(sndr, policy, shape, f) sender to be
|
||
expressed in terms of bulk_chunked(sndr, policy, shape, f) when
|
||
it is connected to a receiver whose
|
||
execution domain does not customize bulk[.](#exec.bulk-4.sentence-2)
|
||
|
||
â *end note*]
|
||
|
||
[5](#exec.bulk-5)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4172)
|
||
|
||
The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2 Exposition-only entities"))
|
||
is specialized for bulk_chunked_t as follows:
|
||
|
||
[ð](#lib:impls-for%3cbulk_chunked_t%3e)
|
||
|
||
namespace std::execution {template<>struct *impls-for*<bulk_chunked_t> : *default-impls* {static constexpr auto *complete* = *see below*; template<class Sndr, class... Env>static consteval void *check-types*(); };}
|
||
|
||
The member *impls-for*<bulk_chunked_t>::*complete* is initialized with a callable object equivalent to the following lambda:[]<class Index, class State, class Rcvr, class Tag, class... Args>(Index, State& state, Rcvr& rcvr, Tag, Args&&... args) noexcept-> void requires *see below* {if constexpr ([same_as](concept.same#concept:same_as "18.4.2 Concept same_as [concept.same]")<Tag, set_value_t>) {auto& [policy, shape, f] = state; constexpr bool nothrow = noexcept(f(auto(shape), auto(shape), args...)); *TRY-EVAL*(rcvr, [&]() noexcept(nothrow) { f(static_cast<decltype(auto(shape))>(0), auto(shape), args...);
|
||
Tag()(std::move(rcvr), std::forward<Args>(args)...); }()); } else { Tag()(std::move(rcvr), std::forward<Args>(args)...); }}
|
||
|
||
The expression in the [*requires-clause*](temp.pre#nt:requires-clause "13.1 Preamble [temp.pre]") of the lambda above istrue if and only
|
||
if Tag denotes a type other than set_value_t or
|
||
if the expression f(auto(shape), auto(shape), args...) is well-formed[.](#exec.bulk-5.sentence-3)
|
||
|
||
[6](#exec.bulk-6)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4210)
|
||
|
||
The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2 Exposition-only entities"))
|
||
is specialized for bulk_unchunked_t as follows:namespace std::execution {template<>struct *impls-for*<bulk_unchunked_t> : *default-impls* {static constexpr auto *complete* = *see below*; };}
|
||
|
||
The member *impls-for*<bulk_unchunked_t>::*complete* is initialized with a callable object equivalent to the following lambda:[]<class Index, class State, class Rcvr, class Tag, class... Args>(Index, State& state, Rcvr& rcvr, Tag, Args&&... args) noexcept-> void requires *see below* {if constexpr ([same_as](concept.same#concept:same_as "18.4.2 Concept same_as [concept.same]")<Tag, set_value_t>) {auto& [shape, f] = state; constexpr bool nothrow = noexcept(f(auto(shape), args...)); *TRY-EVAL*(rcvr, [&]() noexcept(nothrow) {for (decltype(auto(shape)) i = 0; i < shape; ++i) { f(auto(i), args...); } Tag()(std::move(rcvr), std::forward<Args>(args)...); }()); } else { Tag()(std::move(rcvr), std::forward<Args>(args)...); }}
|
||
|
||
The expression in the [*requires-clause*](temp.pre#nt:requires-clause "13.1 Preamble [temp.pre]") of the lambda above
|
||
is true if and only
|
||
if Tag denotes a type other than set_value_t or
|
||
if the expression f(auto(shape), args...) is well-formed[.](#exec.bulk-6.sentence-3)
|
||
|
||
[ð](#lib:check-types,impls-for%3cbulk_t%3e)
|
||
|
||
`template<class Sndr, class... Env>
|
||
static consteval void check-types();
|
||
`
|
||
|
||
[7](#exec.bulk-7)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4253)
|
||
|
||
*Effects*: Equivalent to:auto cs = get_completion_signatures<*child-type*<Sndr>, *FWD-ENV-T*(Env)...>();auto fn = []<class... Ts>(set_value_t(*)(Ts...)) {if constexpr (<remove_cvref_t<*data-type*<Sndr>>, Ts&...>)throw *unspecified-exception*();};
|
||
cs.*for-each*(*overload-set*(fn, [](auto){})); where *unspecified-exception* is
|
||
a type derived from exception[.](#exec.bulk-7.sentence-1)
|
||
|
||
[8](#exec.bulk-8)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4268)
|
||
|
||
Let the subexpression out_sndr denote
|
||
the result of the invocation*bulk-algo*(sndr, policy, shape, 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[.](#exec.bulk-8.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:
|
||
|
||
- [(8.1)](#exec.bulk-8.1)
|
||
|
||
If sndr has a successful completion, whereargs is a pack of lvalue subexpressions
|
||
referring to the value completion result datums of sndr, or
|
||
decayed copies of those values if they model [copy_constructible](concept.copyconstructible#concept:copy_constructible "18.4.14 Concept copy_constructible [concept.copyconstructible]"),
|
||
then:
|
||
|
||
* [(8.1.1)](#exec.bulk-8.1.1)
|
||
|
||
If out_sndr also completes successfully, then:
|
||
|
||
+
|
||
[(8.1.1.1)](#exec.bulk-8.1.1.1)
|
||
for bulk,
|
||
invokes f(i, args...) for every i of type Shape from 0 to shape;
|
||
|
||
+
|
||
[(8.1.1.2)](#exec.bulk-8.1.1.2)
|
||
for bulk_unchunked,
|
||
invokes f(i, args...) for every i of type Shape from 0 to shape;
|
||
*Recommended practice*: The underlying scheduler should execute each iteration
|
||
on a distinct execution agent[.](#exec.bulk-8.1.1.2.sentence-2)
|
||
|
||
+
|
||
[(8.1.1.3)](#exec.bulk-8.1.1.3)
|
||
for bulk_chunked,
|
||
invokes f(b, e, args...) zero or more times
|
||
with pairs of b and e of type Shape in range [0, shape],
|
||
such that b<e and
|
||
for every i of type Shape from 0 to shape,
|
||
there is exactly one invocation with a pair b and e,
|
||
such that i is in the range [b, e)[.](#exec.bulk-8.1.1.3.sentence-1)
|
||
|
||
* [(8.1.2)](#exec.bulk-8.1.2)
|
||
|
||
If out_sndr completes with set_error(rcvr, eptr), then
|
||
the asynchronous operation may invoke a subset of
|
||
the invocations of f before the error completion handler is called, and eptr is an exception_ptr containing either:
|
||
+
|
||
[(8.1.2.1)](#exec.bulk-8.1.2.1)
|
||
an exception thrown by an invocation of f, or
|
||
|
||
+
|
||
[(8.1.2.2)](#exec.bulk-8.1.2.2)
|
||
a bad_alloc exception if
|
||
the implementation fails to allocate required resources, or
|
||
|
||
+
|
||
[(8.1.2.3)](#exec.bulk-8.1.2.3)
|
||
an exception derived from runtime_error[.](#exec.bulk-8.1.2.sentence-1)
|
||
|
||
* [(8.1.3)](#exec.bulk-8.1.3)
|
||
|
||
If out_sndr completes with set_stopped(rcvr), then
|
||
the asynchronous operation may invoke a subset of
|
||
the invocations of f before the stopped completion handler[.](#exec.bulk-8.1.3.sentence-1)
|
||
|
||
- [(8.2)](#exec.bulk-8.2)
|
||
|
||
If sndr does not complete with set_value, then
|
||
the completion is forwarded to recv[.](#exec.bulk-8.2.sentence-1)
|
||
|
||
- [(8.3)](#exec.bulk-8.3)
|
||
|
||
For *bulk-algo*,
|
||
the parameter policy describes
|
||
the manner in which
|
||
the execution of the asynchronous operations corresponding to these algorithms
|
||
may be parallelized and
|
||
the manner in which
|
||
they apply f[.](#exec.bulk-8.3.sentence-1)
|
||
Permissions and requirements
|
||
on parallel algorithm element access functions ([[algorithms.parallel.exec]](algorithms.parallel.exec "26.3.3 Effect of execution policies on algorithm execution"))
|
||
apply to f[.](#exec.bulk-8.3.sentence-2)
|
||
|
||
[9](#exec.bulk-9)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4358)
|
||
|
||
[*Note [2](#exec.bulk-note-2)*:
|
||
|
||
The asynchronous operation corresponding to*bulk-algo*(sndr, policy, shape, f) can complete with set_stopped if cancellation is requested or
|
||
ignore cancellation requests[.](#exec.bulk-9.sentence-1)
|
||
|
||
â *end note*]
|
||
|
||
#### [33.9.12.12](#exec.when.all) execution::when_all [[exec.when.all]](exec.when.all)
|
||
|
||
[1](#exec.when.all-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4368)
|
||
|
||
when_all and when_all_with_variant both adapt multiple input senders into a sender
|
||
that completes when all input senders have completed[.](#exec.when.all-1.sentence-1)
|
||
|
||
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[.](#exec.when.all-1.sentence-2)
|
||
|
||
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](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]")[.](#exec.when.all-1.sentence-3)
|
||
|
||
[2](#exec.when.all-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4381)
|
||
|
||
The names when_all and when_all_with_variant denote
|
||
customization point objects[.](#exec.when.all-2.sentence-1)
|
||
|
||
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))...>[.](#exec.when.all-2.sentence-2)
|
||
|
||
Let CD2 be CD if CD is well-formed, anddefault_domain otherwise[.](#exec.when.all-2.sentence-3)
|
||
|
||
The expressions when_all(sndrs...) andwhen_all_with_variant(sndrs...) are ill-formed
|
||
if any of the following is true:
|
||
|
||
- [(2.1)](#exec.when.all-2.1)
|
||
|
||
sizeof...(sndrs) is 0, or
|
||
|
||
- [(2.2)](#exec.when.all-2.2)
|
||
|
||
([sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]")<Sndrs> && ...) is false[.](#exec.when.all-2.sentence-4)
|
||
|
||
[3](#exec.when.all-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4400)
|
||
|
||
The expression when_all(sndrs...) is expression-equivalent to:transform_sender(CD2(), *make-sender*(when_all, {}, sndrs...))
|
||
|
||
[4](#exec.when.all-4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4406)
|
||
|
||
The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2 Exposition-only entities"))
|
||
is specialized for when_all_t as follows:
|
||
|
||
[ð](#lib:impls-for%3cwhen_all_t%3e)
|
||
|
||
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](#exec.when.all-5)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4426)
|
||
|
||
Let *make-when-all-env* be
|
||
the following exposition-only function template:template<class Env>constexpr auto *make-when-all-env*(inplace_stop_source& stop_src, // *exposition only* Env&& env) noexcept {return *see below*;}
|
||
|
||
Returns an object e such that
|
||
|
||
- [(5.1)](#exec.when.all-5.1)
|
||
|
||
decltype(e) models [*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]"), and
|
||
|
||
- [(5.2)](#exec.when.all-5.2)
|
||
|
||
e.query(get_stop_token) is expression-equivalent tostate.*stop-src*.get_token(), and
|
||
|
||
- [(5.3)](#exec.when.all-5.3)
|
||
|
||
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)[.](#exec.when.all-5.sentence-2)
|
||
|
||
[6](#exec.when.all-6)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4452)
|
||
|
||
Let *when-all-env* be an alias template such that*when-all-env*<Env> denotes the typedecltype(*make-
|
||
when-all-env*(declval<inplace_stop_source&>(), declval<Env>()))[.](#exec.when.all-6.sentence-1)
|
||
|
||
[ð](#lib:check-types,impls-for%3cwhen_all_t%3e)
|
||
|
||
`template<class Sndr, class... Env>
|
||
static consteval void check-types();
|
||
`
|
||
|
||
[7](#exec.when.all-7)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4464)
|
||
|
||
Let Is be the pack of integral template arguments of
|
||
the integer_sequence specialization denoted by*indices-for*<Sndr>[.](#exec.when.all-7.sentence-1)
|
||
|
||
[8](#exec.when.all-8)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4469)
|
||
|
||
*Effects*: Equivalent to:auto fn = []<class Child>() {auto cs = get_completion_signatures<Child, *when-all-env*<Env>...>(); if constexpr (cs.*count-of*(set_value) >= 2)throw *unspecified-exception*(); *decay-copyable-result-datums*(cs); // see [[exec.snd.expos]](exec.snd.expos "33.9.2 Exposition-only entities")};(fn.template operator()<*child-type*<Sndr, Is>>(), ...); where *unspecified-exception* is
|
||
a type derived from exception[.](#exec.when.all-8.sentence-1)
|
||
|
||
[9](#exec.when.all-9)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4484)
|
||
|
||
*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[.](#exec.when.all-9.sentence-1)
|
||
|
||
[10](#exec.when.all-10)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4491)
|
||
|
||
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](concept.same#concept:same_as "18.4.2 Concept same_as [concept.same]")<CD, default_domain>) {return env<>(); } else {return *MAKE-ENV*(get_domain, CD()); }}
|
||
|
||
[11](#exec.when.all-11)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4505)
|
||
|
||
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](#exec.when.all-12)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4515)
|
||
|
||
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>(sndr).*apply*(*make-state*<Rcvr>()) and where *make-state* is the following exposition-only class template:enum class *disposition* { *started*, *error*, *stopped* }; // *exposition only*template<class Rcvr>struct *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<Rcvr>>, *on-stop-request*>; struct *state-type* {void *arrive*(Rcvr& rcvr) noexcept { // *exposition only*if (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](#exec.when.all-13)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4562)
|
||
|
||
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[.](#exec.when.all-13.sentence-1)
|
||
|
||
[14](#exec.when.all-14)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4568)
|
||
|
||
The alias values_tuple denotes the typetuple<value_types_of_t<Sndrs, *FWD-ENV-T*(env_of_t<Rcvr>), *decayed-tuple*, optional>...> if that type is well-formed; otherwise, tuple<>[.](#exec.when.all-14.sentence-1)
|
||
|
||
[15](#exec.when.all-15)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4575)
|
||
|
||
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[.](#exec.when.all-15.sentence-1)
|
||
|
||
[16](#exec.when.all-16)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4582)
|
||
|
||
The membervoid *state-type*::*complete*(Rcvr& rcvr) noexcept behaves as follows:
|
||
|
||
- [(16.1)](#exec.when.all-16.1)
|
||
|
||
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);
|
||
|
||
- [(16.2)](#exec.when.all-16.2)
|
||
|
||
Otherwise,
|
||
if disp is equal to *disposition*::*error*,
|
||
evaluates:*on_stop*.reset();
|
||
visit([&]<class Error>(Error& error) noexcept {if constexpr (<Error, *none-such*>) { set_error(std::move(rcvr), std::move(error)); }},
|
||
errors);
|
||
|
||
- [(16.3)](#exec.when.all-16.3)
|
||
|
||
Otherwise, evaluates:*on_stop*.reset();
|
||
set_stopped(std::move(rcvr));
|
||
|
||
[17](#exec.when.all-17)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4623)
|
||
|
||
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](#exec.when.all-18)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4642)
|
||
|
||
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](concept.same#concept:same_as "18.4.2 Concept same_as [concept.same]")<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>(args)...); }} else if constexpr ([same_as](concept.same#concept:same_as "18.4.2 Concept same_as [concept.same]")<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 (<decltype(State::values), tuple<>>) {if (state.disp == *disposition*::*started*) {auto& opt = get<Index::value>(state.values); *TRY-EMPLACE-VALUE*(complete, opt, std::forward<Args>(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...)[.](#exec.when.all-18.sentence-1)
|
||
|
||
[19](#exec.when.all-19)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4694)
|
||
|
||
The expression when_all_with_variant(sndrs...) is expression-equivalent to:transform_sender(CD2(), *make-sender*(when_all_with_variant, {}, sndrs...));
|
||
|
||
[20](#exec.when.all-20)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4701)
|
||
|
||
Given subexpressions sndr and env,
|
||
if[*sender-for*](exec.snd.concepts#concept:sender-for "33.9.3 Sender concepts [exec.snd.concepts]")<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](#exec.when.all-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[.](#exec.when.all-20.sentence-1)
|
||
|
||
â *end note*]
|
||
|
||
#### [33.9.12.13](#exec.into.variant) execution::into_variant [[exec.into.variant]](exec.into.variant)
|
||
|
||
[1](#exec.into.variant-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4722)
|
||
|
||
into_variant adapts a sender with multiple value completion signatures
|
||
into a sender with just one value completion signature
|
||
consisting of a variant of tuples[.](#exec.into.variant-1.sentence-1)
|
||
|
||
[2](#exec.into.variant-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4727)
|
||
|
||
The name into_variant denotes a pipeable sender adaptor object[.](#exec.into.variant-2.sentence-1)
|
||
|
||
For a subexpression sndr, let Sndr be decltype((sndr))[.](#exec.into.variant-2.sentence-2)
|
||
|
||
If Sndr does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"),into_variant(sndr) is ill-formed[.](#exec.into.variant-2.sentence-3)
|
||
|
||
[3](#exec.into.variant-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4733)
|
||
|
||
Otherwise, the expression into_variant(sndr) is expression-equivalent to:transform_sender(*get-domain-early*(sndr), *make-sender*(into_variant, {}, sndr)) except that sndr is only evaluated once[.](#exec.into.variant-3.sentence-1)
|
||
|
||
[4](#exec.into.variant-4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4741)
|
||
|
||
The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2 Exposition-only entities"))
|
||
is specialized for into_variant as follows:
|
||
|
||
[ð](#lib:impls-for%3cinto_variant_t%3e)
|
||
|
||
namespace std::execution {template<>struct *impls-for*<into_variant_t> : *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*() {auto cs = get_completion_signatures<*child-type*<Sndr>, *FWD-ENV-T*(Env)...>(); *decay-copyable-result-datums*(cs); // see [[exec.snd.expos]](exec.snd.expos "33.9.2 Exposition-only entities")}};}
|
||
|
||
[5](#exec.into.variant-5)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4762)
|
||
|
||
The member *impls-for*<into_variant_t>::*get-state* is initialized with a callable object equivalent to the following lambda:[]<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept-> type_identity<value_types_of_t<*child-type*<Sndr>, *FWD-ENV-T*(env_of_t<Rcvr>)>> {return {};}
|
||
|
||
[6](#exec.into.variant-6)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4772)
|
||
|
||
The member *impls-for*<into_variant_t>::*complete* is initialized with a callable object equivalent to the following lambda:[]<class State, class Rcvr, class Tag, class... Args>(auto, State, Rcvr& rcvr, Tag, Args&&... args) noexcept -> void {if constexpr ([same_as](concept.same#concept:same_as "18.4.2 Concept same_as [concept.same]")<Tag, set_value_t>) {using variant_type = typename State::type; *TRY-SET-VALUE*(rcvr, variant_type(*decayed-tuple*<Args...>{std::forward<Args>(args)...})); } else { Tag()(std::move(rcvr), std::forward<Args>(args)...); }}
|
||
|
||
#### [33.9.12.14](#exec.stopped.opt) execution::stopped_as_optional [[exec.stopped.opt]](exec.stopped.opt)
|
||
|
||
[1](#exec.stopped.opt-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4789)
|
||
|
||
stopped_as_optional maps a sender's stopped completion operation
|
||
into a value completion operation as a disengaged optional[.](#exec.stopped.opt-1.sentence-1)
|
||
|
||
The sender's value completion operation
|
||
is also converted into an optional[.](#exec.stopped.opt-1.sentence-2)
|
||
|
||
The result is a sender that never completes with stopped,
|
||
reporting cancellation by completing with a disengaged optional[.](#exec.stopped.opt-1.sentence-3)
|
||
|
||
[2](#exec.stopped.opt-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4797)
|
||
|
||
The name stopped_as_optional denotes a pipeable sender adaptor object[.](#exec.stopped.opt-2.sentence-1)
|
||
|
||
For a subexpression sndr, let Sndr be decltype((sndr))[.](#exec.stopped.opt-2.sentence-2)
|
||
|
||
The expression stopped_as_optional(sndr) is expression-equivalent to:transform_sender(*get-domain-early*(sndr), *make-sender*(stopped_as_optional, {}, sndr)) except that sndr is only evaluated once[.](#exec.stopped.opt-2.sentence-3)
|
||
|
||
[3](#exec.stopped.opt-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4806)
|
||
|
||
The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2 Exposition-only entities"))
|
||
is specialized for stopped_as_optional_t as follows:
|
||
|
||
[ð](#lib:impls-for%3cstopped_as_optional_t%3e)
|
||
|
||
namespace std::execution {template<>struct *impls-for*<stopped_as_optional_t> : *default-impls* {template<class Sndr, class... Env>static consteval void *check-types*() {*default-impls*::*check-types*<Sndr, Env...>(); if constexpr (!requires {requires (<void, *single-sender-value-type*<*child-type*<Sndr>, *FWD-ENV-T*(Env)...>>); })throw *unspecified-exception*(); }};} where *unspecified-exception* is
|
||
a type derived from exception[.](#exec.stopped.opt-3.sentence-1)
|
||
|
||
[4](#exec.stopped.opt-4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4829)
|
||
|
||
Let sndr and env be subexpressions
|
||
such that Sndr is decltype((sndr)) andEnv is decltype((env))[.](#exec.stopped.opt-4.sentence-1)
|
||
|
||
If [*sender-for*](exec.snd.concepts#concept:sender-for "33.9.3 Sender concepts [exec.snd.concepts]")<Sndr, stopped_as_optional_t> is false then the expression stopped_as_optional.transform_sender(sndr, env) is ill-formed;
|
||
otherwise,
|
||
if [sender_in](exec.snd.concepts#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]")<*child-type*<Sndr>, *FWD-ENV-T*(Env)> is false,
|
||
the expression stopped_as_optional.transform_sender(sndr, env) is equivalent to *not-a-sender*();
|
||
otherwise, it is equivalent to:auto&& [_, _, child] = sndr;using V = *single-sender-value-type*<*child-type*<Sndr>, *FWD-ENV-T*(Env)>;return let_stopped( then(std::forward_like<Sndr>(child), []<class... Ts>(Ts&&... ts) noexcept(is_nothrow_constructible_v<V, Ts...>) {return optional<V>(in_place, std::forward<Ts>(ts)...); }), []() noexcept { return just(optional<V>()); });
|
||
|
||
#### [33.9.12.15](#exec.stopped.err) execution::stopped_as_error [[exec.stopped.err]](exec.stopped.err)
|
||
|
||
[1](#exec.stopped.err-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4856)
|
||
|
||
stopped_as_error maps an input sender's stopped completion operation
|
||
into an error completion operation as a custom error type[.](#exec.stopped.err-1.sentence-1)
|
||
|
||
The result is a sender that never completes with stopped,
|
||
reporting cancellation by completing with an error[.](#exec.stopped.err-1.sentence-2)
|
||
|
||
[2](#exec.stopped.err-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4862)
|
||
|
||
The name stopped_as_error denotes a pipeable sender adaptor object[.](#exec.stopped.err-2.sentence-1)
|
||
|
||
For some subexpressions sndr and err,
|
||
let Sndr be decltype((sndr)) and
|
||
let Err be decltype((err))[.](#exec.stopped.err-2.sentence-2)
|
||
|
||
If the type Sndr does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") or
|
||
if the type Err does not satisfy [*movable-value*](exec.general#concept:movable-value "33.1 General [exec.general]"),stopped_as_error(sndr, err) is ill-formed[.](#exec.stopped.err-2.sentence-3)
|
||
|
||
Otherwise, the expression stopped_as_error(sndr, err) is expression-equivalent to:transform_sender(*get-domain-early*(sndr), *make-sender*(stopped_as_error, err, sndr)) except that sndr is only evaluated once[.](#exec.stopped.err-2.sentence-4)
|
||
|
||
[3](#exec.stopped.err-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4877)
|
||
|
||
Let sndr and env be subexpressions
|
||
such that Sndr is decltype((sndr)) andEnv is decltype((env))[.](#exec.stopped.err-3.sentence-1)
|
||
|
||
If [*sender-for*](exec.snd.concepts#concept:sender-for "33.9.3 Sender concepts [exec.snd.concepts]")<Sndr, stopped_as_error_t> is false,
|
||
then the expression stopped_as_error.transform_sender(sndr, env) is ill-formed;
|
||
otherwise, it is equivalent to:auto&& [_, err, child] = sndr;using E = decltype(auto(err));return let_stopped( std::forward_like<Sndr>(child), [err = std::forward_like<Sndr>(err)]() mutable noexcept(is_nothrow_move_constructible_v<E>) {return just_error(std::move(err)); });
|
||
|
||
#### [33.9.12.16](#exec.associate) execution::associate [[exec.associate]](exec.associate)
|
||
|
||
[1](#exec.associate-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4897)
|
||
|
||
associate tries to associate
|
||
a sender with an async scope such that
|
||
the scope can track the lifetime of any asynchronous operations
|
||
created with the sender[.](#exec.associate-1.sentence-1)
|
||
|
||
[2](#exec.associate-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4903)
|
||
|
||
Let *associate-data* be the following exposition-only class template:
|
||
|
||
[ð](#lib:execution::associate-data)
|
||
|
||
namespace std::execution {template<[scope_token](exec.scope.concepts#concept:scope_token "33.14.1 Execution scope concepts [exec.scope.concepts]") Token, [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") Sender>struct *associate-data* { // *exposition only*using *wrap-sender* = // *exposition only* remove_cvref_t<decltype(declval<Token&>().wrap(declval<Sender>()))>; explicit *associate-data*(Token t, Sender&& s): *sndr*(t.wrap(std::forward<Sender>(s))), *token*(t) {if (!*token*.try_associate())*sndr*.reset(); }*associate-data*(const *associate-data*& other)noexcept(is_nothrow_copy_constructible_v<*wrap-sender*> &&noexcept(other.*token*.try_associate())); *associate-data*(*associate-data*&& other)noexcept(is_nothrow_move_constructible_v<*wrap-sender*>); ~*associate-data*();
|
||
|
||
optional<pair<Token, *wrap-sender*>> release() && noexcept(is_nothrow_move_constructible_v<*wrap-sender*>); private: optional<*wrap-sender*> *sndr*; // *exposition only* Token *token*; // *exposition only*}; template<[scope_token](exec.scope.concepts#concept:scope_token "33.14.1 Execution scope concepts [exec.scope.concepts]") Token, [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") Sender>*associate-data*(Token, Sender&&) -> *associate-data*<Token, Sender>;}
|
||
|
||
[3](#exec.associate-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4943)
|
||
|
||
For an *associate-data* object a,a.*sndr*.has_value() is true if and only if
|
||
an association was successfully made and is owned by a[.](#exec.associate-3.sentence-1)
|
||
|
||
[ð](#lib:execution::associate-data,constructor)
|
||
|
||
`associate-data(const associate-data& other)
|
||
noexcept(is_nothrow_copy_constructible_v<wrap-sender> &&
|
||
noexcept(other.token.try_associate()));
|
||
`
|
||
|
||
[4](#exec.associate-4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4957)
|
||
|
||
*Constraints*: [copy_constructible](concept.copyconstructible#concept:copy_constructible "18.4.14 Concept copy_constructible [concept.copyconstructible]")<*wrap-sender*> is true[.](#exec.associate-4.sentence-1)
|
||
|
||
[5](#exec.associate-5)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4961)
|
||
|
||
*Effects*: Value-initializes *sndr* and
|
||
initializes *token* with other.*token*[.](#exec.associate-5.sentence-1)
|
||
|
||
If other.*sndr*.has_value() is false,
|
||
no further effects;
|
||
otherwise,
|
||
calls *token*.try_associate() and,
|
||
if that returns true,
|
||
calls *sndr*.emplace(*other.*sndr*) and,
|
||
if that exits with an exception,
|
||
calls *token*.disassociate() before propagating the exception[.](#exec.associate-5.sentence-2)
|
||
|
||
[ð](#lib:execution::associate-data,constructor_)
|
||
|
||
`associate-data(associate-data&& other)
|
||
noexcept(is_nothrow_move_constructible_v<wrap-sender>);
|
||
`
|
||
|
||
[6](#exec.associate-6)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4982)
|
||
|
||
*Effects*: Initializes *sndr* with std::move(other.*sndr*) and
|
||
initializes *token* with std::move(other.*token*) and
|
||
then calls other.*sndr*.reset()[.](#exec.associate-6.sentence-1)
|
||
|
||
[ð](#lib:execution::associate-data,destructor)
|
||
|
||
`~associate-data();
|
||
`
|
||
|
||
[7](#exec.associate-7)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4995)
|
||
|
||
*Effects*: If *sndr*.has_value() returns false then no effect;
|
||
otherwise, invokes *sndr*.reset() before invoking *token*.disassociate()[.](#exec.associate-7.sentence-1)
|
||
|
||
[ð](#lib:release,execution::associate-data)
|
||
|
||
`optional<pair<Token, wrap-sender>>
|
||
release() && noexcept(is_nothrow_move_constructible_v<wrap-sender>);
|
||
`
|
||
|
||
[8](#exec.associate-8)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5009)
|
||
|
||
*Effects*: If *sndr*.has_value() returns false then
|
||
returns an optional that does not contain a value;
|
||
otherwise returns an optional containing a value of type pair<Token, *wrap-sender*> as if by:return optional(pair(*token*, std::move(**sndr*)));
|
||
|
||
[9](#exec.associate-9)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5020)
|
||
|
||
*Postconditions*: *sndr* does not contain a value[.](#exec.associate-9.sentence-1)
|
||
|
||
[10](#exec.associate-10)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5025)
|
||
|
||
The name associate denotes a pipeable sender adaptor object[.](#exec.associate-10.sentence-1)
|
||
|
||
For subexpressions sndr and token:
|
||
|
||
- [(10.1)](#exec.associate-10.1)
|
||
|
||
If decltype((sndr)) does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"), orremove_cvref_t<decltype((token))> does not satisfy [scope_token](exec.scope.concepts#concept:scope_token "33.14.1 Execution scope concepts [exec.scope.concepts]"), thenassociate(sndr, token) is ill-formed.
|
||
|
||
- [(10.2)](#exec.associate-10.2)
|
||
|
||
Otherwise,
|
||
the expression associate(sndr, token) is expression-equivalent to:transform_sender(*get-domain-early*(sndr), *make-sender*(associate, *associate-data*(token, sndr))) except that sndr is evaluated only once.
|
||
|
||
[11](#exec.associate-11)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5045)
|
||
|
||
The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2 Exposition-only entities"))
|
||
is specialized for associate_t as follows:
|
||
|
||
[ð](#lib:execution::impls-for%3cassociate_t%3e)
|
||
|
||
namespace std::execution {template<>struct *impls-for*<associate_t> : *default-impls* {static constexpr auto *get-state* = *see below*; // *exposition only*static constexpr auto *start* = *see below*; // *exposition only*template<class Sndr, class... Env>static consteval void *check-types*() { // *exposition only*using associate_data_t = remove_cvref_t<*data-type*<Sndr>>; using child_type_t = typename associate_data_t::*wrap-sender*; (void)get_completion_signatures<child_type_t, *FWD-ENV-T*(Env)...>(); }};}
|
||
|
||
[12](#exec.associate-12)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5066)
|
||
|
||
The member *impls-for*<associate_t>::*get-state* is initialized with a callable object equivalent to the following lambda:
|
||
|
||
[]<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept(*see below*) {auto [_, data] = std::forward<Sndr>(sndr); auto dataParts = std::move(data).release(); using scope_tkn = decltype(dataParts->first); using wrap_sender = decltype(dataParts->second); using op_t = connect_result_t<wrap_sender, Rcvr>; struct op_state {bool *associated* = false; // *exposition only*union { Rcvr* *rcvr*; // *exposition only*struct { scope_tkn *token*; // *exposition only* op_t *op*; // *exposition only*} *assoc*; // *exposition only*}; explicit op_state(Rcvr& r) noexcept: *rcvr*(addressof(r)) {}explicit op_state(scope_tkn tkn, wrap_sender&& sndr, Rcvr& r) try: *associated*(true), *assoc*(tkn, connect(std::move(sndr), std::move(r))) {}catch (...) { tkn.disassociate(); throw; } op_state(op_state&&) = delete; ~op_state() {if (*associated*) {*assoc*.*op*.~op_t(); *assoc*.*token*.disassociate(); *assoc*.*token*.~scope_tkn(); }}void *run*() noexcept { // *exposition only*if (*associated*) start(*assoc*.*op*); else set_stopped(std::move(**rcvr*)); }}; if (dataParts)return op_state{std::move(dataParts->first), std::move(dataParts->second), rcvr}; elsereturn op_state{rcvr};}
|
||
|
||
[13](#exec.associate-13)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5126)
|
||
|
||
The expression in the noexcept clause of*impls-for*<associate_t>::*get-state* isis_nothrow_constructible_v<remove_cvref_t<Sndr>, Sndr> && is_nothrow_move_constructible_v<*wrap-sender*> &&[*nothrow-callable*](functional.syn#concept:nothrow-callable "22.10.2 Header <functional> synopsis [functional.syn]")<connect_t, *wrap-sender*, Rcvr> where *wrap-sender* is the typeremove_cvref_t<*data-type*<Sndr>>::*wrap-sender*[.](#exec.associate-13.sentence-1)
|
||
|
||
[14](#exec.associate-14)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5137)
|
||
|
||
The member *impls-for*<associate_t>::*start* is initialized with a callable object equivalent to the following lambda:[](auto& state, auto&) noexcept -> void { state.*run*();}
|
||
|
||
[15](#exec.associate-15)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5146)
|
||
|
||
The evaluation of associate(sndr, token) may cause side effects observable
|
||
via token's associated async scope object[.](#exec.associate-15.sentence-1)
|
||
|
||
#### [33.9.12.17](#exec.stop.when) Exposition-only execution::*stop-when* [[exec.stop.when]](exec.stop.when)
|
||
|
||
[1](#exec.stop.when-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5154)
|
||
|
||
*stop-when* fuses an additional stop token t into a sender so that, upon connecting to a receiver r,
|
||
the resulting operation state receives stop requests from botht and the token returned from get_stop_token(get_env(r))[.](#exec.stop.when-1.sentence-1)
|
||
|
||
[2](#exec.stop.when-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5161)
|
||
|
||
The name *stop-when* denotes an exposition-only sender adaptor[.](#exec.stop.when-2.sentence-1)
|
||
|
||
For subexpressions sndr and token:
|
||
|
||
- [(2.1)](#exec.stop.when-2.1)
|
||
|
||
If decltype((sndr)) does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"), orremove_cvref_t<decltype((token))> does not satisfy [stoppable_token](stoptoken.concepts#concept:stoppable_token "32.3.3 Stop token concepts [stoptoken.concepts]"),
|
||
then *stop-when*(sndr, token) is ill-formed[.](#exec.stop.when-2.1.sentence-1)
|
||
|
||
- [(2.2)](#exec.stop.when-2.2)
|
||
|
||
Otherwise,
|
||
if remove_cvref_t<decltype((token))> models[unstoppable_token](stoptoken.concepts#concept:unstoppable_token "32.3.3 Stop token concepts [stoptoken.concepts]") then*stop-when*(sndr, token) is expression-equivalent tosndr[.](#exec.stop.when-2.2.sentence-1)
|
||
|
||
- [(2.3)](#exec.stop.when-2.3)
|
||
|
||
Otherwise,*stop-when*(sndr, token) returns a sender osndr[.](#exec.stop.when-2.3.sentence-1)
|
||
If osndr is connected to a receiver r,
|
||
let rtoken be the result of get_stop_token(get_env(r))[.](#exec.stop.when-2.3.sentence-2)
|
||
|
||
* [(2.3.1)](#exec.stop.when-2.3.1)
|
||
|
||
If the type of rtoken models [unstoppable_token](stoptoken.concepts#concept:unstoppable_token "32.3.3 Stop token concepts [stoptoken.concepts]") then
|
||
the effects of connecting osndr to r are equivalent toconnect(write_env(sndr, prop(get_stop_token, token)), r)[.](#exec.stop.when-2.3.1.sentence-1)
|
||
|
||
* [(2.3.2)](#exec.stop.when-2.3.2)
|
||
|
||
Otherwise,
|
||
the effects of connecting osndr to r are equivalent toconnect(write_env(sndr, prop(get_stop_token, stoken)), r) where stoken is an object of
|
||
an exposition-only type *stoken-t* such that:
|
||
+
|
||
[(2.3.2.1)](#exec.stop.when-2.3.2.1)
|
||
*stoken-t* models [stoppable_token](stoptoken.concepts#concept:stoppable_token "32.3.3 Stop token concepts [stoptoken.concepts]");
|
||
|
||
+
|
||
[(2.3.2.2)](#exec.stop.when-2.3.2.2)
|
||
stoken.stop_requested() returns token.stop_requested() || rtoken.stop_reques-
|
||
ted();
|
||
|
||
+
|
||
[(2.3.2.3)](#exec.stop.when-2.3.2.3)
|
||
stoken.stop_possible() returns token.stop_possible() || rtoken.stop_possible(); and
|
||
|
||
+
|
||
[(2.3.2.4)](#exec.stop.when-2.3.2.4)
|
||
for types Fn and Init such that both [invocable](concept.invocable#concept:invocable "18.7.2 Concept invocable [concept.invocable]")<Fn> and [constructible_from](concept.constructible#concept:constructible_from "18.4.11 Concept constructible_from [concept.constructible]")<Fn, Init> are modeled, *stoken-t*::callback_type<Fn> models [*stoppable-callback-for*](stoptoken.concepts#concept:stoppable-callback-for "32.3.3 Stop token concepts [stoptoken.concepts]")<Fn, *stoken-t*, Init>[.](#exec.stop.when-2.3.2.sentence-1)
|
||
[*Note [1](#exec.stop.when-note-1)*:
|
||
For an object fn of type Fn constructed from a value, init, of type Init,
|
||
registering fn using *stoken-t*::callback_type<Fn>(stoken, init) results in an invocation of fn when
|
||
a callback registered with token or rtoken would be invoked[.](#exec.stop.when-2.3.2.4.sentence-2)
|
||
fn is invoked at most once[.](#exec.stop.when-2.3.2.4.sentence-3)
|
||
â *end note*]
|
||
|
||
#### [33.9.12.18](#exec.spawn.future) execution::spawn_future [[exec.spawn.future]](exec.spawn.future)
|
||
|
||
[1](#exec.spawn.future-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5230)
|
||
|
||
spawn_future attempts to associate the given input sender
|
||
with the given token's async scope and, on success,
|
||
eagerly starts the input sender;
|
||
the return value is a sender that, when connected and started,
|
||
completes with either
|
||
the result of the eagerly-started input sender or withset_stopped if the input sender was not started[.](#exec.spawn.future-1.sentence-1)
|
||
|
||
[2](#exec.spawn.future-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5239)
|
||
|
||
The name spawn_future denotes a customization point object[.](#exec.spawn.future-2.sentence-1)
|
||
|
||
For subexpressions sndr, token, and env,
|
||
|
||
- [(2.1)](#exec.spawn.future-2.1)
|
||
|
||
let Sndr be decltype((sndr)),
|
||
|
||
- [(2.2)](#exec.spawn.future-2.2)
|
||
|
||
let Token be remove_cvref_t<decltype((token))>, and
|
||
|
||
- [(2.3)](#exec.spawn.future-2.3)
|
||
|
||
let Env be remove_cvref_t<decltype((env))>[.](#exec.spawn.future-2.sentence-2)
|
||
|
||
If any of[sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]")<Sndr>,[scope_token](exec.scope.concepts#concept:scope_token "33.14.1 Execution scope concepts [exec.scope.concepts]")<Token>, or[*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]")<Env> are not satisfied,
|
||
the expression spawn_future(sndr, token, env) is ill-formed[.](#exec.spawn.future-2.sentence-3)
|
||
|
||
[3](#exec.spawn.future-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5254)
|
||
|
||
Let *spawn-future-state-base* be the exposition-only class template:
|
||
|
||
[ð](#lib:execution::spawn-future-state-base)
|
||
|
||
namespace std::execution {template<class Completions>struct *spawn-future-state-base*; // *exposition only*template<class... Sigs>struct *spawn-future-state-base*<completion_signatures<Sigs...>> { // *exposition only*using *variant-t* = *see below*; // *exposition only**variant-t* *result*; // *exposition only*virtual void *complete*() noexcept = 0; // *exposition only*};}
|
||
|
||
[4](#exec.spawn.future-4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5272)
|
||
|
||
Let Sigs be the pack of arguments to
|
||
the completion_signatures specialization provided as
|
||
a parameter to the *spawn-future-state-base* class template[.](#exec.spawn.future-4.sentence-1)
|
||
|
||
Let *as-tuple* be an alias template that
|
||
transforms a completion signature Tag(Args...) into the tuple specialization *decayed-tuple*<Tag, Args...>[.](#exec.spawn.future-4.sentence-2)
|
||
|
||
- [(4.1)](#exec.spawn.future-4.1)
|
||
|
||
If is_nothrow_constructible_v<decay_t<Arg>, Arg> is true for every type Arg in every parameter pack Args in every completion signature Tag(Args...) in Sigs then*variant-t* denotes the typevariant<monostate, tuple<set_stopped_t>, *as-tuple*<Sigs>...>,
|
||
except with duplicate types removed[.](#exec.spawn.future-4.1.sentence-1)
|
||
|
||
- [(4.2)](#exec.spawn.future-4.2)
|
||
|
||
Otherwise*variant-t* denotes the typevariant<monostate, tuple<set_stopped_t>, tuple<set_error_t, exception_ptr>, *as-tuple*<Sigs>...>,
|
||
except with duplicate types removed[.](#exec.spawn.future-4.2.sentence-1)
|
||
|
||
[5](#exec.spawn.future-5)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5298)
|
||
|
||
Let *spawn-future-receiver* be the exposition-only class template:
|
||
|
||
[ð](#lib:execution::spawn-future-receiver)
|
||
|
||
namespace std::execution {template<class Completions>struct *spawn-future-receiver* { // *exposition only*using receiver_concept = receiver_t; *spawn-future-state-base*<Completions>* *state*; // *exposition only*template<class... T>void set_value(T&&... t) && noexcept {*set-complete*<set_value_t>(std::forward<T>(t)...); }template<class E>void set_error(E&& e) && noexcept {*set-complete*<set_error_t>(std::forward<E>(e)); }void set_stopped() && noexcept {*set-complete*<set_stopped_t>(); }private:template<class CPO, class... T>void *set-complete*(T&&... t) noexcept { // *exposition only*constexpr bool nothrow = (is_nothrow_constructible_v<decay_t<T>, T> && ...); try {*state*->*result*.template emplace<*decayed-tuple*<CPO, T...>>(CPO{},
|
||
std::forward<T>(t)...); }catch (...) {if constexpr (!nothrow) {using tuple_t = *decayed-tuple*<set_error_t, exception_ptr>; *state*->*result*.template emplace<tuple_t>(set_error_t{}, current_exception()); }}*state*->*complete*(); }};}
|
||
|
||
[6](#exec.spawn.future-6)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5344)
|
||
|
||
Let *ssource-t* be an unspecified type
|
||
that models [*stoppable-source*](stoptoken.concepts#concept:stoppable-source "32.3.3 Stop token concepts [stoptoken.concepts]") and
|
||
let ssource be an lvalue of type *ssource-t*[.](#exec.spawn.future-6.sentence-1)
|
||
|
||
Let *stoken-t* be decltype(ssource.get_token())[.](#exec.spawn.future-6.sentence-2)
|
||
|
||
Let *future-spawned-sender* be the alias template:
|
||
|
||
template<[sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") Sender, class Env>using *future-spawned-sender* = // *exposition only*decltype(write_env(*stop-when*(declval<Sender>(), declval<*stoken-t*>()), declval<Env>()));
|
||
|
||
[7](#exec.spawn.future-7)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5357)
|
||
|
||
Let *spawn-future-state* be the exposition-only class template:
|
||
|
||
[ð](#lib:execution::spawn-future-state)
|
||
|
||
namespace std::execution {template<class Alloc, [scope_token](exec.scope.concepts#concept:scope_token "33.14.1 Execution scope concepts [exec.scope.concepts]") Token, [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") Sender, class Env>struct *spawn-future-state* // *exposition only*: *spawn-future-state-base*<completion_signatures_of_t<*future-spawned-sender*<Sender, Env>>> {using *sigs-t* = // *exposition only* completion_signatures_of_t<*future-spawned-sender*<Sender, Env>>; using *receiver-t* = // *exposition only**spawn-future-receiver*<*sigs-t*>; using *op-t* = // *exposition only* connect_result_t<*future-spawned-sender*<Sender, Env>, *receiver-t*>; *spawn-future-state*(Alloc alloc, Sender&& sndr, Token token, Env env) // *exposition only*: *alloc*(std::move(alloc)), *op*(connect( write_env(*stop-when*(std::forward<Sender>(sndr), *ssource*.get_token()), std::move(env)), *receiver-t*(this))), *token*(std::move(token)), *associated*(token.try_associate()) {if (associated) start(*op*); else set_stopped(*receiver-t*(this)); }void *complete*() noexcept override; // *exposition only*void *consume*([receiver](exec.recv.concepts#concept:receiver "33.7.1 Receiver concepts [exec.recv.concepts]") auto& rcvr) noexcept; // *exposition only*void *abandon*() noexcept; // *exposition only*private:using *alloc-t* = // *exposition only*typename allocator_traits<Alloc>::template rebind_alloc<*spawn-future-state*>; *alloc-t* *alloc*; // *exposition only**ssource-t* *ssource*; // *exposition only**op-t* *op*; // *exposition only* Token *token*; // *exposition only*bool *associated*; // *exposition only*void *destroy*() noexcept; // *exposition only*};}
|
||
|
||
[8](#exec.spawn.future-8)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5405)
|
||
|
||
For purposes of determining the existence of a data race,*complete*, *consume*, and *abandon* behave as atomic operations ([[intro.multithread]](intro.multithread "6.10.2 Multi-threaded executions and data races"))[.](#exec.spawn.future-8.sentence-1)
|
||
|
||
These operations on a single object of a type
|
||
that is a specialization of *spawn-future-state* appear to occur in a single total order[.](#exec.spawn.future-8.sentence-2)
|
||
|
||
[ð](#lib:complete,execution::spawn-future-state)
|
||
|
||
`void complete() noexcept;
|
||
`
|
||
|
||
[9](#exec.spawn.future-9)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5419)
|
||
|
||
*Effects*:
|
||
|
||
- [(9.1)](#exec.spawn.future-9.1)
|
||
|
||
No effects if this invocation of *complete* happens before
|
||
an invocation of *consume* or *abandon* on *this;
|
||
|
||
- [(9.2)](#exec.spawn.future-9.2)
|
||
|
||
otherwise,
|
||
if an invocation of *consume* on *this happens before
|
||
this invocation of *complete* then
|
||
there is a receiver, rcvr, registered and
|
||
that receiver is completed as if by *consume*(rcvr);
|
||
|
||
- [(9.3)](#exec.spawn.future-9.3)
|
||
|
||
otherwise,*destroy* is invoked[.](#exec.spawn.future-9.sentence-1)
|
||
|
||
[ð](#lib:consume,execution::spawn-future-state)
|
||
|
||
`void consume([receiver](exec.recv.concepts#concept:receiver "33.7.1 Receiver concepts [exec.recv.concepts]") auto& rcvr) noexcept;
|
||
`
|
||
|
||
[10](#exec.spawn.future-10)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5443)
|
||
|
||
*Effects*:
|
||
|
||
- [(10.1)](#exec.spawn.future-10.1)
|
||
|
||
If this invocation of *consume* happens before
|
||
an invocation of *complete* on *this thenrcvr is registered to be completed when*complete* is subsequently invoked on *this;
|
||
|
||
- [(10.2)](#exec.spawn.future-10.2)
|
||
|
||
otherwise,rcvr is completed as if by:std::move(this->*result*).visit([&rcvr](auto&& tuple) noexcept {if constexpr (<remove_reference_t<decltype(tuple)>, monostate>) { apply([&rcvr](auto cpo, auto&&... vals) { cpo(std::move(rcvr), std::move(vals)...); }, std::move(tuple)); }});
|
||
|
||
[ð](#lib:abandon,execution::spawn-future-state)
|
||
|
||
`void abandon() noexcept;
|
||
`
|
||
|
||
[11](#exec.spawn.future-11)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5474)
|
||
|
||
*Effects*:
|
||
|
||
- [(11.1)](#exec.spawn.future-11.1)
|
||
|
||
If this invocation of *abandon* happens before
|
||
an invocation of *complete* on *this then
|
||
equivalent to:*ssource*.request_stop();
|
||
|
||
- [(11.2)](#exec.spawn.future-11.2)
|
||
|
||
otherwise,*destroy* is invoked[.](#exec.spawn.future-11.sentence-1)
|
||
|
||
[ð](#lib:destroy,execution::spawn-future-state)
|
||
|
||
`void destroy() noexcept;
|
||
`
|
||
|
||
[12](#exec.spawn.future-12)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5496)
|
||
|
||
*Effects*: Equivalent to:auto token = std::move(this->*token*);bool associated = this->*associated*;
|
||
|
||
{auto alloc = std::move(this->*alloc*);
|
||
|
||
allocator_traits<*alloc-t*>::destroy(alloc, this);
|
||
allocator_traits<*alloc-t*>::deallocate(alloc, this, 1);}if (associated) token.disassociate();
|
||
|
||
[13](#exec.spawn.future-13)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5515)
|
||
|
||
The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2 Exposition-only entities"))
|
||
is specialized for spawn_future_t as follows:
|
||
|
||
[ð](#lib:execution::impls-for%3cspawn_future_t%3e)
|
||
|
||
namespace std::execution {template<>struct *impls-for*<spawn_future_t> : *default-impls* {static constexpr auto *start* = *see below*; // *exposition only*};}
|
||
|
||
[14](#exec.spawn.future-14)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5529)
|
||
|
||
The member *impls-for*<spawn_future_t>::*start* is initialized with a callable object equivalent to the following lambda:[](auto& state, auto& rcvr) noexcept -> void { state->*consume*(rcvr);}
|
||
|
||
[15](#exec.spawn.future-15)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5538)
|
||
|
||
For the expression spawn_future(sndr, token, env) let new_sender be the expression token.wrap(sndr) and
|
||
let alloc and senv be defined as follows:
|
||
|
||
- [(15.1)](#exec.spawn.future-15.1)
|
||
|
||
if the expression get_allocator(env) is well-formed, thenalloc is the result of get_allocator(env) andsenv is the expression env;
|
||
|
||
- [(15.2)](#exec.spawn.future-15.2)
|
||
|
||
otherwise,
|
||
if the expression get_allocator(get_env(new_sender)) is well-formed, thenalloc is the result of get_allocator(get_env(new_sender)) andsenv is the expression*JOIN-ENV*(prop(get_allocator, alloc), env);
|
||
|
||
- [(15.3)](#exec.spawn.future-15.3)
|
||
|
||
otherwise,alloc is allocator<void>() andsenv is the expression env[.](#exec.spawn.future-15.sentence-1)
|
||
|
||
[16](#exec.spawn.future-16)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5559)
|
||
|
||
The expression spawn_future(sndr, token, env) has the following effects:
|
||
|
||
- [(16.1)](#exec.spawn.future-16.1)
|
||
|
||
Uses alloc to allocate and construct an object s of
|
||
a type that is a specialization of *spawn-future-state* from alloc, token.wrap(sndr), token, and senv[.](#exec.spawn.future-16.1.sentence-1)
|
||
If an exception is thrown then
|
||
any constructed objects are destroyed and
|
||
any allocated memory is deallocated[.](#exec.spawn.future-16.1.sentence-2)
|
||
|
||
- [(16.2)](#exec.spawn.future-16.2)
|
||
|
||
Constructs an object u of
|
||
a type that is a specialization of unique_ptr such that:
|
||
* [(16.2.1)](#exec.spawn.future-16.2.1)
|
||
|
||
u.get() is equal to the address of s, and
|
||
|
||
* [(16.2.2)](#exec.spawn.future-16.2.2)
|
||
|
||
u.get_deleter()(u.release()) is equivalent to u.release()->*abandon*()[.](#exec.spawn.future-16.2.sentence-1)
|
||
|
||
- [(16.3)](#exec.spawn.future-16.3)
|
||
|
||
Returns *make-sender*(spawn_future, std::move(u))[.](#exec.spawn.future-16.3.sentence-1)
|
||
|
||
[17](#exec.spawn.future-17)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5587)
|
||
|
||
The expression spawn_future(sndr, token) is expression-equivalent tospawn_future(sndr, token, execution::env<>())[.](#exec.spawn.future-17.sentence-1)
|