168 lines
8.9 KiB
Markdown
168 lines
8.9 KiB
Markdown
[exec.associate]
|
||
|
||
# 33 Execution control library [[exec]](./#exec)
|
||
|
||
## 33.9 Senders [[exec.snd]](exec.snd#exec.associate)
|
||
|
||
### 33.9.12 Sender adaptors [[exec.adapt]](exec.adapt#exec.associate)
|
||
|
||
#### 33.9.12.16 execution::associate [exec.associate]
|
||
|
||
[1](#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[.](#1.sentence-1)
|
||
|
||
[2](#2)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4903)
|
||
|
||
Let *associate-data* be the following exposition-only class template:
|
||
|
||
[ð](#lib:execution::associate-data)
|
||
|
||
namespace std::execution {template<[scope_token](exec.scope.concepts#concept:scope_token "33.14.1 Execution scope concepts [exec.scope.concepts]") Token, [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") Sender>struct *associate-data* { // *exposition only*using *wrap-sender* = // *exposition only* remove_cvref_t<decltype(declval<Token&>().wrap(declval<Sender>()))>; explicit *associate-data*(Token t, Sender&& s): *sndr*(t.wrap(std::forward<Sender>(s))), *token*(t) {if (!*token*.try_associate())*sndr*.reset(); }*associate-data*(const *associate-data*& other)noexcept(is_nothrow_copy_constructible_v<*wrap-sender*> &&noexcept(other.*token*.try_associate())); *associate-data*(*associate-data*&& other)noexcept(is_nothrow_move_constructible_v<*wrap-sender*>); ~*associate-data*();
|
||
|
||
optional<pair<Token, *wrap-sender*>> release() && noexcept(is_nothrow_move_constructible_v<*wrap-sender*>); private: optional<*wrap-sender*> *sndr*; // *exposition only* Token *token*; // *exposition only*}; template<[scope_token](exec.scope.concepts#concept:scope_token "33.14.1 Execution scope concepts [exec.scope.concepts]") Token, [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]") Sender>*associate-data*(Token, Sender&&) -> *associate-data*<Token, Sender>;}
|
||
|
||
[3](#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[.](#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](#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[.](#4.sentence-1)
|
||
|
||
[5](#5)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L4961)
|
||
|
||
*Effects*: Value-initializes *sndr* and
|
||
initializes *token* with other.*token*[.](#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[.](#5.sentence-2)
|
||
|
||
[ð](#lib:execution::associate-data,constructor_)
|
||
|
||
`associate-data(associate-data&& other)
|
||
noexcept(is_nothrow_move_constructible_v<wrap-sender>);
|
||
`
|
||
|
||
[6](#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()[.](#6.sentence-1)
|
||
|
||
[ð](#lib:execution::associate-data,destructor)
|
||
|
||
`~associate-data();
|
||
`
|
||
|
||
[7](#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()[.](#7.sentence-1)
|
||
|
||
[ð](#lib:release,execution::associate-data)
|
||
|
||
`optional<pair<Token, wrap-sender>>
|
||
release() && noexcept(is_nothrow_move_constructible_v<wrap-sender>);
|
||
`
|
||
|
||
[8](#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](#9)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5020)
|
||
|
||
*Postconditions*: *sndr* does not contain a value[.](#9.sentence-1)
|
||
|
||
[10](#10)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5025)
|
||
|
||
The name associate denotes a pipeable sender adaptor object[.](#10.sentence-1)
|
||
|
||
For subexpressions sndr and token:
|
||
|
||
- [(10.1)](#10.1)
|
||
|
||
If decltype((sndr)) does not satisfy [sender](exec.snd.concepts#concept:sender "33.9.3 Sender concepts [exec.snd.concepts]"), orremove_cvref_t<decltype((token))> does not satisfy [scope_token](exec.scope.concepts#concept:scope_token "33.14.1 Execution scope concepts [exec.scope.concepts]"), thenassociate(sndr, token) is ill-formed.
|
||
|
||
- [(10.2)](#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](#11)
|
||
|
||
[#](http://github.com/Eelis/draft/tree/9adde4bc1c62ec234483e63ea3b70a59724c745a/source/exec.tex#L5045)
|
||
|
||
The exposition-only class template *impls-for* ([[exec.snd.expos]](exec.snd.expos "33.9.2 Exposition-only entities"))
|
||
is specialized for associate_t as follows:
|
||
|
||
[ð](#lib:execution::impls-for%3cassociate_t%3e)
|
||
|
||
namespace std::execution {template<>struct *impls-for*<associate_t> : *default-impls* {static constexpr auto *get-state* = *see below*; // *exposition only*static constexpr auto *start* = *see below*; // *exposition only*template<class Sndr, class... Env>static consteval void *check-types*() { // *exposition only*using associate_data_t = remove_cvref_t<*data-type*<Sndr>>; using child_type_t = typename associate_data_t::*wrap-sender*; (void)get_completion_signatures<child_type_t, *FWD-ENV-T*(Env)...>(); }};}
|
||
|
||
[12](#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](#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*[.](#13.sentence-1)
|
||
|
||
[14](#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](#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[.](#15.sentence-1)
|