3838 lines
198 KiB
Markdown
3838 lines
198 KiB
Markdown
[exec.snd]
|
||
|
||
# 33 Execution control library [[exec]](./#exec)
|
||
|
||
## 33.9 Senders [exec.snd]
|
||
|
||
### [33.9.1](#general) General [[exec.snd.general]](exec.snd.general)
|
||
|
||
[1](#general-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1325)
|
||
|
||
Subclauses [[exec.factories]](#exec.factories "33.9.11 Sender factories") and [[exec.adapt]](#exec.adapt "33.9.12 Sender adaptors") define
|
||
customizable algorithms that return senders[.](#general-1.sentence-1)
|
||
|
||
Each algorithm has a default implementation[.](#general-1.sentence-2)
|
||
|
||
Let sndr be the result of an invocation of such an algorithm or
|
||
an object equal to the result ([[concepts.equality]](concepts.equality "18.2 Equality preservation")), and
|
||
let Sndr be decltype((sndr))[.](#general-1.sentence-3)
|
||
|
||
Let rcvr be a receiver of type Rcvr with associated environment env of type Env such that [sender_to](#concept:sender_to "33.9.3 Sender concepts [exec.snd.concepts]")<Sndr, Rcvr> is true[.](#general-1.sentence-4)
|
||
|
||
For the default implementation of the algorithm that produced sndr,
|
||
connecting sndr to rcvr and
|
||
starting the resulting operation state ([[exec.async.ops]](exec.async.ops "33.3 Asynchronous operations"))
|
||
necessarily results in the potential evaluation ([[basic.def.odr]](basic.def.odr "6.3 One-definition rule")) of
|
||
a set of completion operations
|
||
whose first argument is a subexpression equal to rcvr[.](#general-1.sentence-5)
|
||
|
||
Let Sigs be a pack of completion signatures corresponding to
|
||
this set of completion operations, and
|
||
let CS be
|
||
the type of the expression get_completion_signatures<Sndr, Env>()[.](#general-1.sentence-6)
|
||
|
||
Then CS is
|
||
a specialization of
|
||
the class template completion_signatures ([[exec.cmplsig]](exec.cmplsig "33.10 Completion signatures")),
|
||
the set of whose template arguments is Sigs[.](#general-1.sentence-7)
|
||
|
||
If none of the types in Sigs are dependent on the type Env, then
|
||
the expression get_completion_signatures<Sndr>() is well-formed and
|
||
its type is CS[.](#general-1.sentence-8)
|
||
|
||
If a user-provided implementation of the algorithm
|
||
that produced sndr is selected instead of the default:
|
||
|
||
- [(1.1)](#general-1.1)
|
||
|
||
Any completion signature
|
||
that is in the set of types
|
||
denoted by completion_signatures_of_t<Sndr, Env> and
|
||
that is not part of Sigs shall correspond to
|
||
error or stopped completion operations,
|
||
unless otherwise specified[.](#general-1.1.sentence-1)
|
||
|
||
- [(1.2)](#general-1.2)
|
||
|
||
If none of the types in Sigs are dependent on the type Env, thencompletion_signatures_of_t<Sndr> andcompletion_signatures_of_t<Sndr, Env> shall denote the same type[.](#general-1.2.sentence-1)
|
||
|
||
### [33.9.2](#expos) Exposition-only entities [[exec.snd.expos]](exec.snd.expos)
|
||
|
||
[1](#expos-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1373)
|
||
|
||
Subclause [exec.snd] makes use of the following exposition-only entities[.](#expos-1.sentence-1)
|
||
|
||
[2](#expos-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1376)
|
||
|
||
For a queryable object env,*FWD-ENV*(env) is an expression
|
||
whose type satisfies [*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]") such that for a query object q and
|
||
a pack of subexpressions as,
|
||
the expression *FWD-ENV*(env).query(q, as...) is ill-formed
|
||
if forwarding_query(q) is false;
|
||
otherwise, it is expression-equivalent to env.query(q, as...)[.](#expos-2.sentence-1)
|
||
|
||
The type *FWD-ENV-T*(Env) isdecltype(*FWD-ENV*(declval<Env>()))[.](#expos-2.sentence-2)
|
||
|
||
[3](#expos-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1390)
|
||
|
||
For a query object q and a subexpression v,*MAKE-ENV*(q, v) is an expression env whose type satisfies [*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]") such that the result of env.query(q) has
|
||
a value equal to v ([[concepts.equality]](concepts.equality "18.2 Equality preservation"))[.](#expos-3.sentence-1)
|
||
|
||
Unless otherwise stated,
|
||
the object to which env.query(q) refers remains valid
|
||
while env remains valid[.](#expos-3.sentence-2)
|
||
|
||
[4](#expos-4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1400)
|
||
|
||
For two queryable objects env1 and env2,
|
||
a query object q, and
|
||
a pack of subexpressions as,*JOIN-ENV*(env1, env2) is an expression env3 whose type satisfies [*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]") such that env3.query(q, as...) is expression-equivalent to:
|
||
|
||
- [(4.1)](#expos-4.1)
|
||
|
||
env1.query(q, as...) if that expression is well-formed,
|
||
|
||
- [(4.2)](#expos-4.2)
|
||
|
||
otherwise, env2.query(q, as...) if that expression is well-formed,
|
||
|
||
- [(4.3)](#expos-4.3)
|
||
|
||
otherwise, env3.query(q, as...) is ill-formed[.](#expos-4.sentence-1)
|
||
|
||
[5](#expos-5)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1416)
|
||
|
||
The results of *FWD-ENV*, *MAKE-ENV*, and *JOIN-ENV* can be context-dependent;
|
||
i.e., they can evaluate to expressions
|
||
with different types and value categories
|
||
in different contexts for the same arguments[.](#expos-5.sentence-1)
|
||
|
||
[6](#expos-6)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1423)
|
||
|
||
For a scheduler sch,*SCHED-ATTRS*(sch) is an expression o1 whose type satisfies [*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]") such that o1.query(get_completion_scheduler<Tag>) is
|
||
an expression with the same type and value as sch where Tag is one of set_value_t or set_stopped_t, and
|
||
such that o1.query(get_domain) is expression-equivalent tosch.query(get_domain)[.](#expos-6.sentence-1)
|
||
|
||
*SCHED-ENV*(sch) is an expression o2 whose type satisfies [*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]") such that o2.query(get_scheduler) is a prvalue
|
||
with the same type and value as sch, and
|
||
such that o2.query(get_domain) is expression-equivalent tosch.query(get_domain)[.](#expos-6.sentence-2)
|
||
|
||
[7](#expos-7)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1439)
|
||
|
||
For two subexpressions rcvr and expr,*SET-VALUE*(rcvr, expr) is expression-equivalent to(expr, set_value(std::move(rcvr))) if the type of expr is void;
|
||
otherwise, set_value(std::move(rcvr), expr)[.](#expos-7.sentence-1)
|
||
|
||
*TRY-EVAL*(rcvr, expr) is equivalent to:try { expr;} catch(...) { set_error(std::move(rcvr), current_exception());} if expr is potentially-throwing; otherwise, expr[.](#expos-7.sentence-2)
|
||
|
||
*TRY-SET-VALUE*(rcvr, expr) is*TRY-EVAL*(rcvr, *SET-VALUE*(rcvr, expr)) except that rcvr is evaluated only once[.](#expos-7.sentence-3)
|
||
|
||
[ð](#expos-itemdecl:1)
|
||
|
||
`template<class Default = default_domain, class Sndr>
|
||
constexpr auto completion-domain(const Sndr& sndr) noexcept;
|
||
`
|
||
|
||
[8](#expos-8)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1466)
|
||
|
||
*COMPL-DOMAIN*(T) is the type of the expressionget_domain(get_completion_scheduler<T>(get_env(sndr)))[.](#expos-8.sentence-1)
|
||
|
||
[9](#expos-9)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1470)
|
||
|
||
*Effects*: If all of the types*COMPL-DOMAIN*(set_value_t),*COMPL-DOMAIN*(set_error_t), and
|
||
*COMPL-DOMAIN*(set_stopped_t) are ill-formed,completion-domain<Default>(sndr) is
|
||
a default-constructed prvalue of type Default[.](#expos-9.sentence-1)
|
||
|
||
Otherwise, if they all share a common type ([[meta.trans.other]](meta.trans.other "21.3.9.7 Other transformations"))
|
||
(ignoring those types that are ill-formed),
|
||
then *completion-domain*<Default>(sndr) is
|
||
a default-constructed prvalue of that type[.](#expos-9.sentence-2)
|
||
|
||
Otherwise, *completion-domain*<Default>(sndr) is ill-formed[.](#expos-9.sentence-3)
|
||
|
||
[ð](#expos-itemdecl:2)
|
||
|
||
`template<class Tag, class Env, class Default>
|
||
constexpr decltype(auto) query-with-default(
|
||
Tag, const Env& env, Default&& value) noexcept(see below);
|
||
`
|
||
|
||
[10](#expos-10)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1492)
|
||
|
||
Let e be the expression Tag()(env) if that expression is well-formed;
|
||
otherwise, it is static_cast<Default>(std::forward<Default>(value))[.](#expos-10.sentence-1)
|
||
|
||
[11](#expos-11)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1497)
|
||
|
||
*Returns*: e[.](#expos-11.sentence-1)
|
||
|
||
[12](#expos-12)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1501)
|
||
|
||
*Remarks*: The expression in the noexcept clause is noexcept(e)[.](#expos-12.sentence-1)
|
||
|
||
[ð](#expos-itemdecl:3)
|
||
|
||
`template<class Sndr>
|
||
constexpr auto get-domain-early(const Sndr& sndr) noexcept;
|
||
`
|
||
|
||
[13](#expos-13)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1512)
|
||
|
||
*Effects*: Equivalent to:return Domain(); where Domain is
|
||
the decayed type of the first of the following expressions that is well-formed:
|
||
|
||
- [(13.1)](#expos-13.1)
|
||
|
||
get_domain(get_env(sndr))
|
||
|
||
- [(13.2)](#expos-13.2)
|
||
|
||
*completion-domain*(sndr)
|
||
|
||
- [(13.3)](#expos-13.3)
|
||
|
||
default_domain()
|
||
|
||
[ð](#expos-itemdecl:4)
|
||
|
||
`template<class Sndr, class Env>
|
||
constexpr auto get-domain-late(const Sndr& sndr, const Env& env) noexcept;
|
||
`
|
||
|
||
[14](#expos-14)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1533)
|
||
|
||
*Effects*: Equivalent to:
|
||
|
||
- [(14.1)](#expos-14.1)
|
||
|
||
If [*sender-for*](#concept:sender-for "33.9.3 Sender concepts [exec.snd.concepts]")<Sndr, continues_on_t> is true, thenreturn Domain(); where Domain is the type of the following expression:[] {auto [_, sch, _] = sndr; return *query-with-default*(get_domain, sch, default_domain());}();
|
||
[*Note [1](#expos-note-1)*:
|
||
The continues_on algorithm works
|
||
in tandem with schedule_from ([[exec.schedule.from]](#exec.schedule.from "33.9.12.7 execution::schedule_from"))
|
||
to give scheduler authors a way to customize both
|
||
how to transition onto (continues_on) and off of (schedule_from)
|
||
a given execution context[.](#expos-14.1.sentence-1)
|
||
Thus, continues_on ignores the domain of the predecessor and
|
||
uses the domain of the destination scheduler to select a customization,
|
||
a property that is unique to continues_on[.](#expos-14.1.sentence-2)
|
||
That is why it is given special treatment here[.](#expos-14.1.sentence-3)
|
||
â *end note*]
|
||
|
||
- [(14.2)](#expos-14.2)
|
||
|
||
Otherwise,return Domain(); where Domain is the first of the following expressions
|
||
that is well-formed and whose type is not void:
|
||
* [(14.2.1)](#expos-14.2.1)
|
||
|
||
get_domain(get_env(sndr))
|
||
|
||
* [(14.2.2)](#expos-14.2.2)
|
||
|
||
*completion-domain*<void>(sndr)
|
||
|
||
* [(14.2.3)](#expos-14.2.3)
|
||
|
||
get_domain(env)
|
||
|
||
* [(14.2.4)](#expos-14.2.4)
|
||
|
||
get_domain(get_scheduler(env))
|
||
|
||
* [(14.2.5)](#expos-14.2.5)
|
||
|
||
default_domain()
|
||
|
||
[15](#expos-15)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1577)
|
||
|
||
template<[*callable*](functional.syn#concept:callable "22.10.2 Header <functional> synopsis [functional.syn]") Fun>requires is_nothrow_move_constructible_v<Fun>struct *emplace-from* { Fun *fun*; // *exposition only*using type = *call-result-t*<Fun>; constexpr operator type() && noexcept([*nothrow-callable*](functional.syn#concept:nothrow-callable "22.10.2 Header <functional> synopsis [functional.syn]")<Fun>) {return std::move(fun)(); }constexpr type operator()() && noexcept([*nothrow-callable*](functional.syn#concept:nothrow-callable "22.10.2 Header <functional> synopsis [functional.syn]")<Fun>) {return std::move(fun)(); }};
|
||
|
||
[*Note [2](#expos-note-2)*:
|
||
|
||
*emplace-from* is used to emplace non-movable types
|
||
into tuple, optional, variant, and similar types[.](#expos-15.sentence-1)
|
||
|
||
â *end note*]
|
||
|
||
[16](#expos-16)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1599)
|
||
|
||
struct *on-stop-request* { inplace_stop_source& *stop-src*; // *exposition only*void operator()() noexcept { *stop-src*.request_stop(); }};
|
||
|
||
[17](#expos-17)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1607)
|
||
|
||
template<class T0, class T1, …, class Tn>struct *product-type* { // *exposition only* T0 t0; // *exposition only* T1 t1; // *exposition only* ⋮
|
||
Tn tn; // *exposition only*template<size_t I, class Self>constexpr decltype(auto) *get*(this Self&& self) noexcept; // *exposition only*template<class Self, class Fn>constexpr decltype(auto) *apply*(this Self&& self, Fn&& fn) // *exposition only*noexcept(*see below*);};
|
||
|
||
[18](#expos-18)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1625)
|
||
|
||
[*Note [3](#expos-note-3)*:
|
||
|
||
*product-type* is presented here in pseudo-code form
|
||
for the sake of exposition[.](#expos-18.sentence-1)
|
||
|
||
It can be approximated in standard C++ with a tuple-like implementation
|
||
that takes care to keep the type an aggregate
|
||
that can be used as the initializer of a structured binding declaration[.](#expos-18.sentence-2)
|
||
|
||
â *end note*]
|
||
|
||
[*Note [4](#expos-note-4)*:
|
||
|
||
An expression of type *product-type* is usable as
|
||
the initializer of a structured binding declaration ([[dcl.struct.bind]](dcl.struct.bind "9.7 Structured binding declarations"))[.](#expos-18.sentence-3)
|
||
|
||
â *end note*]
|
||
|
||
[ð](#expos-itemdecl:5)
|
||
|
||
`template<size_t I, class Self>
|
||
constexpr decltype(auto) get(this Self&& self) noexcept;
|
||
`
|
||
|
||
[19](#expos-19)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1644)
|
||
|
||
*Effects*: Equivalent to:auto& [...ts] = self;return std::forward_like<Self>(ts...[I]);
|
||
|
||
template<class Self, class Fn>constexpr decltype(auto) *apply*(this Self&& self, Fn&& fn) noexcept(*see below*);
|
||
|
||
[20](#expos-20)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1659)
|
||
|
||
*Constraints*: The expression in the return statement below is well-formed[.](#expos-20.sentence-1)
|
||
|
||
[21](#expos-21)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1663)
|
||
|
||
*Effects*: Equivalent to:auto& [...ts] = self;return std::forward<Fn>(fn)(std::forward_like<Self>(ts)...);
|
||
|
||
[22](#expos-22)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1671)
|
||
|
||
*Remarks*: The expression in the noexcept clause is true if the return statement above is not potentially throwing;
|
||
otherwise, false[.](#expos-22.sentence-1)
|
||
|
||
[23](#expos-23)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1678)
|
||
|
||
Let [*valid-specialization*](#concept:valid-specialization "33.9.2 Exposition-only entities [exec.snd.expos]") be the following concept:namespace std::execution {template<template<class...> class T, class... Args>concept [*valid-specialization*](#concept:valid-specialization "33.9.2 Exposition-only entities [exec.snd.expos]") = // *exposition only*requires { typename T<Args...>; };}
|
||
|
||
[ð](#expos-itemdecl:6)
|
||
|
||
`template<class Tag, class Data = see below, class... Child>
|
||
constexpr auto make-sender(Tag tag, Data&& data, Child&&... child);
|
||
`
|
||
|
||
[24](#expos-24)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1694)
|
||
|
||
*Mandates*: The following expressions are true:
|
||
|
||
- [(24.1)](#expos-24.1)
|
||
|
||
[semiregular](concepts.object#concept:semiregular "18.6 Object concepts [concepts.object]")<Tag>
|
||
|
||
- [(24.2)](#expos-24.2)
|
||
|
||
[*movable-value*](exec.general#concept:movable-value "33.1 General [exec.general]")<Data>
|
||
|
||
- [(24.3)](#expos-24.3)
|
||
|
||
([sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]")<Child> && ...)
|
||
|
||
- [(24.4)](#expos-24.4)
|
||
|
||
[dependent_sender](#concept:dependent_sender "33.9.3 Sender concepts [exec.snd.concepts]")<Sndr> || [sender_in](#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]")<Sndr>,
|
||
where Sndr is *basic-sender*<Tag, Data,
|
||
Child...> as defined below[.](#expos-24.sentence-1)
|
||
*Recommended practice*: If evaluation of [sender_in](#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]")<Sndr> results in
|
||
an uncaught exception from
|
||
the evaluation of get_completion_signatures<Sndr>(),
|
||
the implementation should include information about that exception in
|
||
the resulting diagnostic[.](#expos-24.4.sentence-2)
|
||
|
||
[25](#expos-25)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1714)
|
||
|
||
*Returns*: A prvalue of
|
||
type *basic-sender*<Tag, decay_t<Data>, decay_t<Child>...> that has been direct-list-initialized with the forwarded arguments,
|
||
where *basic-sender* is the following exposition-only class template except as noted below[.](#expos-25.sentence-1)
|
||
|
||
[26](#expos-26)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1722)
|
||
|
||
*Remarks*: The default template argument for the Data template parameter
|
||
denotes an unspecified empty trivially copyable class type
|
||
that models [semiregular](concepts.object#concept:semiregular "18.6 Object concepts [concepts.object]")[.](#expos-26.sentence-1)
|
||
|
||
namespace std::execution {template<class Tag>concept [*completion-tag*](#concept:completion-tag "33.9.2 Exposition-only entities [exec.snd.expos]") = // *exposition only*[same_as](concept.same#concept:same_as "18.4.2 Concept same_as [concept.same]")<Tag, set_value_t> || [same_as](concept.same#concept:same_as "18.4.2 Concept same_as [concept.same]")<Tag, set_error_t> || [same_as](concept.same#concept:same_as "18.4.2 Concept same_as [concept.same]")<Tag, set_stopped_t>; struct *default-impls* { // *exposition only*static constexpr auto *get-attrs* = *see below*; // *exposition only*static constexpr auto *get-env* = *see below*; // *exposition only*static constexpr auto *get-state* = *see below*; // *exposition only*static constexpr auto *start* = *see below*; // *exposition only*static constexpr auto *complete* = *see below*; // *exposition only*template<class Sndr, class... Env>static consteval void *check-types*(); // *exposition only*}; template<class Tag>struct *impls-for* : *default-impls* {}; // *exposition only*template<class Sndr, class Rcvr> // *exposition only*using *state-type* = decay_t<*call-result-t*<decltype(*impls-for*<tag_of_t<Sndr>>::*get-state*), Sndr, Rcvr&>>; template<class Index, class Sndr, class Rcvr> // *exposition only*using *env-type* = *call-result-t*<decltype(*impls-for*<tag_of_t<Sndr>>::*get-env*), Index, *state-type*<Sndr, Rcvr>&, const Rcvr&>; template<class Sndr>using *data-type* = decltype(declval<Sndr>().template *get*<1>()); // *exposition only*template<class Sndr, size_t I = 0>using *child-type* = decltype(declval<Sndr>().template *get*<I+2>()); // *exposition only*template<class Sndr>using *indices-for* = remove_reference_t<Sndr>::*indices-for*; // *exposition only*template<class Sndr, class Rcvr>struct *basic-state* { // *exposition only**basic-state*(Sndr&& sndr, Rcvr&& rcvr) noexcept(*see below*): *rcvr*(std::move(rcvr)) , *state*(*impls-for*<tag_of_t<Sndr>>::*get-state*(std::forward<Sndr>(sndr), *rcvr*)) { } Rcvr *rcvr*; // *exposition only**state-type*<Sndr, Rcvr> *state*; // *exposition only*}; template<class Sndr, class Rcvr, class Index>requires [*valid-specialization*](#concept:valid-specialization "33.9.2 Exposition-only entities [exec.snd.expos]")<*env-type*, Index, Sndr, Rcvr>struct *basic-receiver* { // *exposition only*using receiver_concept = receiver_t; using *tag-t* = tag_of_t<Sndr>; // *exposition only*using *state-t* = *state-type*<Sndr, Rcvr>; // *exposition only*static constexpr const auto& *complete* = *impls-for*<*tag-t*>::*complete*; // *exposition only*template<class... Args>requires [*callable*](functional.syn#concept:callable "22.10.2 Header <functional> synopsis [functional.syn]")<decltype(*complete*), Index, *state-t*&, Rcvr&, set_value_t, Args...>void set_value(Args&&... args) && noexcept {*complete*(Index(), op->*state*, op->*rcvr*, set_value_t(), std::forward<Args>(args)...); }template<class Error>requires [*callable*](functional.syn#concept:callable "22.10.2 Header <functional> synopsis [functional.syn]")<decltype(*complete*), Index, *state-t*&, Rcvr&, set_error_t, Error>void set_error(Error&& err) && noexcept {*complete*(Index(), op->*state*, op->*rcvr*, set_error_t(), std::forward<Error>(err)); }void set_stopped() && noexceptrequires [*callable*](functional.syn#concept:callable "22.10.2 Header <functional> synopsis [functional.syn]")<decltype(*complete*), Index, *state-t*&, Rcvr&, set_stopped_t> {*complete*(Index(), op->*state*, op->*rcvr*, set_stopped_t()); }auto get_env() const noexcept -> *env-type*<Index, Sndr, Rcvr> {return *impls-for*<tag-t>::*get-env*(Index(), op->*state*, op->*rcvr*); }*basic-state*<Sndr, Rcvr>* *op*; // *exposition only*}; constexpr auto *connect-all* = *see below*; // *exposition only*template<class Sndr, class Rcvr>using *connect-all-result* = *call-result-t*< // *exposition only*decltype(*connect-all*), *basic-state*<Sndr, Rcvr>*, Sndr, *indices-for*<Sndr>>; template<class Sndr, class Rcvr>requires [*valid-specialization*](#concept:valid-specialization "33.9.2 Exposition-only entities [exec.snd.expos]")<*state-type*, Sndr, Rcvr> &&[*valid-specialization*](#concept:valid-specialization "33.9.2 Exposition-only entities [exec.snd.expos]")<*connect-all-result*, Sndr, Rcvr>struct *basic-operation* : *basic-state*<Sndr, Rcvr> { // *exposition only*using operation_state_concept = operation_state_t; using *tag-t* = tag_of_t<Sndr>; // *exposition only**connect-all-result*<Sndr, Rcvr> *inner-ops*; // *exposition only**basic-operation*(Sndr&& sndr, Rcvr&& rcvr) noexcept(*see below*) // *exposition only*: *basic-state*<Sndr, Rcvr>(std::forward<Sndr>(sndr), std::move(rcvr)), *inner-ops*(*connect-all*(this, std::forward<Sndr>(sndr), *indices-for*<Sndr>())){}void start() & noexcept {auto& [...ops] = *inner-ops*; *impls-for*<tag-t>::*start*(this->*state*, this->*rcvr*, ops...); }}; template<class Tag, class Data, class... Child>struct *basic-sender* : *product-type*<Tag, Data, Child...> { // *exposition only*using sender_concept = sender_t; using *indices-for* = index_sequence_for<Child...>; // *exposition only*decltype(auto) get_env() const noexcept {auto& [_, data, ...child] = *this; return *impls-for*<Tag>::*get-attrs*(data, child...); }template<[*decays-to*](execution.syn#concept:decays-to "33.4 Header <execution> synopsis [execution.syn]")<*basic-sender*> Self, [receiver](exec.recv.concepts#concept:receiver "33.7.1 Receiver concepts [exec.recv.concepts]") Rcvr>auto connect(this Self&& self, Rcvr rcvr) noexcept(*see below*)-> *basic-operation*<Self, Rcvr> {return {std::forward<Self>(self), std::move(rcvr)}; }template<[*decays-to*](execution.syn#concept:decays-to "33.4 Header <execution> synopsis [execution.syn]")<*basic-sender*> Self, class... Env>static constexpr auto get_completion_signatures(); };}
|
||
|
||
[27](#expos-27)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1857)
|
||
|
||
It is unspecified whether a specialization of *basic-sender* is an aggregate[.](#expos-27.sentence-1)
|
||
|
||
[28](#expos-28)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1861)
|
||
|
||
An expression of type *basic-sender* is usable as
|
||
the initializer of a structured binding declaration ([[dcl.struct.bind]](dcl.struct.bind "9.7 Structured binding declarations"))[.](#expos-28.sentence-1)
|
||
|
||
[29](#expos-29)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1865)
|
||
|
||
The expression in the noexcept clause of
|
||
the constructor of *basic-state* isis_nothrow_move_constructible_v<Rcvr> &&[*nothrow-callable*](functional.syn#concept:nothrow-callable "22.10.2 Header <functional> synopsis [functional.syn]")<decltype(*impls-for*<tag_of_t<Sndr>>::*get-state*), Sndr, Rcvr&> &&([same_as](concept.same#concept:same_as "18.4.2 Concept same_as [concept.same]")<*state-type*<Sndr, Rcvr>, *get-state-result*> || is_nothrow_constructible_v<*state-type*<Sndr, Rcvr>, *get-state-result*>) where *get-state-result* is*call-result-t*<decltype(*impls-for*<tag_of_t<Sndr>>::*get-state*), Sndr, Rcvr&>.
|
||
|
||
[30](#expos-30)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1879)
|
||
|
||
The object *connect-all* is initialized with
|
||
a callable object equivalent to the following lambda:
|
||
|
||
[ð](#expos-itemdecl:7)
|
||
|
||
`[]<class Sndr, class Rcvr, size_t... Is>(
|
||
basic-state<Sndr, Rcvr>* op, Sndr&& sndr, index_sequence<Is...>) noexcept(see below)
|
||
-> decltype(auto) {
|
||
auto& [_, data, ...child] = sndr;
|
||
return product-type{connect(
|
||
std::forward_like<Sndr>(child),
|
||
basic-receiver<Sndr, Rcvr, integral_constant<size_t, Is>>{op})...};
|
||
}
|
||
`
|
||
|
||
[31](#expos-31)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1894)
|
||
|
||
*Constraints*: The expression in the return statement is well-formed[.](#expos-31.sentence-1)
|
||
|
||
[32](#expos-32)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1898)
|
||
|
||
*Remarks*: The expression in the noexcept clause is true if the return statement is not potentially throwing;
|
||
otherwise, false[.](#expos-32.sentence-1)
|
||
|
||
[33](#expos-33)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1905)
|
||
|
||
The expression in the noexcept clause of
|
||
the constructor of *basic-operation* is:is_nothrow_constructible_v<*basic-state*<Self, Rcvr>, Self, Rcvr> &&noexcept(*connect-all*(this, std::forward<Sndr>(sndr), *indices-for*<Sndr>()))
|
||
|
||
[34](#expos-34)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1913)
|
||
|
||
The expression in the noexcept clause of
|
||
the connect member function of *basic-sender* is:is_nothrow_constructible_v<*basic-operation*<Self, Rcvr>, Self, Rcvr>
|
||
|
||
[35](#expos-35)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1920)
|
||
|
||
The member *default-impls*::*get-attrs* is initialized with a callable object equivalent to the following lambda:[](const auto&, const auto&... child) noexcept -> decltype(auto) {if constexpr (sizeof...(child) == 1)return (*FWD-ENV*(get_env(child)), ...); elsereturn env<>();}
|
||
|
||
[36](#expos-36)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1932)
|
||
|
||
The member *default-impls*::*get-env* is initialized with a callable object equivalent to the following lambda:[](auto, auto&, const auto& rcvr) noexcept -> decltype(auto) {return *FWD-ENV*(get_env(rcvr));}
|
||
|
||
[37](#expos-37)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1941)
|
||
|
||
The member *default-impls*::*get-state* is initialized with a callable object equivalent to the following lambda:[]<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) noexcept -> decltype(auto) {auto& [_, data, ...child] = sndr; return *allocator-aware-forward*(std::forward_like<Sndr>(data), rcvr);}
|
||
|
||
[38](#expos-38)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1951)
|
||
|
||
The member *default-impls*::*start* is initialized with a callable object equivalent to the following lambda:[](auto&, auto&, auto&... ops) noexcept -> void {(execution::start(ops), ...);}
|
||
|
||
[39](#expos-39)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1960)
|
||
|
||
The member *default-impls*::*complete* is initialized with a callable object equivalent to the following lambda:[]<class Index, class Rcvr, class Tag, class... Args>( Index, auto& state, Rcvr& rcvr, Tag, Args&&... args) noexcept-> void requires [*callable*](functional.syn#concept:callable "22.10.2 Header <functional> synopsis [functional.syn]")<Tag, Rcvr, Args...> {static_assert(Index::value == 0);
|
||
Tag()(std::move(rcvr), std::forward<Args>(args)...);}
|
||
|
||
[ð](#lib:check-types,default-impls)
|
||
|
||
`template<class Sndr, class... Env>
|
||
static consteval void default-impls::check-types();
|
||
`
|
||
|
||
[40](#expos-40)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1979)
|
||
|
||
Let Is be the pack of integral template arguments of
|
||
the integer_sequence specialization denoted by*indices-for*<Sndr>[.](#expos-40.sentence-1)
|
||
|
||
[41](#expos-41)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1984)
|
||
|
||
*Effects*: Equivalent to:(get_completion_signatures<*child-type*<Sndr, Is>, *FWD-ENV-T*(Env)...>(), ...);
|
||
|
||
[42](#expos-42)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L1991)
|
||
|
||
[*Note [5](#expos-note-5)*:
|
||
|
||
For any types T and S, and pack E,
|
||
let e be the expression*impls-for*<T>::*check-types*<S, E...>()[.](#expos-42.sentence-1)
|
||
|
||
Then exactly one of the following is true:
|
||
|
||
- [(42.1)](#expos-42.1)
|
||
|
||
e is ill-formed, or
|
||
|
||
- [(42.2)](#expos-42.2)
|
||
|
||
the evaluation of e exits with an exception, or
|
||
|
||
- [(42.3)](#expos-42.3)
|
||
|
||
e is a core constant expression[.](#expos-42.sentence-2)
|
||
|
||
When e is a core constant expression,
|
||
the pack S, E... uniquely determines a set of completion signatures[.](#expos-42.sentence-3)
|
||
|
||
â *end note*]
|
||
|
||
[ð](#lib:get_completion_signatures,basic-sender)
|
||
|
||
`template<class Tag, class Data, class... Child>
|
||
template<class Sndr, class... Env>
|
||
constexpr auto basic-sender<Tag, Data, Child...>::get_completion_signatures();
|
||
`
|
||
|
||
[43](#expos-43)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2015)
|
||
|
||
Let Rcvr be the type of a receiver whose
|
||
environment has type E, whereE is the first type in the list Env..., env<>[.](#expos-43.sentence-1)
|
||
|
||
Let *CHECK-TYPES*() be the expression*impls-for*<Tag>::template *check-types*<
|
||
Sndr, E>(), and
|
||
let CS be a type determined as follows:
|
||
|
||
- [(43.1)](#expos-43.1)
|
||
|
||
If *CHECK-TYPES*() is a core constant expression,
|
||
let op be an lvalue subexpression
|
||
whose type is connect_result_t<Sndr, Rcvr>[.](#expos-43.1.sentence-1)
|
||
Then CS is the specialization of completion_signatures the set of whose template arguments
|
||
correspond to the set of completion operations
|
||
that are potentially evaluated ([[basic.def.odr]](basic.def.odr "6.3 One-definition rule"))
|
||
as a result of evaluating op.start()[.](#expos-43.1.sentence-2)
|
||
|
||
- [(43.2)](#expos-43.2)
|
||
|
||
Otherwise, CS is completion_signatures<>[.](#expos-43.2.sentence-1)
|
||
|
||
[44](#expos-44)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2038)
|
||
|
||
*Constraints*: *CHECK-TYPES*() is a well-formed expression[.](#expos-44.sentence-1)
|
||
|
||
[45](#expos-45)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2042)
|
||
|
||
*Effects*: Equivalent to:*CHECK-TYPES*();return CS();
|
||
|
||
[46](#expos-46)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2051)
|
||
|
||
template<class... Fns>struct *overload-set* : Fns... {using Fns::operator()...;};
|
||
|
||
[47](#expos-47)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2061)
|
||
|
||
struct *not-a-sender* {using sender_concept = sender_t; template<class Sndr>static consteval auto get_completion_signatures() -> completion_signatures<> {throw *unspecified-exception*(); }}; where *unspecified-exception* is
|
||
a type derived from exception[.](#expos-47.sentence-1)
|
||
|
||
[48](#expos-48)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2078)
|
||
|
||
constexpr void *decay-copyable-result-datums*(auto cs) { cs.*for-each*([]<class Tag, class... Ts>(Tag(*)(Ts...)) {if constexpr (!(is_constructible_v<decay_t<Ts>, Ts> &&...))throw *unspecified-exception*(); });} where *unspecified-exception* is
|
||
a type derived from exception[.](#expos-48.sentence-1)
|
||
|
||
[ð](#expos-itemdecl:10)
|
||
|
||
`template<class T, class Context>
|
||
decltype(auto) allocator-aware-forward(T&& obj, Context&& context); // exposition only
|
||
`
|
||
|
||
[49](#expos-49)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2098)
|
||
|
||
*allocator-aware-forward* is an exposition-only function template
|
||
used to either
|
||
create a new object of type remove_cvref_t<T> from obj or forward obj depending on whether an allocator is available[.](#expos-49.sentence-1)
|
||
|
||
If the environment associated with context provides an allocator
|
||
(i.e., the expression get_allocator(get_env(context)) is valid),
|
||
let *alloc* be the result of this expression
|
||
and let P be remove_cvref_t<T>[.](#expos-49.sentence-2)
|
||
|
||
[50](#expos-50)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2108)
|
||
|
||
*Returns*:
|
||
|
||
- [(50.1)](#expos-50.1)
|
||
|
||
If *alloc* is not defined, returns std::forward<T>(obj),
|
||
|
||
- [(50.2)](#expos-50.2)
|
||
|
||
otherwise if P is a specialization of *product-type*,
|
||
returns an object of type P whose elements are initialized usingmake_obj_using_allocator<decltype(e)>(std::forward_like<T>(e), *alloc*) where e is the corresponding element of obj,
|
||
|
||
- [(50.3)](#expos-50.3)
|
||
|
||
otherwise, returns make_obj_using_allocator<P>(std::forward<T>(obj), *alloc*)[.](#expos-50.sentence-1)
|
||
|
||
### [33.9.3](#concepts) Sender concepts [[exec.snd.concepts]](exec.snd.concepts)
|
||
|
||
[1](#concepts-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2127)
|
||
|
||
The [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") concept defines
|
||
the requirements for a sender type ([[exec.async.ops]](exec.async.ops "33.3 Asynchronous operations"))[.](#concepts-1.sentence-1)
|
||
|
||
The [sender_in](#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]") concept defines
|
||
the requirements for a sender type
|
||
that can create asynchronous operations given an associated environment type[.](#concepts-1.sentence-2)
|
||
|
||
The [sender_to](#concept:sender_to "33.9.3 Sender concepts [exec.snd.concepts]") concept defines
|
||
the requirements for a sender type
|
||
that can connect with a specific receiver type[.](#concepts-1.sentence-3)
|
||
|
||
The get_env customization point object is used to access
|
||
a sender's associated attributes[.](#concepts-1.sentence-4)
|
||
|
||
The connect customization point object is used to connect ([[exec.async.ops]](exec.async.ops "33.3 Asynchronous operations"))
|
||
a sender and a receiver to produce an operation state[.](#concepts-1.sentence-5)
|
||
|
||
[ð](#lib:is-dependent-sender-helper)
|
||
|
||
namespace std::execution {template<auto>concept [*is-constant*](#concept:is-constant "33.9.3 Sender concepts [exec.snd.concepts]") = true; // *exposition only*template<class Sndr>concept [*is-sender*](#concept:is-sender "33.9.3 Sender concepts [exec.snd.concepts]") = // *exposition only*[derived_from](concept.derived#concept:derived_from "18.4.3 Concept derived_from [concept.derived]")<typename Sndr::sender_concept, sender_t>; template<class Sndr>concept [*enable-sender*](#concept:enable-sender "33.9.3 Sender concepts [exec.snd.concepts]") = // *exposition only*[*is-sender*](#concept:is-sender "33.9.3 Sender concepts [exec.snd.concepts]")<Sndr> ||[*is-awaitable*](#concept:is-awaitable "33.9.4 Awaitable helpers [exec.awaitable]")<Sndr, *env-promise*<env<>>>; // [[exec.awaitable]](#exec.awaitable "33.9.4 Awaitable helpers")template<class Sndr>inline constexpr bool enable_sender = [*enable-sender*](#concept:enable-sender "33.9.3 Sender concepts [exec.snd.concepts]")<Sndr>; template<class Sndr>consteval bool *is-dependent-sender-helper*() try { // *exposition only* get_completion_signatures<Sndr>(); return false; } catch (dependent_sender_error&) {return true; }template<class Sndr>concept [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") = enable_sender<remove_cvref_t<Sndr>> &&requires (const remove_cvref_t<Sndr>& sndr) {{ get_env(sndr) } -> [*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]"); } &&[move_constructible](concept.moveconstructible#concept:move_constructible "18.4.13 Concept move_constructible [concept.moveconstructible]")<remove_cvref_t<Sndr>> &&[constructible_from](concept.constructible#concept:constructible_from "18.4.11 Concept constructible_from [concept.constructible]")<remove_cvref_t<Sndr>, Sndr>; template<class Sndr, class... Env>concept [sender_in](#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]") =[sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]")<Sndr> &&(sizeof...(Env) <= 1) &&([*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]")<Env> &&...) &&[*is-constant*](#concept:is-constant "33.9.3 Sender concepts [exec.snd.concepts]")<get_completion_signatures<Sndr, Env...>()>; template<class Sndr>concept [dependent_sender](#concept:dependent_sender "33.9.3 Sender concepts [exec.snd.concepts]") =[sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]")<Sndr> && bool_constant<*is-dependent-sender-helper*<Sndr>()>::value; template<class Sndr, class Rcvr>concept [sender_to](#concept:sender_to "33.9.3 Sender concepts [exec.snd.concepts]") =[sender_in](#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]")<Sndr, env_of_t<Rcvr>> &&[receiver_of](exec.recv.concepts#concept:receiver_of "33.7.1 Receiver concepts [exec.recv.concepts]")<Rcvr, completion_signatures_of_t<Sndr, env_of_t<Rcvr>>> &&requires (Sndr&& sndr, Rcvr&& rcvr) { connect(std::forward<Sndr>(sndr), std::forward<Rcvr>(rcvr)); };}
|
||
|
||
[2](#concepts-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2197)
|
||
|
||
For a type Sndr, if[sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]")<Sndr> is true and[dependent_sender](#concept:dependent_sender "33.9.3 Sender concepts [exec.snd.concepts]")<Sndr> is false,
|
||
then Sndr is a non-dependent sender ([[exec.async.ops]](exec.async.ops "33.3 Asynchronous operations"))[.](#concepts-2.sentence-1)
|
||
|
||
[3](#concepts-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2203)
|
||
|
||
Given a subexpression sndr,
|
||
let Sndr be decltype((sndr)) and
|
||
let rcvr be a receiver
|
||
with an associated environment whose type is Env[.](#concepts-3.sentence-1)
|
||
|
||
A completion operation is a [*permissible completion*](#def:completion,permissible "33.9.3 Sender concepts [exec.snd.concepts]") for Sndr and Env if its completion signature appears in the argument list of the specialization of completion_signatures denoted bycompletion_signatures_of_t<Sndr, Env>[.](#concepts-3.sentence-2)
|
||
|
||
Sndr and Env model [sender_in](#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]")<Sndr, Env> if all the completion operations
|
||
that are potentially evaluated by connecting sndr to rcvr and
|
||
starting the resulting operation state
|
||
are permissible completions for Sndr and Env[.](#concepts-3.sentence-3)
|
||
|
||
[4](#concepts-4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2218)
|
||
|
||
*Remarks*: Pursuant to [[namespace.std]](namespace.std "16.4.5.2.1 Namespace std"),
|
||
users may specialize enable_sender totrue for cv-unqualified program-defined types that
|
||
model [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"), andfalse for types that do not[.](#concepts-4.sentence-1)
|
||
|
||
Such specializations shall
|
||
be usable in constant expressions ([[expr.const]](expr.const "7.7 Constant expressions")) and
|
||
have type const bool[.](#concepts-4.sentence-2)
|
||
|
||
[5](#concepts-5)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2229)
|
||
|
||
The exposition-only concepts[*sender-of*](#concept:sender-of "33.9.3 Sender concepts [exec.snd.concepts]") and [*sender-in-of*](#concept:sender-in-of "33.9.3 Sender concepts [exec.snd.concepts]") define the requirements for a sender type
|
||
that completes with a given unique set of value result types[.](#concepts-5.sentence-1)
|
||
|
||
namespace std::execution {template<class... As>using *value-signature* = set_value_t(As...); // *exposition only*template<class Sndr, class SetValue, class... Env>concept [*sender-in-of-impl*](#concept:sender-in-of-impl "33.9.3 Sender concepts [exec.snd.concepts]") = // *exposition only*[sender_in](#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]")<Sndr, Env...> &&*MATCHING-SIG*(SetValue, // see [[exec.general]](exec.general "33.1 General")*gather-signatures*<set_value_t, // see [[exec.cmplsig]](exec.cmplsig "33.10 Completion signatures") completion_signatures_of_t<Sndr, Env...>, *value-signature*,
|
||
type_identity_t>); template<class Sndr, class Env, class... Values>concept [*sender-in-of*](#concept:sender-in-of "33.9.3 Sender concepts [exec.snd.concepts]") = // *exposition only*[*sender-in-of-impl*](#concept:sender-in-of-impl "33.9.3 Sender concepts [exec.snd.concepts]")<Sndr, set_value_t(Values...), Env>; template<class Sndr, class... Values>concept [*sender-of*](#concept:sender-of "33.9.3 Sender concepts [exec.snd.concepts]") = // *exposition only*[*sender-in-of-impl*](#concept:sender-in-of-impl "33.9.3 Sender concepts [exec.snd.concepts]")<Sndr, set_value_t(Values...)>;}
|
||
|
||
[6](#concepts-6)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2258)
|
||
|
||
Let sndr be an expression
|
||
such that decltype((sndr)) is Sndr[.](#concepts-6.sentence-1)
|
||
|
||
The type tag_of_t<Sndr> is as follows:
|
||
|
||
- [(6.1)](#concepts-6.1)
|
||
|
||
If the declarationauto&& [tag, data, ...children] = sndr; would be well-formed, tag_of_t<Sndr> is
|
||
an alias for decltype(auto(tag)).
|
||
|
||
- [(6.2)](#concepts-6.2)
|
||
|
||
Otherwise, tag_of_t<Sndr> is ill-formed.
|
||
|
||
[7](#concepts-7)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2274)
|
||
|
||
Let [*sender-for*](#concept:sender-for "33.9.3 Sender concepts [exec.snd.concepts]") be an exposition-only concept defined as follows:namespace std::execution {template<class Sndr, class Tag>concept [*sender-for*](#concept:sender-for "33.9.3 Sender concepts [exec.snd.concepts]") =[sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]")<Sndr> &&[same_as](concept.same#concept:same_as "18.4.2 Concept same_as [concept.same]")<tag_of_t<Sndr>, Tag>;}
|
||
|
||
[8](#concepts-8)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2285)
|
||
|
||
For a type T,*SET-VALUE-SIG*(T) denotes the type set_value_t() if T is cv void;
|
||
otherwise, it denotes the type set_value_t(T)[.](#concepts-8.sentence-1)
|
||
|
||
[9](#concepts-9)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2291)
|
||
|
||
Library-provided sender types
|
||
|
||
- [(9.1)](#concepts-9.1)
|
||
|
||
always expose an overload of a member connect that accepts an rvalue sender and
|
||
|
||
- [(9.2)](#concepts-9.2)
|
||
|
||
only expose an overload of a member connect that accepts an lvalue sender if they model [copy_constructible](concept.copyconstructible#concept:copy_constructible "18.4.14 Concept copy_constructible [concept.copyconstructible]")[.](#concepts-9.sentence-1)
|
||
|
||
### [33.9.4](#exec.awaitable) Awaitable helpers [[exec.awaitable]](exec.awaitable)
|
||
|
||
[1](#exec.awaitable-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2304)
|
||
|
||
The sender concepts recognize awaitables as senders[.](#exec.awaitable-1.sentence-1)
|
||
|
||
For [[exec]](exec "33 Execution control library"), an [*awaitable*](#def:awaitable "33.9.4 Awaitable helpers [exec.awaitable]") is an expression
|
||
that would be well-formed as the operand of a co_await expression
|
||
within a given context[.](#exec.awaitable-1.sentence-2)
|
||
|
||
[2](#exec.awaitable-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2310)
|
||
|
||
For a subexpression c,
|
||
let *GET-AWAITER*(c, p) be expression-equivalent to
|
||
the series of transformations and conversions applied to c as the operand of an [*await-expression*](expr.await#nt:await-expression "7.6.2.4 Await [expr.await]") in a coroutine,
|
||
resulting in lvalue e as described by [[expr.await]](expr.await "7.6.2.4 Await"),
|
||
where p is an lvalue referring to the coroutine's promise,
|
||
which has type Promise[.](#exec.awaitable-2.sentence-1)
|
||
|
||
[*Note [1](#exec.awaitable-note-1)*:
|
||
|
||
This includes the invocation of
|
||
the promise type's await_transform member if any,
|
||
the invocation of the operator co_await picked by overload resolution if any, and
|
||
any necessary implicit conversions and materializations[.](#exec.awaitable-2.sentence-2)
|
||
|
||
â *end note*]
|
||
|
||
Let *GET-AWAITER*(c) be
|
||
expression-equivalent to *GET-AWAITER*(c, q) where q is an lvalue of
|
||
an unspecified empty class type *none-such* that
|
||
lacks an await_transform member, and
|
||
where coroutine_handle<*none-such*> behaves ascoroutine_handle<void>[.](#exec.awaitable-2.sentence-3)
|
||
|
||
[3](#exec.awaitable-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2334)
|
||
|
||
Let [*is-awaitable*](#concept:is-awaitable "33.9.4 Awaitable helpers [exec.awaitable]") be the following exposition-only concept:namespace std {template<class T>concept [*await-suspend-result*](#concept:await-suspend-result "33.9.4 Awaitable helpers [exec.awaitable]") = *see below*; // *exposition only*template<class A, class... Promise>concept [*is-awaiter*](#concept:is-awaiter "33.9.4 Awaitable helpers [exec.awaitable]") = // *exposition only*requires (A& a, coroutine_handle<Promise...> h) { a.await_ready() ? 1 : 0; { a.await_suspend(h) } -> [*await-suspend-result*](#concept:await-suspend-result "33.9.4 Awaitable helpers [exec.awaitable]");
|
||
a.await_resume(); }; template<class C, class... Promise>concept [*is-awaitable*](#concept:is-awaitable "33.9.4 Awaitable helpers [exec.awaitable]") = // *exposition only*requires (C (*fc)() noexcept, Promise&... p) {{ *GET-AWAITER*(fc(), p...) } -> [*is-awaiter*](#concept:is-awaiter "33.9.4 Awaitable helpers [exec.awaitable]")<Promise...>; };}
|
||
|
||
[*await-suspend-result*](#concept:await-suspend-result "33.9.4 Awaitable helpers [exec.awaitable]")<T> is true if and only if one of the following is true:
|
||
|
||
- [(3.1)](#exec.awaitable-3.1)
|
||
|
||
T is void, or
|
||
|
||
- [(3.2)](#exec.awaitable-3.2)
|
||
|
||
T is bool, or
|
||
|
||
- [(3.3)](#exec.awaitable-3.3)
|
||
|
||
T is a specialization of coroutine_handle[.](#exec.awaitable-3.sentence-2)
|
||
|
||
[4](#exec.awaitable-4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2365)
|
||
|
||
For a subexpression c such that decltype((c)) is type C, and
|
||
an lvalue p of type Promise,*await-result-
|
||
type*<C, Promise> denotes
|
||
the type decltype(*GET-AWAITER*(c, p).await_resume()) and*await-result-type*<C> denotes
|
||
the type decltype(*GET-AWAITER*(c).await_resume())[.](#exec.awaitable-4.sentence-1)
|
||
|
||
[5](#exec.awaitable-5)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2374)
|
||
|
||
Let *with-await-transform* be the exposition-only class template:namespace std::execution {template<class T, class Promise>concept [*has-as-awaitable*](#concept:has-as-awaitable "33.9.4 Awaitable helpers [exec.awaitable]") = // *exposition only*requires (T&& t, Promise& p) {{ std::forward<T>(t).as_awaitable(p) } -> [*is-awaitable*](#concept:is-awaitable "33.9.4 Awaitable helpers [exec.awaitable]")<Promise&>; }; template<class Derived>struct *with-await-transform* { // *exposition only*template<class T> T&& await_transform(T&& value) noexcept {return std::forward<T>(value); }template<[*has-as-awaitable*](#concept:has-as-awaitable "33.9.4 Awaitable helpers [exec.awaitable]")<Derived> T>auto await_transform(T&& value)noexcept(noexcept(std::forward<T>(value).as_awaitable(declval<Derived&>())))-> decltype(std::forward<T>(value).as_awaitable(declval<Derived&>())) {return std::forward<T>(value).as_awaitable(static_cast<Derived&>(*this)); }};}
|
||
|
||
[6](#exec.awaitable-6)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2401)
|
||
|
||
Let *env-promise* be the exposition-only class template:namespace std::execution {template<class Env>struct *env-promise* : *with-await-transform*<*env-promise*<Env>> { // *exposition only**unspecified* get_return_object() noexcept; *unspecified* initial_suspend() noexcept; *unspecified* final_suspend() noexcept; void unhandled_exception() noexcept; void return_void() noexcept;
|
||
coroutine_handle<> unhandled_stopped() noexcept; const Env& get_env() const noexcept; };}
|
||
|
||
[*Note [2](#exec.awaitable-note-2)*:
|
||
|
||
Specializations of *env-promise* are used only for the purpose of type computation;
|
||
its members need not be defined[.](#exec.awaitable-6.sentence-1)
|
||
|
||
â *end note*]
|
||
|
||
### [33.9.5](#exec.domain.default) execution::default_domain [[exec.domain.default]](exec.domain.default)
|
||
|
||
[1](#exec.domain.default-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2425)
|
||
|
||
namespace std::execution {struct [default_domain](#lib:default_domain "33.9.5 execution::default_domain [exec.domain.default]") {template<[sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") Sndr, [*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]")... Env>requires (sizeof...(Env) <= 1)static constexpr [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") decltype(auto) transform_sender(Sndr&& sndr, const Env&... env)noexcept(*see below*); template<[sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") Sndr, [*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]") Env>static constexpr [*queryable*](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]") decltype(auto) transform_env(Sndr&& sndr, Env&& env) noexcept; template<class Tag, [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") Sndr, class... Args>static constexpr decltype(auto) apply_sender(Tag, Sndr&& sndr, Args&&... args)noexcept(*see below*); };}
|
||
|
||
[ð](#lib:transform_sender,default_domain)
|
||
|
||
`template<[sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") Sndr, [queryable](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]")... Env>
|
||
requires (sizeof...(Env) <= 1)
|
||
constexpr [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") decltype(auto) transform_sender(Sndr&& sndr, const Env&... env)
|
||
noexcept(see below);
|
||
`
|
||
|
||
[2](#exec.domain.default-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2453)
|
||
|
||
Let e be the expressiontag_of_t<Sndr>().transform_sender(std::forward<Sndr>(sndr), env...) if that expression is well-formed;
|
||
otherwise, std::forward<Sndr>(sndr)[.](#exec.domain.default-2.sentence-1)
|
||
|
||
[3](#exec.domain.default-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2461)
|
||
|
||
*Returns*: e[.](#exec.domain.default-3.sentence-1)
|
||
|
||
[4](#exec.domain.default-4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2465)
|
||
|
||
*Remarks*: The exception specification is equivalent to noexcept(e)[.](#exec.domain.default-4.sentence-1)
|
||
|
||
[ð](#lib:transform_env,default_domain)
|
||
|
||
`template<[sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") Sndr, [queryable](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]") Env>
|
||
constexpr [queryable](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]") decltype(auto) transform_env(Sndr&& sndr, Env&& env) noexcept;
|
||
`
|
||
|
||
[5](#exec.domain.default-5)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2477)
|
||
|
||
Let e be the expressiontag_of_t<Sndr>().transform_env(std::forward<Sndr>(sndr), std::forward<Env>(env)) if that expression is well-formed;
|
||
otherwise, *FWD-ENV*(std::forward<Env>(env))[.](#exec.domain.default-5.sentence-1)
|
||
|
||
[6](#exec.domain.default-6)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2485)
|
||
|
||
*Mandates*: noexcept(e) is true[.](#exec.domain.default-6.sentence-1)
|
||
|
||
[7](#exec.domain.default-7)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2489)
|
||
|
||
*Returns*: e[.](#exec.domain.default-7.sentence-1)
|
||
|
||
[ð](#lib:apply_sender,default_domain)
|
||
|
||
`template<class Tag, [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") Sndr, class... Args>
|
||
constexpr decltype(auto) apply_sender(Tag, Sndr&& sndr, Args&&... args)
|
||
noexcept(see below);
|
||
`
|
||
|
||
[8](#exec.domain.default-8)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2502)
|
||
|
||
Let e be the expression Tag().apply_sender(std::forward<Sndr>(sndr), std::forward<Args>(args)...)
|
||
|
||
[9](#exec.domain.default-9)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2508)
|
||
|
||
*Constraints*: e is a well-formed expression[.](#exec.domain.default-9.sentence-1)
|
||
|
||
[10](#exec.domain.default-10)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2512)
|
||
|
||
*Returns*: e[.](#exec.domain.default-10.sentence-1)
|
||
|
||
[11](#exec.domain.default-11)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2516)
|
||
|
||
*Remarks*: The exception specification is equivalent to noexcept(e)[.](#exec.domain.default-11.sentence-1)
|
||
|
||
### [33.9.6](#transform) execution::transform_sender [[exec.snd.transform]](exec.snd.transform)
|
||
|
||
[ð](#lib:transform_sender)
|
||
|
||
`namespace std::execution {
|
||
template<class Domain, [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") Sndr, [queryable](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]")... Env>
|
||
requires (sizeof...(Env) <= 1)
|
||
constexpr [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") decltype(auto) transform_sender(Domain dom, Sndr&& sndr, const Env&... env)
|
||
noexcept(see below);
|
||
}
|
||
`
|
||
|
||
[1](#transform-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2534)
|
||
|
||
Let *transformed-sndr* be the expressiondom.transform_sender(std::forward<Sndr>(sndr), env...) if that expression is well-formed; otherwise,default_domain().transform_sender(std::forward<Sndr>(sndr), env...)
|
||
|
||
Let *final-sndr* be the expression *transformed-sndr* if *transformed-sndr* and *sndr* have the same type ignoring cv-qualifiers;
|
||
otherwise, it is
|
||
the expression transform_sender(dom, *transformed-sndr*, env...)[.](#transform-1.sentence-2)
|
||
|
||
[2](#transform-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2549)
|
||
|
||
*Returns*: *final-sndr*[.](#transform-2.sentence-1)
|
||
|
||
[3](#transform-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2553)
|
||
|
||
*Remarks*: The exception specification is equivalent tonoexcept(*final-sndr*)[.](#transform-3.sentence-1)
|
||
|
||
### [33.9.7](#transform.env) execution::transform_env [[exec.snd.transform.env]](exec.snd.transform.env)
|
||
|
||
[ð](#lib:transform_env)
|
||
|
||
`namespace std::execution {
|
||
template<class Domain, [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") Sndr, [queryable](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]") Env>
|
||
constexpr [queryable](exec.queryable.concept#concept:queryable "33.2.2 queryable concept [exec.queryable.concept]") decltype(auto) transform_env(Domain dom, Sndr&& sndr, Env&& env) noexcept;
|
||
}
|
||
`
|
||
|
||
[1](#transform.env-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2570)
|
||
|
||
Let e be the expressiondom.transform_env(std::forward<Sndr>(sndr), std::forward<Env>(env)) if that expression is well-formed; otherwise,default_domain().transform_env(std::forward<Sndr>(sndr), std::forward<Env>(env))
|
||
|
||
[2](#transform.env-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2580)
|
||
|
||
*Mandates*: noexcept(e) is true[.](#transform.env-2.sentence-1)
|
||
|
||
[3](#transform.env-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2584)
|
||
|
||
*Returns*: e[.](#transform.env-3.sentence-1)
|
||
|
||
### [33.9.8](#apply) execution::apply_sender [[exec.snd.apply]](exec.snd.apply)
|
||
|
||
[ð](#lib:apply_sender)
|
||
|
||
`namespace std::execution {
|
||
template<class Domain, class Tag, [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") Sndr, class... Args>
|
||
constexpr decltype(auto) apply_sender(Domain dom, Tag, Sndr&& sndr, Args&&... args)
|
||
noexcept(see below);
|
||
}
|
||
`
|
||
|
||
[1](#apply-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2601)
|
||
|
||
Let e be the expressiondom.apply_sender(Tag(), std::forward<Sndr>(sndr), std::forward<Args>(args)...) if that expression is well-formed; otherwise,default_domain().apply_sender(Tag(), std::forward<Sndr>(sndr), std::forward<Args>(args)...)
|
||
|
||
[2](#apply-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2611)
|
||
|
||
*Constraints*: The expression e is well-formed[.](#apply-2.sentence-1)
|
||
|
||
[3](#apply-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2615)
|
||
|
||
*Returns*: e[.](#apply-3.sentence-1)
|
||
|
||
[4](#apply-4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2619)
|
||
|
||
*Remarks*: The exception specification is equivalent to noexcept(e)[.](#apply-4.sentence-1)
|
||
|
||
### [33.9.9](#exec.getcomplsigs) execution::get_completion_signatures [[exec.getcomplsigs]](exec.getcomplsigs)
|
||
|
||
[ð](#exec.getcomplsigs-itemdecl:1)
|
||
|
||
`template<class Sndr, class... Env>
|
||
consteval auto get_completion_signatures() -> [valid-completion-signatures](execution.syn#concept:valid-completion-signatures "33.4 Header <execution> synopsis [execution.syn]") auto;
|
||
`
|
||
|
||
[1](#exec.getcomplsigs-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2634)
|
||
|
||
Let except be an rvalue subexpression of
|
||
an unspecified class type Except such that[move_constructible](concept.moveconstructible#concept:move_constructible "18.4.13 Concept move_constructible [concept.moveconstructible]")<Except> && [derived_from](concept.derived#concept:derived_from "18.4.3 Concept derived_from [concept.derived]")<Except, exception> is true[.](#exec.getcomplsigs-1.sentence-1)
|
||
|
||
Let *CHECKED-COMPLSIGS*(e) be e if e is a core constant expression whose
|
||
type satisfies [*valid-completion-signatures*](execution.syn#concept:valid-completion-signatures "33.4 Header <execution> synopsis [execution.syn]");
|
||
otherwise, it is the following expression:(e, throw except, completion_signatures())
|
||
|
||
Let *get-complsigs*<Sndr, Env...>() be expression-equivalent toremove_reference_t<Sndr>::template get_completion_signatures<Sndr, Env...>()[.](#exec.getcomplsigs-1.sentence-3)
|
||
|
||
Let NewSndr be Sndr if sizeof...(Env) == 0 is true;
|
||
otherwise, decltype(s) where s is the following expression:transform_sender(*get-domain-late*(declval<Sndr>(), declval<Env>()...),
|
||
declval<Sndr>(),
|
||
declval<Env>()...)
|
||
|
||
[2](#exec.getcomplsigs-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2660)
|
||
|
||
*Constraints*: sizeof...(Env) <= 1 is true[.](#exec.getcomplsigs-2.sentence-1)
|
||
|
||
[3](#exec.getcomplsigs-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2664)
|
||
|
||
*Effects*: Equivalent to: return e; where e is expression-equivalent to the following:
|
||
|
||
- [(3.1)](#exec.getcomplsigs-3.1)
|
||
|
||
*CHECKED-COMPLSIGS*(*get-complsigs*<NewSndr, Env...>()) if *get-complsigs*<NewSndr, Env
|
||
...>() is a well-formed expression[.](#exec.getcomplsigs-3.1.sentence-1)
|
||
|
||
- [(3.2)](#exec.getcomplsigs-3.2)
|
||
|
||
Otherwise,*CHECKED-COMPLSIGS*(*get-complsigs*<NewSndr>()) if *get-complsigs*<NewSndr>() is a well-formed expression[.](#exec.getcomplsigs-3.2.sentence-1)
|
||
|
||
- [(3.3)](#exec.getcomplsigs-3.3)
|
||
|
||
Otherwise,completion_signatures<*SET-VALUE-SIG*(*await-result-type*<NewSndr, *env-promise*<Env>...>), // [[exec.snd.concepts]](#concepts "33.9.3 Sender concepts") set_error_t(exception_ptr),
|
||
set_stopped_t()> if [*is-awaitable*](#concept:is-awaitable "33.9.4 Awaitable helpers [exec.awaitable]")<NewSndr, *env-promise*<Env>...> is true[.](#exec.getcomplsigs-3.3.sentence-1)
|
||
|
||
- [(3.4)](#exec.getcomplsigs-3.4)
|
||
|
||
Otherwise,(throw *dependent-sender-error*(), completion_signatures()) if sizeof...(
|
||
Env) == 0 is true,
|
||
where *dependent-sender-error* isdependent_sender_error or
|
||
an unspecified type derived publicly and unambiguously fromdependent_sender_error[.](#exec.getcomplsigs-3.4.sentence-1)
|
||
|
||
- [(3.5)](#exec.getcomplsigs-3.5)
|
||
|
||
Otherwise,(throw except, completion_signatures())[.](#exec.getcomplsigs-3.5.sentence-1)
|
||
|
||
[4](#exec.getcomplsigs-4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2706)
|
||
|
||
Given a type Env, ifcompletion_signatures_of_t<Sndr> andcompletion_signatures_of_t<Sndr, Env> are both well-formed,
|
||
they shall denote the same type[.](#exec.getcomplsigs-4.sentence-1)
|
||
|
||
[5](#exec.getcomplsigs-5)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2713)
|
||
|
||
Let rcvr be an rvalue
|
||
whose type Rcvr models [receiver](exec.recv.concepts#concept:receiver "33.7.1 Receiver concepts [exec.recv.concepts]"), and
|
||
let Sndr be the type of a sender
|
||
such that [sender_in](#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]")<Sndr, env_of_t<Rcvr>> is true[.](#exec.getcomplsigs-5.sentence-1)
|
||
|
||
Let Sigs... be the template arguments of
|
||
the completion_signatures specialization
|
||
named by completion_signatures_of_t<Sndr, env_of_t<Rcvr>>[.](#exec.getcomplsigs-5.sentence-2)
|
||
|
||
Let CSO be a completion function[.](#exec.getcomplsigs-5.sentence-3)
|
||
|
||
If sender Sndr or its operation state cause
|
||
the expression CSO(rcvr, args...) to be potentially evaluated ([[basic.def.odr]](basic.def.odr "6.3 One-definition rule"))
|
||
then there shall be a signature Sig in Sigs... such that*MATCHING-SIG*(*decayed-typeof*<CSO>(decltype(args)...), Sig) is true ([[exec.general]](exec.general "33.1 General"))[.](#exec.getcomplsigs-5.sentence-4)
|
||
|
||
### [33.9.10](#exec.connect) execution::connect [[exec.connect]](exec.connect)
|
||
|
||
[1](#exec.connect-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2734)
|
||
|
||
connect connects ([[exec.async.ops]](exec.async.ops "33.3 Asynchronous operations")) a sender with a receiver[.](#exec.connect-1.sentence-1)
|
||
|
||
[2](#exec.connect-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2737)
|
||
|
||
The name connect denotes a customization point object[.](#exec.connect-2.sentence-1)
|
||
|
||
For subexpressions sndr and rcvr,
|
||
let Sndr be decltype((sndr)) andRcvr be decltype((rcvr)),
|
||
let new_sndr be the expressiontransform_sender(decltype(*get-domain-late*(sndr, get_env(rcvr))){}, sndr, get_env(rcvr)) and let DS and DR bedecay_t<decltype((new_sndr))> and decay_t<Rcvr>, respectively[.](#exec.connect-2.sentence-2)
|
||
|
||
[3](#exec.connect-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2749)
|
||
|
||
Let *connect-awaitable-promise* be the following exposition-only class:
|
||
|
||
namespace std::execution {struct *connect-awaitable-promise* : *with-await-transform*<*connect-awaitable-promise*> {*connect-awaitable-promise*(DS&, DR& rcvr) noexcept : *rcvr*(rcvr) {} suspend_always initial_suspend() noexcept { return {}; }[[noreturn]] suspend_always final_suspend() noexcept { terminate(); }[[noreturn]] void unhandled_exception() noexcept { terminate(); }[[noreturn]] void return_void() noexcept { terminate(); } coroutine_handle<> unhandled_stopped() noexcept { set_stopped(std::move(*rcvr*)); return noop_coroutine(); }*operation-state-task* get_return_object() noexcept {return *operation-state-task*{ coroutine_handle<*connect-awaitable-promise*>::from_promise(*this)}; } env_of_t<DR> get_env() const noexcept {return execution::get_env(*rcvr*); }private: DR& *rcvr*; // *exposition only*};}
|
||
|
||
[4](#exec.connect-4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2783)
|
||
|
||
Let *operation-state-task* be the following exposition-only class:namespace std::execution {struct *operation-state-task* { // *exposition only*using operation_state_concept = operation_state_t; using promise_type = *connect-awaitable-promise*; explicit *operation-state-task*(coroutine_handle<> h) noexcept : coro(h) {}*operation-state-task*(*operation-state-task*&&) = delete; ~*operation-state-task*() { *coro*.destroy(); }void start() & noexcept {*coro*.resume(); }private: coroutine_handle<> *coro*; // *exposition only*};}
|
||
|
||
[5](#exec.connect-5)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2805)
|
||
|
||
Let V name the type*await-result-type*<DS, *connect-awaitable-promise*>,
|
||
let Sigs name the typecompletion_signatures<*SET-VALUE-SIG*(V), // see [[exec.snd.concepts]](#concepts "33.9.3 Sender concepts") set_error_t(exception_ptr),
|
||
set_stopped_t()> and let *connect-awaitable* be an exposition-only coroutine
|
||
defined as follows:namespace std::execution {template<class Fun, class... Ts>auto *suspend-complete*(Fun fun, Ts&&... as) noexcept { // *exposition only*auto fn = [&, fun]() noexcept { fun(std::forward<Ts>(as)...); }; struct awaiter {decltype(fn) *fn*; // *exposition only*static constexpr bool await_ready() noexcept { return false; }void await_suspend(coroutine_handle<>) noexcept { *fn*(); }[[noreturn]] void await_resume() noexcept { unreachable(); }}; return awaiter{fn}; }*operation-state-task* *connect-awaitable*(DS sndr, DR rcvr) requires [receiver_of](exec.recv.concepts#concept:receiver_of "33.7.1 Receiver concepts [exec.recv.concepts]")<DR, Sigs> { exception_ptr ep; try {if constexpr ([same_as](concept.same#concept:same_as "18.4.2 Concept same_as [concept.same]")<V, void>) {co_await std::move(sndr); co_await *suspend-complete*(set_value, std::move(rcvr)); } else {co_await *suspend-complete*(set_value, std::move(rcvr), co_await std::move(sndr)); }} catch(...) { ep = current_exception(); }co_await *suspend-complete*(set_error, std::move(rcvr), std::move(ep)); }}
|
||
|
||
[6](#exec.connect-6)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2850)
|
||
|
||
The expression connect(sndr, rcvr) is expression-equivalent to:
|
||
|
||
- [(6.1)](#exec.connect-6.1)
|
||
|
||
new_sndr.connect(rcvr) if that expression is well-formed[.](#exec.connect-6.1.sentence-1)
|
||
*Mandates*: The type of the expression above satisfies [operation_state](exec.opstate.general#concept:operation_state "33.8.1 General [exec.opstate.general]")[.](#exec.connect-6.1.sentence-2)
|
||
|
||
- [(6.2)](#exec.connect-6.2)
|
||
|
||
Otherwise, *connect-awaitable*(new_sndr, rcvr)[.](#exec.connect-6.2.sentence-1)
|
||
|
||
Except that rcvr is evaluated only once[.](#exec.connect-6.sentence-2)
|
||
|
||
*Mandates*: The following are true:
|
||
|
||
- [(6.3)](#exec.connect-6.3)
|
||
|
||
[sender_in](#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]")<Sndr, env_of_t<Rcvr>>
|
||
|
||
- [(6.4)](#exec.connect-6.4)
|
||
|
||
[receiver_of](exec.recv.concepts#concept:receiver_of "33.7.1 Receiver concepts [exec.recv.concepts]")<Rcvr, completion_signatures_of_t<Sndr, env_of_t<Rcvr>>>
|
||
|
||
### [33.9.11](#exec.factories) Sender factories [[exec.factories]](exec.factories)
|
||
|
||
#### [33.9.11.1](#exec.schedule) execution::schedule [[exec.schedule]](exec.schedule)
|
||
|
||
[1](#exec.schedule-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2877)
|
||
|
||
schedule obtains a schedule sender ([[exec.async.ops]](exec.async.ops "33.3 Asynchronous operations"))
|
||
from a scheduler[.](#exec.schedule-1.sentence-1)
|
||
|
||
[2](#exec.schedule-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2881)
|
||
|
||
The name schedule denotes a customization point object[.](#exec.schedule-2.sentence-1)
|
||
|
||
For a subexpression sch,
|
||
the expression schedule(sch) is expression-equivalent tosch.schedule()[.](#exec.schedule-2.sentence-2)
|
||
|
||
[3](#exec.schedule-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2887)
|
||
|
||
*Mandates*: The type of sch.schedule() satisfies [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]")[.](#exec.schedule-3.sentence-1)
|
||
|
||
[4](#exec.schedule-4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2891)
|
||
|
||
If the expressionget_completion_scheduler<set_value_t>(get_env(sch.schedule())) == sch is ill-formed or evaluates to false,
|
||
the behavior of calling schedule(sch) is undefined[.](#exec.schedule-4.sentence-1)
|
||
|
||
#### [33.9.11.2](#exec.just) execution::just, execution::just_error, execution::just_stopped [[exec.just]](exec.just)
|
||
|
||
[1](#exec.just-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2901)
|
||
|
||
just, just_error, and just_stopped are sender factories
|
||
whose asynchronous operations complete synchronously in their start operation
|
||
with a value completion operation,
|
||
an error completion operation, or
|
||
a stopped completion operation, respectively[.](#exec.just-1.sentence-1)
|
||
|
||
[2](#exec.just-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2908)
|
||
|
||
The names just, just_error, and just_stopped denote
|
||
customization point objects[.](#exec.just-2.sentence-1)
|
||
|
||
Let *just-cpo* be one ofjust, just_error, or just_stopped[.](#exec.just-2.sentence-2)
|
||
|
||
For a pack of subexpressions ts,
|
||
let Ts be the pack of types decltype((ts))[.](#exec.just-2.sentence-3)
|
||
|
||
The expression *just-cpo*(ts...) is ill-formed if
|
||
|
||
- [(2.1)](#exec.just-2.1)
|
||
|
||
([*movable-value*](exec.general#concept:movable-value "33.1 General [exec.general]")<Ts> &&...) is false, or
|
||
|
||
- [(2.2)](#exec.just-2.2)
|
||
|
||
*just-cpo* is just_error andsizeof...(ts) == 1 is false, or
|
||
|
||
- [(2.3)](#exec.just-2.3)
|
||
|
||
*just-cpo* is just_stopped andsizeof...(ts) == 0 is false[.](#exec.just-2.sentence-4)
|
||
|
||
Otherwise, it is expression-equivalent to*make-sender*(*just-cpo*, *product-type*{ts...})[.](#exec.just-2.sentence-5)
|
||
|
||
For just, just_error, and just_stopped,
|
||
let *set-cpo* beset_value, set_error, and set_stopped, respectively[.](#exec.just-2.sentence-6)
|
||
|
||
The exposition-only class template *impls-for* ([[exec.snd.expos]](#expos "33.9.2 Exposition-only entities"))
|
||
is specialized for *just-cpo* as follows:namespace std::execution {template<>struct *impls-for*<*decayed-typeof*<*just-cpo*>> : *default-impls* {static constexpr auto *start* =[](auto& state, auto& rcvr) noexcept -> void {auto& [...ts] = state; *set-cpo*(std::move(rcvr), std::move(ts)...); }; };}
|
||
|
||
#### [33.9.11.3](#exec.read.env) execution::read_env [[exec.read.env]](exec.read.env)
|
||
|
||
[1](#exec.read.env-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2950)
|
||
|
||
read_env is a sender factory for a sender
|
||
whose asynchronous operation completes synchronously in its start operation
|
||
with a value completion result equal to
|
||
a value read from the receiver's associated environment[.](#exec.read.env-1.sentence-1)
|
||
|
||
[2](#exec.read.env-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2956)
|
||
|
||
read_env is a customization point object[.](#exec.read.env-2.sentence-1)
|
||
|
||
For some query object q,
|
||
the expression read_env(q) is expression-equivalent to*make-sender*(read_env, q)[.](#exec.read.env-2.sentence-2)
|
||
|
||
[3](#exec.read.env-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2962)
|
||
|
||
The exposition-only class template *impls-for* ([[exec.snd.expos]](#expos "33.9.2 Exposition-only entities"))
|
||
is specialized for read_env as follows:
|
||
|
||
[ð](#lib:impls-for%3cdecayed-typeof%3cread_env%3e%3e)
|
||
|
||
namespace std::execution {template<>struct *impls-for*<*decayed-typeof*<read_env>> : *default-impls* {static constexpr auto start =[](auto query, auto& rcvr) noexcept -> void {*TRY-SET-VALUE*(rcvr, query(get_env(rcvr))); }; }; template<class Sndr, class Env>static consteval void *check-types*();}
|
||
|
||
[ð](#lib:check-types,impls-for%3cdecayed-typeof%3cread_env%3e%3e)
|
||
|
||
`template<class Sndr, class Env>
|
||
static consteval void check-types();
|
||
`
|
||
|
||
[4](#exec.read.env-4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2988)
|
||
|
||
Let Q be decay_t<*data-type*<Sndr>>[.](#exec.read.env-4.sentence-1)
|
||
|
||
[5](#exec.read.env-5)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L2991)
|
||
|
||
*Throws*: An exception of an unspecified type derived from exception if
|
||
the expression Q()(env) is ill-formed or has type void, whereenv is an lvalue subexpression whose type is Env[.](#exec.read.env-5.sentence-1)
|
||
|
||
### [33.9.12](#exec.adapt) Sender adaptors [[exec.adapt]](exec.adapt)
|
||
|
||
#### [33.9.12.1](#exec.adapt.general) General [[exec.adapt.general]](exec.adapt.general)
|
||
|
||
[1](#exec.adapt.general-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3002)
|
||
|
||
Subclause [[exec.adapt]](#exec.adapt "33.9.12 Sender adaptors") specifies a set of sender adaptors[.](#exec.adapt.general-1.sentence-1)
|
||
|
||
[2](#exec.adapt.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[.](#exec.adapt.general-2.sentence-1)
|
||
|
||
The adaptors also support function call syntax with equivalent semantics[.](#exec.adapt.general-2.sentence-2)
|
||
|
||
[3](#exec.adapt.general-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L3010)
|
||
|
||
Unless otherwise specified:
|
||
|
||
- [(3.1)](#exec.adapt.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[.](#exec.adapt.general-3.1.sentence-1)
|
||
|
||
- [(3.2)](#exec.adapt.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"))[.](#exec.adapt.general-3.2.sentence-1)
|
||
|
||
- [(3.3)](#exec.adapt.general-3.3)
|
||
|
||
A parent sender with more than one child sender has
|
||
an associated attributes object equal to env<>{}[.](#exec.adapt.general-3.3.sentence-1)
|
||
|
||
- [(3.4)](#exec.adapt.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))[.](#exec.adapt.general-3.4.sentence-1)
|
||
|
||
- [(3.5)](#exec.adapt.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[.](#exec.adapt.general-3.5.sentence-1)
|
||
|
||
- [(3.6)](#exec.adapt.general-3.6)
|
||
|
||
These requirements apply to any function
|
||
that is selected by the implementation of the sender adaptor[.](#exec.adapt.general-3.6.sentence-1)
|
||
|
||
- [(3.7)](#exec.adapt.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[.](#exec.adapt.general-3.7.sentence-1)
|
||
|
||
[4](#exec.adapt.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]](#exec.adapt "33.9.12 Sender adaptors") 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[.](#exec.adapt.general-4.sentence-1)
|
||
|
||
#### [33.9.12.2](#exec.adapt.obj) Closure objects [[exec.adapt.obj]](exec.adapt.obj)
|
||
|
||
[1](#exec.adapt.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](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") arguments and returns a [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]")[.](#exec.adapt.obj-1.sentence-1)
|
||
|
||
For a pipeable sender adaptor closure object c and
|
||
an expression sndr such that decltype((sndr)) models [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"),
|
||
the following expressions are equivalent and yield a [sender](#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)](#exec.adapt.obj-1.1)
|
||
|
||
Its target object is an object d2 of type decltype(auto(d)) direct-non-list-initialized with d[.](#exec.adapt.obj-1.1.sentence-1)
|
||
|
||
- [(1.2)](#exec.adapt.obj-1.2)
|
||
|
||
It has one bound argument entity,
|
||
an object c2 of type decltype(auto(c)) direct-non-list-initialized with c[.](#exec.adapt.obj-1.2.sentence-1)
|
||
|
||
- [(1.3)](#exec.adapt.obj-1.3)
|
||
|
||
Its call pattern is d2(c2(arg)),
|
||
where arg is the argument used in a function call expression of e[.](#exec.adapt.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[.](#exec.adapt.obj-1.sentence-5)
|
||
|
||
[2](#exec.adapt.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](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]")[.](#exec.adapt.obj-2.sentence-1)
|
||
|
||
[3](#exec.adapt.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[.](#exec.adapt.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>>[.](#exec.adapt.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[.](#exec.adapt.obj-3.sentence-3)
|
||
|
||
[4](#exec.adapt.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](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") as its first argument and
|
||
returns a [sender](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]")[.](#exec.adapt.obj-4.sentence-1)
|
||
|
||
If a pipeable sender adaptor object accepts only one argument,
|
||
then it is a pipeable sender adaptor closure object[.](#exec.adapt.obj-4.sentence-2)
|
||
|
||
[5](#exec.adapt.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](#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))...[.](#exec.adapt.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)](#exec.adapt.obj-5.1)
|
||
|
||
Its target object is a copy of adaptor[.](#exec.adapt.obj-5.1.sentence-1)
|
||
|
||
- [(5.2)](#exec.adapt.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[.](#exec.adapt.obj-5.2.sentence-1)
|
||
|
||
- [(5.3)](#exec.adapt.obj-5.3)
|
||
|
||
Its call pattern is adaptor(rcvr, bound_args...),
|
||
where rcvr is
|
||
the argument used in a function call expression of f[.](#exec.adapt.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[.](#exec.adapt.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](#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]](#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](#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*](#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](#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](#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]](#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*](#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](#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](#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]](#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](#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]](#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](#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](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") andsndr is not
|
||
a pipeable sender adaptor closure object ([[exec.adapt.obj]](#exec.adapt.obj "33.9.12.2 Closure objects")), or
|
||
|
||
- [(2.3)](#exec.on-2.3)
|
||
|
||
decltype((sndr)) satisfies [sender](#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](#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](#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]](#exec.adapt.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*](#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](#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](#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](#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]](#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](#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]](#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](#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](#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*](#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](#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*](#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]](#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]](#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](#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](#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]](#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]](#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*](#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](#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]](#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]](#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]](#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*](#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](#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](#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*](#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](#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](#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](#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]](#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](#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](#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](#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](#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]](#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)
|
||
|
||
### [33.9.13](#exec.consumers) Sender consumers [[exec.consumers]](exec.consumers)
|
||
|
||
#### [33.9.13.1](#exec.sync.wait) this_thread::sync_wait [[exec.sync.wait]](exec.sync.wait)
|
||
|
||
[1](#exec.sync.wait-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5595)
|
||
|
||
this_thread::sync_wait and this_thread::sync_wait_with_variant are used
|
||
to block the current thread of execution
|
||
until the specified sender completes and
|
||
to return its async result[.](#exec.sync.wait-1.sentence-1)
|
||
|
||
sync_wait mandates
|
||
that the input sender has exactly one value completion signature[.](#exec.sync.wait-1.sentence-2)
|
||
|
||
[2](#exec.sync.wait-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5604)
|
||
|
||
Let *sync-wait-env* be the following exposition-only class type:namespace std::this_thread {struct *sync-wait-env* { execution::run_loop* *loop*; // *exposition only*auto query(execution::get_scheduler_t) const noexcept {return *loop*->get_scheduler(); }auto query(execution::get_delegation_scheduler_t) const noexcept {return *loop*->get_scheduler(); }};}
|
||
|
||
[3](#exec.sync.wait-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5622)
|
||
|
||
Let *sync-wait-result-type* and*sync-wait-with-variant-result-type* be exposition-only alias templates defined as follows:namespace std::this_thread {template<execution::[sender_in](#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]")<*sync-wait-env*> Sndr>using *sync-wait-result-type* = optional<execution::value_types_of_t<Sndr, *sync-wait-env*, *decayed-tuple*,
|
||
type_identity_t>>; template<execution::[sender_in](#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]")<*sync-wait-env*> Sndr>using *sync-wait-with-variant-result-type* = optional<execution::value_types_of_t<Sndr, *sync-wait-env*>>;}
|
||
|
||
[4](#exec.sync.wait-4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5639)
|
||
|
||
The name this_thread::sync_wait denotes a customization point object[.](#exec.sync.wait-4.sentence-1)
|
||
|
||
For a subexpression sndr, let Sndr be decltype((sndr))[.](#exec.sync.wait-4.sentence-2)
|
||
|
||
The expression this_thread::sync_wait(sndr) is expression-equivalent to the following,
|
||
except that sndr is evaluated only once:apply_sender(*get-domain-early*(sndr), sync_wait, sndr)*Mandates*:
|
||
|
||
- [(4.1)](#exec.sync.wait-4.1)
|
||
|
||
[sender_in](#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]")<Sndr, *sync-wait-env*> is true[.](#exec.sync.wait-4.1.sentence-1)
|
||
|
||
- [(4.2)](#exec.sync.wait-4.2)
|
||
|
||
The type *sync-wait-result-type*<Sndr> is well-formed[.](#exec.sync.wait-4.2.sentence-1)
|
||
|
||
- [(4.3)](#exec.sync.wait-4.3)
|
||
|
||
[same_as](concept.same#concept:same_as "18.4.2 Concept same_as [concept.same]")<decltype(e), *sync-wait-result-type*<Sndr>> is true, where e is the apply_sender expression above[.](#exec.sync.wait-4.3.sentence-1)
|
||
|
||
[5](#exec.sync.wait-5)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5659)
|
||
|
||
Let *sync-wait-state* and *sync-wait-receiver* be the following exposition-only class templates:namespace std::this_thread {template<class Sndr>struct *sync-wait-state* { // *exposition only* execution::run_loop *loop*; // *exposition only* exception_ptr *error*; // *exposition only**sync-wait-result-type*<Sndr> *result*; // *exposition only*}; template<class Sndr>struct *sync-wait-receiver* { // *exposition only*using receiver_concept = execution::receiver_t; *sync-wait-state*<Sndr>* *state*; // *exposition only*template<class... Args>void set_value(Args&&... args) && noexcept; template<class Error>void set_error(Error&& err) && noexcept; void set_stopped() && noexcept; *sync-wait-env* get_env() const noexcept { return {&*state*->*loop*}; }};}
|
||
|
||
[ð](#exec.sync.wait-itemdecl:1)
|
||
|
||
`template<class... Args>
|
||
void set_value(Args&&... args) && noexcept;
|
||
`
|
||
|
||
[6](#exec.sync.wait-6)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5695)
|
||
|
||
*Effects*: Equivalent to:try {*state*->*result*.emplace(std::forward<Args>(args)...);} catch (...) {*state*->*error* = current_exception();}*state*->*loop*.finish();
|
||
|
||
[ð](#exec.sync.wait-itemdecl:2)
|
||
|
||
`template<class Error>
|
||
void set_error(Error&& err) && noexcept;
|
||
`
|
||
|
||
[7](#exec.sync.wait-7)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5714)
|
||
|
||
*Effects*: Equivalent to:*state*->*error* = *AS-EXCEPT-PTR*(std::forward<Error>(err)); // see [[exec.general]](exec.general "33.1 General")*state*->*loop*.finish();
|
||
|
||
[ð](#exec.sync.wait-itemdecl:3)
|
||
|
||
`void set_stopped() && noexcept;
|
||
`
|
||
|
||
[8](#exec.sync.wait-8)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5728)
|
||
|
||
*Effects*: Equivalent to *state*->*loop*.finish()[.](#exec.sync.wait-8.sentence-1)
|
||
|
||
[9](#exec.sync.wait-9)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5733)
|
||
|
||
For a subexpression sndr, let Sndr be decltype((sndr))[.](#exec.sync.wait-9.sentence-1)
|
||
|
||
If [sender_to](#concept:sender_to "33.9.3 Sender concepts [exec.snd.concepts]")<Sndr, *sync-wait-receiver*<Sndr>> is false,
|
||
the expression sync_wait.apply_sender(sndr) is ill-formed;
|
||
otherwise, it is equivalent to:*sync-wait-state*<Sndr> state;auto op = connect(sndr, *sync-wait-receiver*<Sndr>{&state});
|
||
start(op);
|
||
|
||
state.*loop*.run();if (state.*error*) { rethrow_exception(std::move(state.*error*));}return std::move(state.*result*);
|
||
|
||
[10](#exec.sync.wait-10)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5751)
|
||
|
||
The behavior of this_thread::sync_wait(sndr) is undefined unless:
|
||
|
||
- [(10.1)](#exec.sync.wait-10.1)
|
||
|
||
It blocks the current thread of execution ([[defns.block]](defns.block "3.6 block"))
|
||
with forward progress guarantee delegation ([[intro.progress]](intro.progress "6.10.2.3 Forward progress"))
|
||
until the specified sender completes[.](#exec.sync.wait-10.1.sentence-1)
|
||
[*Note [1](#exec.sync.wait-note-1)*:
|
||
The default implementation of sync_wait achieves
|
||
forward progress guarantee delegation by providing a run_loop scheduler
|
||
via the get_delegation_scheduler query
|
||
on the *sync-wait-receiver*'s environment[.](#exec.sync.wait-10.1.sentence-2)
|
||
The run_loop is driven by the current thread of execution[.](#exec.sync.wait-10.1.sentence-3)
|
||
â *end note*]
|
||
|
||
- [(10.2)](#exec.sync.wait-10.2)
|
||
|
||
It returns the specified sender's async results as follows:
|
||
* [(10.2.1)](#exec.sync.wait-10.2.1)
|
||
|
||
For a value completion,
|
||
the result datums are returned in
|
||
a tuple in an engaged optional object[.](#exec.sync.wait-10.2.1.sentence-1)
|
||
|
||
* [(10.2.2)](#exec.sync.wait-10.2.2)
|
||
|
||
For an error completion, an exception is thrown[.](#exec.sync.wait-10.2.2.sentence-1)
|
||
|
||
* [(10.2.3)](#exec.sync.wait-10.2.3)
|
||
|
||
For a stopped completion, a disengaged optional object is returned[.](#exec.sync.wait-10.2.3.sentence-1)
|
||
|
||
#### [33.9.13.2](#exec.sync.wait.var) this_thread::sync_wait_with_variant [[exec.sync.wait.var]](exec.sync.wait.var)
|
||
|
||
[1](#exec.sync.wait.var-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5781)
|
||
|
||
The name this_thread::sync_wait_with_variant denotes
|
||
a customization point object[.](#exec.sync.wait.var-1.sentence-1)
|
||
|
||
For a subexpression sndr,
|
||
let Sndr be decltype(into_variant(sndr))[.](#exec.sync.wait.var-1.sentence-2)
|
||
|
||
The expression this_thread::sync_wait_with_variant(sndr) is expression-equivalent to the following,
|
||
except sndr is evaluated only once:apply_sender(*get-domain-early*(sndr), sync_wait_with_variant, sndr)*Mandates*:
|
||
|
||
- [(1.1)](#exec.sync.wait.var-1.1)
|
||
|
||
[sender_in](#concept:sender_in "33.9.3 Sender concepts [exec.snd.concepts]")<Sndr, *sync-wait-env*> is true[.](#exec.sync.wait.var-1.1.sentence-1)
|
||
|
||
- [(1.2)](#exec.sync.wait.var-1.2)
|
||
|
||
The type *sync-wait-with-variant-result-type*<Sndr> is well-formed[.](#exec.sync.wait.var-1.2.sentence-1)
|
||
|
||
- [(1.3)](#exec.sync.wait.var-1.3)
|
||
|
||
[same_as](concept.same#concept:same_as "18.4.2 Concept same_as [concept.same]")<decltype(e), *sync-wait-with-variant-result-type*<Sndr>> is true, where e is the apply_sender expression above[.](#exec.sync.wait.var-1.3.sentence-1)
|
||
|
||
[2](#exec.sync.wait.var-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5804)
|
||
|
||
The expression sync_wait_with_variant.apply_sender(sndr) is equivalent to:using result_type = *sync-wait-with-variant-result-type*<Sndr>;if (auto opt_value = sync_wait(into_variant(sndr))) {return result_type(std::move(get<0>(*opt_value)));}return result_type(nullopt);
|
||
|
||
[3](#exec.sync.wait.var-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5814)
|
||
|
||
The behavior of this_thread::sync_wait_with_variant(sndr) is undefined unless:
|
||
|
||
- [(3.1)](#exec.sync.wait.var-3.1)
|
||
|
||
It blocks the current thread of execution ([[defns.block]](defns.block "3.6 block"))
|
||
with forward progress guarantee delegation ([[intro.progress]](intro.progress "6.10.2.3 Forward progress"))
|
||
until the specified sender completes[.](#exec.sync.wait.var-3.1.sentence-1)
|
||
[*Note [1](#exec.sync.wait.var-note-1)*:
|
||
The default implementation of sync_wait_with_variant achieves
|
||
forward progress guarantee delegation by relying on
|
||
the forward progress guarantee delegation provided by sync_wait[.](#exec.sync.wait.var-3.1.sentence-2)
|
||
â *end note*]
|
||
|
||
- [(3.2)](#exec.sync.wait.var-3.2)
|
||
|
||
It returns the specified sender's async results as follows:
|
||
* [(3.2.1)](#exec.sync.wait.var-3.2.1)
|
||
|
||
For a value completion,
|
||
the result datums are returned in an engaged optional object
|
||
that contains a variant of tuples[.](#exec.sync.wait.var-3.2.1.sentence-1)
|
||
|
||
* [(3.2.2)](#exec.sync.wait.var-3.2.2)
|
||
|
||
For an error completion, an exception is thrown[.](#exec.sync.wait.var-3.2.2.sentence-1)
|
||
|
||
* [(3.2.3)](#exec.sync.wait.var-3.2.3)
|
||
|
||
For a stopped completion, a disengaged optional object is returned[.](#exec.sync.wait.var-3.2.3.sentence-1)
|
||
|
||
#### [33.9.13.3](#exec.spawn) execution::spawn [[exec.spawn]](exec.spawn)
|
||
|
||
[1](#exec.spawn-1)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5843)
|
||
|
||
spawn attempts to associate the given input sender with
|
||
the given token's async scope and, on success,
|
||
eagerly starts the input sender[.](#exec.spawn-1.sentence-1)
|
||
|
||
[2](#exec.spawn-2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5848)
|
||
|
||
The name spawn denotes a customization point object[.](#exec.spawn-2.sentence-1)
|
||
|
||
For subexpressions sndr, token, and env,
|
||
|
||
- [(2.1)](#exec.spawn-2.1)
|
||
|
||
let Sndr be decltype((sndr)),
|
||
|
||
- [(2.2)](#exec.spawn-2.2)
|
||
|
||
let Token be remove_cvref_t<decltype((token))>, and
|
||
|
||
- [(2.3)](#exec.spawn-2.3)
|
||
|
||
let Env be remove_cvref_t<decltype((env))>[.](#exec.spawn-2.sentence-2)
|
||
|
||
If any of[sender](#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(sndr, token, env) is ill-formed[.](#exec.spawn-2.sentence-3)
|
||
|
||
[3](#exec.spawn-3)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5863)
|
||
|
||
Let *spawn-state-base* be the exposition-only class:
|
||
|
||
[ð](#lib:execution::spawn-state-base)
|
||
|
||
namespace std::execution {struct *spawn-state-base* { // *exposition only*virtual void *complete*() noexcept = 0; // *exposition only*};}
|
||
|
||
[4](#exec.spawn-4)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5875)
|
||
|
||
Let *spawn-receiver* be the exposition-only class:
|
||
|
||
[ð](#lib:execution::spawn-receiver)
|
||
|
||
namespace std::execution {struct *spawn-receiver* { // *exposition only*using receiver_concept = receiver_t; *spawn-state-base** *state*; // *exposition only*void set_value() && noexcept { *state*->*complete*(); }void set_stopped() && noexcept { *state*->*complete*(); }};}
|
||
|
||
[5](#exec.spawn-5)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5891)
|
||
|
||
Let *spawn-state* be the exposition-only class template:
|
||
|
||
[ð](#lib:execution::spawn-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](#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") Sender>struct *spawn-state* : *spawn-state-base* { // *exposition only*using *op-t* = connect_result_t<Sender, *spawn-receiver*>; // *exposition only**spawn-state*(Alloc alloc, Sender&& sndr, Token token); // *exposition only*void *complete*() noexcept override; // *exposition only*void *run*(); // *exposition only*private:using *alloc-t* = // *exposition only*typename allocator_traits<Alloc>::template rebind_alloc<*spawn-state*>; *alloc-t* *alloc*; // *exposition only**op-t* *op*; // *exposition only* Token *token*; // *exposition only*void *destroy*() noexcept; // *exposition only*};}
|
||
|
||
[ð](#lib:execution::spawn-state,constructor)
|
||
|
||
`spawn-state(Alloc alloc, Sender&& sndr, Token token);
|
||
`
|
||
|
||
[6](#exec.spawn-6)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5924)
|
||
|
||
*Effects*: Initializes*alloc* with alloc,*token* with token, and*op* with:connect(std::move(sndr), *spawn-receiver*(this))
|
||
|
||
[ð](#lib:run,execution::spawn-state)
|
||
|
||
`void run();
|
||
`
|
||
|
||
[7](#exec.spawn-7)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5941)
|
||
|
||
*Effects*: Equivalent to:if (*token*.try_associate()) start(*op*);else*destroy*();
|
||
|
||
[ð](#lib:complete,execution::spawn-state)
|
||
|
||
`void complete() noexcept override;
|
||
`
|
||
|
||
[8](#exec.spawn-8)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5958)
|
||
|
||
*Effects*: Equivalent to:auto token = std::move(this->*token*);
|
||
|
||
*destroy*();
|
||
token.disassociate();
|
||
|
||
[ð](#lib:destroy,execution::spawn-state)
|
||
|
||
`void destroy() noexcept;
|
||
`
|
||
|
||
[9](#exec.spawn-9)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5975)
|
||
|
||
*Effects*: Equivalent to:auto alloc = std::move(this->*alloc*);
|
||
|
||
allocator_traits<*alloc-t*>::destroy(alloc, this);
|
||
allocator_traits<*alloc-t*>::deallocate(alloc, this, 1);
|
||
|
||
[10](#exec.spawn-10)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5986)
|
||
|
||
For the expression spawn(sndr, token, env) let new_sender be the expression token.wrap(sndr) and
|
||
let alloc and senv be defined as follows:
|
||
|
||
- [(10.1)](#exec.spawn-10.1)
|
||
|
||
if the expression get_allocator(env) is well-formed, thenalloc is the result of get_allocator(env) andsenv is the expression env,
|
||
|
||
- [(10.2)](#exec.spawn-10.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),
|
||
|
||
- [(10.3)](#exec.spawn-10.3)
|
||
|
||
otherwisealloc is allocator<void>() andsenv is the expression env[.](#exec.spawn-10.sentence-1)
|
||
|
||
[11](#exec.spawn-11)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6006)
|
||
|
||
The expression spawn(sndr, token, env) is of type void and
|
||
has the following effects:
|
||
|
||
- [(11.1)](#exec.spawn-11.1)
|
||
|
||
Uses alloc to allocate and construct an object o of
|
||
type that is a specialization of *spawn-state* fromalloc, write_env(token.wrap(sndr), senv), and token and then
|
||
invokes o.*run*()[.](#exec.spawn-11.sentence-1)
|
||
If an exception is thrown then
|
||
any constructed objects are destroyed and any allocated memory is deallocated[.](#exec.spawn-11.1.sentence-2)
|
||
|
||
[12](#exec.spawn-12)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L6021)
|
||
|
||
The expression spawn(sndr, token) is expression-equivalent tospawn(sndr, token, execution::env<>())[.](#exec.spawn-12.sentence-1)
|