1. Introduction
This paper proposes a refactorization of the receiver concepts of [P2300R4] to
address concerns raised by LEWG during its design review related to the
requirement of an error channel that accepts
. The change to
proposed herein enables a corresponding change to the
concept that strengthens type checking and removes some need to constrain
customizations of the
customization point.
1.1. Motivation
In [P2300R4], the receiver concepts are currently expressed as follows:
template < class T , class E = exception_ptr > concept receiver = move_constructible < remove_cvref_t < T >> && constructible_from < remove_cvref_t < T > , T > && requires ( remove_cvref_t < T >&& t , E && e ) { { execution :: set_stopped ( std :: move ( t )) } noexcept ; { execution :: set_error ( std :: move ( t ), ( E && ) e ) } noexcept ; }; template < class T , class ... An > concept receiver_of = receiver < T > && requires ( remove_cvref_t < T >&& t , An && ... an ) { execution :: set_value ( std :: move ( t ), ( An && ) an ...); };
During the design review of P2300, LEWG raised the following concerns about the form of these concepts:
-
Since
is permitted to be potentially throwing, and since the receiver type is not known when a sender is asked to compute its completion signatures, most senders will need to pessimistically report that they can complete exceptionally, when that may in fact not be true. This may cause the instantiation of expensive error handling code that is effectively dead.set_value -
No receiver
can satisfy theR
orreceiver < R >
concepts without providing an error channel forreceiver_of < R , As ... >
. This has the following problems:exception_ptr -
is a relatively heavy-weight error type, not unlike aexception_ptr
. Requiring the presence of this channel is likely to cause needless code generation.shared_ptr -
It makes it questionable whether any of P2300 can be reasonably expected to work in freestanding environments, which often lack exception handling support.
-
Although the design of P2300 is sound, LEWG nevertheless wanted an investigation into these issues and a recommendation to be made.
This paper makes a recommendation to change the receiver concepts to address these concerns.
1.2. Design Summary
This paper proposes to make the following changes, summarized here without commentary. Commentary is provided below.
-
Remove the default implementation of the
receiver query.get_env -
The
concept takes a receiver and an instance of thereceiver_of
class template.completion_signatures <> -
A receiver’s customization of
is required to beset_value
.noexcept -
The
concept requiressender_to < Sndr , Rcvr >
to accept all ofRcvr
's completions.Sndr -
also requiresconnect ( sndr , rcvr )
to accept all ofrcvr
's completions.sndr -
is required to return an instantiation of theget_completion_signatures
class template; thecompletion_signatures
andvalue_types_of_t
template aliases remain unchanged.error_types_of_t -
The
design is slightly tweaked to be more general.make_completion_signatures
1.3. Design Rationale
The author believes these are all reasonable adjustments to the design of P2300, but one may wonder why they were not considered before now.
The fourth revision of P2300 brought with it some notable changes, the two most significant of which are:
-
Support for dependently-typed senders, where a sender’s completions can depend on information that isn’t known independently of the execution environment within which the sender will be initiated. For instance, a
sender which queries the receiver for the current scheduler and then sends it through the value channel, cannot possibly know the type of the scheduler it will send until it has been connected to a receiver.get_scheduler () -
Dropping of support for "untyped" senders, which do not declare their completion signatures. Untyped senders were supported because of the lack of dependently-typed senders, which ceased to be an issue with R4. At the direction of LEWG, "untyped" senders were dropped, greatly simplifying the design.
Taken together, these two changes open up a huge piece of the design space. The implication is that a sender is always able to provide its completion signatures. This is new, and P2300R4 is not taking advantage of this extra type information.
The author realized that the extra type information can be leveraged to
accommodate LEWGs requests regarding the receiver interface, while at the same
time simplifying uses of
by permitting the library to take on
more of the type checking burden.
The
concept, which checks whether a sender and a receiver can be
connected, now has perfect information: it can ask the receiver for the execution
environment; it can ask the sender how it will complete when initiated in that
environment; and it can ask the receiver if it is capable of receiving all of
the sender’s possible completions. This was not possible before R4.
Below we look at each of the changes suggested in the summary and explain its rationale in light of the extra information now available to the type system.
2. Design Details
2.1. Remove the default implementation of the get_env
receiver query.
The presence of a customization of
becomes the distinguishing feature
of receivers. A "receiver" no longer needs to provide any completion channels at
all to be considered a receiver, only
.
2.2. The receiver_of
concept takes a receiver and an instance of the completion_signatures <>
class template.
The
concept, rather than accepting a receiver and some value
types, is changed to take a receiver and an instance of the
class template. A sender uses
to describe the signals with which it completes. The
concept ensures that a particular receiver is capable of receiving
those signals.
Notably, if a sender only sends a value (i.e., can never send an error or a stopped signal), then a receiver need only provide a value channel to be compatible with it.
2.3. A receiver’s customization of set_value
is required to be noexcept
.
This makes it possible for many senders to become "no-fail"; that is, they
cannot complete with an error.
, for instance, will only ever
successfully send an integer through the value channel. An adaptor such as
can check whether
can ever exit exceptionally when
called with all the sets of values that
may complete with. If so, the
sender must add
to its list of completions.
Otherwise, it need not.
2.4. The sender_to < Sndr , Rcvr >
concept requires Rcvr
to accept all of Sndr
's completions.
The
concept, which checks whether a sender and a receiver can be
connected, now enforces that the sender’s completion signatures can in fact be
handled by the receiver. Previously, it only checked that
was well-formed, relying on sender authors to properly constrain their
customizations.
2.5. connect ( sndr , rcvr )
also requires rcvr
to accept all of sndr
's completions.
For good measure, the
customization point also checks whether a
receiver can receive all of the sender’s possible completions before trying to
dispatch via
to a
customization. This often entirely
frees sender authors from having to constrain their
customizations at
all. It is enough to customize
, and the type
checking is done automatically.
Strictly speaking, with this change, the change to
is unnecessary.
The change to
results in better diagnostics, in the author’s
experience.
2.6. get_completion_signatures
is required to return an instantiation of the completion_signatures
class template.
was added in R4 in response to feedback that
authoring sender traits was too difficult/arcane. Rather than defining a struct
with
aliases, a user can simply declare a sender’s
completions as:
execution :: completion_signatures < execution :: set_value_t ( int ), execution :: set_error_t ( std :: exception_ptr ), execution :: set_stopped_t () >
In R4,
generated the
aliases for
you. The proposed change is to take it further and require
to return an instance of the
class template. With this change, the last vestige of the old sender traits
design with its unloved
alias interface is swept away.
entirely replaces sender traits, further simplifying the
design.
The
concept enforces the new requirement.
2.7. The value_types_of_t
and error_types_of_t
template aliases remain.
It can still be helpful sometimes to consume the old
, say,
for generating a variant of the tuples of all the sets of a sender’s value
types. For that reason, the alias templates
and
retain the same interface and semantic as before. For
instance, generating the variant of tuples of value types, you would use the
following:
execution :: value_types_of_t < Sndr , Env , std :: tuple , std :: variant > ;
Additionally, these two alias joined by a
Boolean
variable template to complete the set.
2.8. The make_completion_signatures
design is slightly tweaked to be more general.
In the proposed design,
plays a much larger role.
Accordingly, the job of specifying the completion signatures of custom sender
adaptors also becomes more important, necessitating better tools. The
, new to R4, narrowly misses being that better tool.
In R4,
has the following interface:
template < execution :: sender Sndr , class Env = execution :: no_env , class OtherSigs = execution :: completion_signatures <> , template < class ... > class SetValue = default - set - value , template < class > class SetError = default - set - error , bool SendsStopped = execution :: completion_signatures_of_t < Sndr , Env >:: sends_stopped > requires sender < Sndr , Env > using make_completion_signatures = execution :: completion_signatures < /* see below */ > ;
In the R4 design,
and
are alias templates, instantiations
of which are required to name function types whose return types are
and
, respectively. This is
overly-restrictive. The problems with it are:
-
It is not possible to map one kind of completion into a different kind. For instance, the
maps error completions into value completions.upon_error ( sndr , fun ) -
It is not possible to map a single completion signature into multiple different completions. For instance, the
sender adaptor needs to map a set oflet_value ( sndr , fun )
's value types into the set of completions of whatever sender that is returned fromsndr
, which is likely more than one.fun ( values ...)
In addition, the final Boolean
parameter merely controls whether
or not the completion
should be added to the
resulting list of completion signatures. This doesn’t help a sender adaptor
such as
, which needs to transform a stopped signal
into the set of completions of the sender that
returns.
This design proposes to change the three final template arguments as follows:
-
: Instantiations of this alias template must name an instantiation of thetemplate < class ... > class SetValue
class template.completion_signatures -
: Instantiations of this alias template must name an instantiation of thetemplate < class > class SetError
class template.completion_signatures -
: Must name an instantiation of theclass SetStopped
class template. If the sendercompletion_signatures
can complete withSndr
, then these signatures are included in the resulting list of completions. Otherwise, this template parameter is ignored.set_stopped
The semantics of
is likewise simplified: The three
template arguments,
,
, and
, are used to map
each of a sender’s completions into a list of completions which are all
concatenated together, along with any additional signatures specified by the
list, and made unique.
3. Considerations
3.1. Implications of noexcept
set_value
The role of
is to execute a continuation on the success
of the predecessor. A continuation is arbitrary code, and surely arbitrary code
can exit exceptionally, so how can we require
to be
?
The answer has two parts:
-
always has the option of accepting arguments by forwarding reference and executing any potentially throwing operations within aexecution :: set_value
/try
block, routing any exceptions tocatch
.set_error ( std :: exception_ptr ) -
A sender knows what types it will send and with what value category. The
concept checks that none of thesender_to
expression(s) it will execute are potentially throwing. This doesn’t necessitate that all receivers accept all arguments by forwarding reference, however. For instance, if a sender knows it will pass an rvalueset_value
to the receiver’sstd :: string
, and if the sender is connected to a receiver whoseset_value
takes aset_value
by value, that will type-check. Thestd :: string
concept will essentially be enforcing this constraint:sender_to requires ( Receiver rcvr ) { { execution :: set_value ( std :: move ( rcvr ), std :: string ()) } noexcept ; } Since
's move constructor isstd :: string
, this constraint is satisfied regardless of whethernoexcept
'srcvr
customization accepts the string by value or by reference.set_value
3.2. Diagnostics
On the whole, the authors of P2300 feel that this design change is the right one
to make to meet LEWG’s requirements. It comes with one drawback, however: The
satisfaction checking of the
concept, which must now check against
a set of signatures specified in a type-list, now requires metaprogramming in
addition to
clauses. As a result, diagnostics can suffer.
During the implementation experience, the author was able to surface a relatively suscinct and accurate error for, say, the lack of a particular completion channel on a receiver, by employing several tricks. While regrettable that such tricks are required, we do not feel that the issue of mediocre diagnostics is dire enough to offset the many advantages of the design presented here.
In addition, the author has discovered a way that an implementation may choose
to extend the
customization point in a way that permits users to
bypass the constraint checking entirely, thus generating a deep instantiation
backtrace that often greatly assists the debugging of custom
sender/receiver-based algorithms. This mechanism can be enshrined in the standard
as "recommended practice."
4. Open questions
4.1. Weasel wording for - fno - exceptions
We may need to add some weasel wording to the effect that:
... if an implementation is able to deduce that all of its operations are not potentially throwing, a conforming implementation of the algorithms in <section> may omit
from any sender’s list of completion signatures.
set_error_t ( exception_ptr )
If an implementation doesn’t support exceptions, e.g., if the user is compiling
with
, it can safely assume that an expression
is not
going to exit exceptionally regardless of the value of
. An
implementation shouldn’t be required to report that it can complete with an
exception in that case.
4.2. Error channel of allocating algorithms
An interesting question is what to do on freestanding implementations for those
algorithms that necessarily must allocate. Those algorithms, as P2300 stands
today, will always have a
completion signature. The
possibilities I see are:
-
Permit implementations to omit the exceptional completion signature when it knows allocations can’t fail with an exception (see above),
-
Replace the exceptional completion signature with
, and call the receiver withset_error_t ( std :: error_code )
on allocation failure.std :: make_error_code ( std :: errc :: not_enough_memory ) -
Replace the exceptional completion signature with
; that is, pass an instance of theset_error_t ( std :: bad_alloc )
exception type through the error channel by value. (From what the author can infer, freestanding implementations are required to provide thestd :: bad_alloc
type even when actually throwing exceptions is not supported.)std :: bad_alloc
5. Implementation experience
The design described above has been implemented in a branch of the reference implementation which can be found in the following GitHub pull request: https://github.com/brycelelbach/wg21_p2300_std_execution/pull/410.
The change, while somewhat disruptive to the reference implementation itself, had the benefits described above; namely:
-
Stricter type-checking "for free". Sender authors need only report the completion signatures, and the concepts and customization points of the library do all the heavy lifting to make sure the capabilities of receivers match the requirements of the senders.
-
More "no-fail" senders. Many fewer of the senders need an error channel at all, and the ones that do generally need it only conditionally, when working with potentially-thrwoing callables or types whose special operations can throw. Only those few senders that must dynamically allocate state necessarily need a
channel, and we may even choose to change those to use something likeset_error_t ( exception_ptr )
instead.set_error_t ( bad_alloc ) -
No required
orset_error_t ( exception_ptr )
channels at all.set_stopped_t ()
In addition, in the author’s opinion, the reference implementation got significantly simpler for the change, and the pull request removes more lines than it adds, while adding functionality at the same time.
6. Proposed wording
The following changes are relative to [P2300R4].
6.1. Header < execution >
synopsis
In [exec.syn], apply the following changes:
namespace std :: execution { // [exec.recv], receivers template < class T , class E = exception_ptr > concept receiver = see - below ; template < class T , class ... An class Completions > concept receiver_of = see - below ; ... template < class S > concept has - sender - types = see - below ; // exposition only ... template < class E > // arguments are not associated entities ([lib.tmpl-heads]) struct dependent_completion_signatures ; ... template < class S , class E = no_env , template < class ... > class Tuple = decayed - tuple , template < class ... > class Variant = variant - or - empty > requires sender < S , E > using value_types_of_t = typename completion_signatures_of_t < S , S >:: template value_types < Tuple , Variant > see below ; template < class S , class E = no_env , template < class ... > class Variant = variant - or - empty > requires sender < S , E > using error_types_of_t = typename completion_signatures_of_t < S , S >:: template error_types < Variant > see below ; template < class S , class E = no_env > requries sender < S , E > inline constexpr bool sends_stopped = see below ; ... // [exec.utils.cmplsigs] template < completion - signature ... Fns > // arguments are not associated entities ([lib.tmpl-heads]) struct completion_signatures {} ; template < class ... Args > // exposition only using default - set - value = completion_signatures < set_value_t ( Args ...) > ; template < class Err > // exposition only using default - set - error = completion_signatures < set_error_t ( Err ) > ; template < class Sigs , class E > // exposition only concept valid - completion - signatures = see below ; // [exec.utils.mkcmplsigs] template < sender Sndr , class Env = no_env , class valid - completion - signatures < Env > AddlSigs = completion_signatures <> , template < class ... > class SetValue = /* see below */ default - set - value , template < class > class SetError = /* see below */ default - set - error , bool SendsStopped = completion_signatures_of_t < Sndr , Env >:: sends_stopped > valid - completion - signatures < Env > SetStopped = completion_signatures < set_stopped_t () >> requires sender using make_completion_signatures = completion_signatures < /* see below */ > ;
6.2. execution :: get_env
Change [exec.get_env] as follows:
-
is a customization point object. For some subexpressionget_env
,r
is expression-equivalent toget_env ( r ) -
if that expression is well-formed.tag_invoke ( execution :: get_env , r ) -
Mandates: The decayed type of the above expression is not
.no_env
-
-
Otherwise,
empty - env {}
is ill-formed .get_env ( r )
-
6.3. Receivers
In [exec.recv], replace paragraphs 1 and 2 with the following:
-
A receiver represents the continuation of an asynchronous operation. An asynchronous operation may complete with a (possibly empty) set of values, an error, or it may be cancelled. A receiver has three principal operations corresponding to the three ways an asynchronous operation may complete:
,set_value
, andset_error
. These are collectively known as a receiver’s completion-signal operations.set_stopped -
The
concept defines the requirements for a receiver type with an unknown set of completion signatures. Thereceiver
concept defines the requirements for a receiver type with a known set of completion signatures.receiver_of template < class T > concept receiver = move_constructible < remove_cvref_t < T >> && constructible_from < remove_cvref_t < T > , T > && requires ( const remove_cvref_t < T >& t ) { execution :: get_env ( t ); }; template < class Signature , class T > concept valid - completion - for = // exposition only requires ( Signature * sig ) { [] < class Ret , class ... Args > ( Ret ( * )( Args ...)) requires nothrow_tag_invocable < Ret , remove_cvref_t < T > , Args ... > {}( sig ); }; template < class T , class Completions > concept receiver_of = receiver < T > && requires ( Completions * completions ) { [] < valid - completion - for < T > ... Sigs > ( completion_signatures < Sigs ... >* ) {}( completions ); };
6.4. execution :: set_value
Change [exec.set_value] as follows:
-
is used to send a value completion signal to a receiver.execution :: set_value -
The name
denotes a customization point object. The expressionexecution :: set_value
for some subexpressionsexecution :: set_value ( R , Vs ...)
andR
is expression-equivalent to:Vs ... -
, if that expression is valid. If the function selected bytag_invoke ( execution :: set_value , R , Vs ...)
does not send the value(s)tag_invoke
to the receiverVs ...
’s value channel, the behavior of callingR
is undefined.execution :: set_value ( R , Vs ...) -
Mandates: The
expression above is not potentially throwing.tag_invoke
-
-
Otherwise,
is ill-formed.execution :: set_value ( R , Vs ...)
-
6.5. Senders
Change [exec.snd] as follows:
-
A sender describes a potentially asynchronous operation. A sender’s responsibility is to fulfill the receiver contract of a connected receiver by delivering one of the receiver completion-signals.
-
The
concept defines the requirements for a sender type. Thesender
concept defines the requirements for a sender type capable of being connected with a specific receiver type.sender_to template < template < template < class ... > class , template < class ... > class > class > struct has - value - types ; // exposition only template < template < template < class ... > class > class > struct has - error - types ; // exposition only template < class S > concept has - sender - types = // exposition only requires { typename has - value - types < S :: template value_types > ; typename has - error - types < S :: template error_types > ; typename bool_constant < S :: sends_stopped > ; }; template < class T , template < class ... > class C > inline constexpr bool is - instance - of = false; // exposition only template < class ... Ts , template < class ... > class C > inline constexpr bool is - instance - of < C < Ts ... > , C > = true; template < class Sigs , class E > concept valid - completion - signatures = // exposition only is - instance - of < Sigs , completion_signatures > || ( same_as < Sigs , dependent_completion_signatures < no_env >> && same_as < E , no_env > ); template < class S , class E > concept sender - base = // exposition only requires { typename completion_signatures_of_t < S , E > ; } && has - sender - types < completion_signatures_of_t < S , E >> requires ( S && s , E && e ) { { get_completion_signatures ( std :: forward < S > ( s ), std :: forward < E > ( e )) } -> valid - completion - signatures < E > ; } ; template < class S , class E = no_env > concept sender = sender - base < S , E > && sender - base < S , no_env > && move_constructible < remove_cvref_t < S >> ; template < class S , class R > concept sender_to = sender < S , env_of_t < R >> && receiver < R > && receiver_of < R , completion_signatures_of_t < S , env_of_t < R >>> && requires ( S && s , R && r ) { execution :: connect ( std :: forward < S > ( s ), std :: forward < R > ( r )); }; -
The
concept defines the requirements for a sender type that on successful completion sends the specified set of value types.sender_of template < class S , class E = no_env , class ... Ts > concept sender_of = sender < S , E > && same_as < type - list < Ts ... > , typename completion_signatures_of_t < S , E >:: template value_types < type - list , type_identity_t > value_types_of_t < S , E , type - list , type_identity_t > > ;
6.6. execution :: completion_signatures_of_t
Change [exec.sndtraitst]/p4 as follows:
-
is a customization point object. Letexecution :: get_completion_signatures
be an expression such thats
isdecltype (( s ))
, and letS
be an expression such thate
isdecltype (( e ))
. ThenE
is expression-equivalent toget_completion_signatures ( s )
andget_completion_signatures ( s , no_env {})
is expression-equivalent to:get_completion_signatures ( s , e ) -
if that expression is well-formed,tag_invoke_result_t < get_completion_signatures_t , S , E > {} -
Mandates:
oris - instance - of < Sigs , completion_signatures >
, whereis - instance - of < Sigs , dependent_completion_signatures >
names the typeSigs
.tag_invoke_result_t < get_completion_signatures_t , S , E >
-
-
Otherwise, if
is well-formed and names a type, then a value-initialized prvalue of typeremove_cvref_t < S >:: completion_signatures
,remove_cvref_t < S >:: completion_signatures -
Mandates:
oris - instance - of < Sigs , completion_signatures >
, whereis - instance - of < Sigs , dependent_completion_signatures >
names the typeSigs
.remove_cvref_t < S >:: completion_signatures
-
-
Otherwise, [...]
-
6.7. dependent_completion_signatures
Change [exec.depsndtraits] as follows:
template < class E > // arguments are not associated entities ([lib.tmpl-heads]) struct dependent_completion_signatures {} ;
-
is a placeholder completion signatures descriptor that can bedependent_completion_signatures usedreturned from
to report that a type might be a sender within a particular execution environment, but it isn’t a sender in an arbitrary execution environment.get_completion_signatures
-
If
isdecay_t < E >
,no_env
is equivalent to:dependent_completion_signatures < E > template <> struct dependent_completion_signatures < no_env > { template < template < class ... > class , template < class ... > class > requires falseusing value_types = /* unspecified */ ; template < template < class ... > class > requires falseusing error_types = /* unspecified */ ; static constexpr bool sends_stopped = /* unspecified */ ; }; Otherwise,
is an empty struct.dependent_completion_signatures < E >
-
When used as the return type of a customization of
, the template argumentget_completion_signatures
shall be the unqualified type of the second argument.E
6.8. execution :: connect
Change [exec.connect]/p2 as follows:
-
The name
denotes a customization point object. For some subexpressionsexecution :: connect
ands
, letr
beS
anddecltype (( s ))
beR
, and letdecltype (( r ))
andS '
be the decayed types ofR '
andS
, respectively. IfR
does not satisfyR
,execution :: receiver
is ill-formed. Otherwise, the expressionexecution :: connect ( s , r )
is expression-equivalent to:execution :: connect ( s , r ) -
, iftag_invoke ( execution :: connect , s , r ) that expression is valid andthe constraints below are satisfied . If the function selected by
satisfiesS execution :: sender
does not return an operation state for whichtag_invoke
starts work described byexecution :: start
, the behavior of callings
is undefined.execution :: connect ( s , r ) -
Constraints:
sender < S , env_of_t < R >> && receiver_of < R , completion_signatures_of_t < S , env_of_t < R >>> && tag_invocable < connect_t , S , R >
-
Mandates: The type of the
expression above satisfiestag_invoke
.operation_state
-
-
Otherwise,
if [...]connect - awaitable ( s , r ) [...]
The operand of the requires-clause of
is equivalent toconnect - awaitable
ifreceiver_of < R >
isawait - result - type < S , connect - awaitable - promise >
; otherwise, it iscv void
.receiver_of < R , await - result - type < S , connect - awaitable - promise >> Let
beRes
, and letawait - result - type < S , connect - awaitable - promise >
be an empty parameter pack ifVs ...
isRes
, or a pack containing the single typecv void
otherwise. The operand of the requires-clause ofRes
is equivalent toconnect - awaitable
wherereceiver_of < R , Sigs >
names the type:Sigs completion_signatures < set_value_t ( Vs ...), set_error_t ( exception_ptr ), set_stopped_t () > -
Otherwise,
is ill-formed.execution :: connect ( s , r )
-
6.9. execution :: just
Change [exec.just] as follows:
-
is used to create a sender that propagates a set of values to a connected receiver.execution :: just template < class ... Ts > struct just - sender { // exposition only : completion_signatures < set_value_t ( Ts ...), set_error_t ( exception_ptr ) > { using completion_signatures = execution :: completion_signatures < set_value_t ( Ts ...) > ; tuple < Ts ... > vs_ ; template < class R > struct operation_state { tuple < Ts ... > vs_ ; R r_ ; friend void tag_invoke ( start_t , operation_state & s ) noexcept { try { apply ([ & s ]( Ts & ... values_ ) { set_value ( std :: move ( s . r_ ), std :: move ( values_ )...); }, s . vs_ ); } catch (...) { set_error ( std :: move ( s . r_ ), current_exception ()); } } }; template < receiver _of < completion_signatures > R > requires receiver_of < R , Ts ... > && ( copy_constructible < Ts > && ...) friend operation_state < decay_t > tag_invoke ( connect_t , const just - sender & j , R && r ) { return { j . vs_ , std :: forward < R > ( r ) }; } template < receiver _of < completion_signatures > R > requires receiver_of < R , Ts ... > friend operation_state < decay_t > tag_invoke ( connect_t , just - sender && j , R && r ) { return { std :: move ( j . vs_ ), std :: forward < R > ( r ) }; } }; template < movable - value ... Ts > just - sender < decay_t < Ts > ... > just ( Ts && ... ts ) noexcept ( see - below ); -
Effects: [...]
6.10. execution :: just_error
Change [exec.just_error] as follows:
-
is used to create a sender that propagates an error to a connected receiver.execution :: just_error template < class T > struct just - error - sender { // exposition only : completion_signatures < set_error_t ( T ) > { using completion_signatures = execution :: completion_signatures < set_error_t ( T ) > ; T err_ ; template < class R > struct operation_state { T err_ ; R r_ ; friend void tag_invoke ( start_t , operation_state & s ) noexcept { set_error ( std :: move ( s . r_ ), std :: move ( err_ )); } }; template < receiver _of < completion_signatures > R > requires receiver < R , T > && copy_constructible < T > friend operation_state < decay_t < R >> tag_invoke ( connect_t , const just - error - sender & j , R && r ) { return { j . err_ , std :: forward < R > ( r ) }; } template < receiver _of < completion_signatures > R > requires receiver < R , T > friend operation_state < decay_t < R >> tag_invoke ( connect_t , just - error - sender && j , R && r ) { return { std :: move ( j . err_ ), std :: forward < R > ( r ) }; } }; template < movable - value T > just - error - sender < decay_t < T >> just_error ( T && t ) noexcept ( see - below ); -
Effects: [...]
6.11. execution :: just_stopped
Change [exec.just_stopped] as follows:
-
is used to create a sender that propagates a stopped signal to a connected receiver.execution :: just_stopped struct just - stopped - sender { // exposition only : completion_signatures < set_stopped_t () > { using completion_signatures = execution :: completion_signatures < set_stopped_t () > ; template < class R > struct operation_state { R r_ ; friend void tag_invoke ( start_t , operation_state & s ) noexcept { set_stopped ( std :: move ( s . r_ )); } }; template < receiver _of < completion_signatures > R > friend operation_state < decay_t < R >> tag_invoke ( connect_t , const just - stopped - sender & j , R && r ) noexcept { return { std :: forward < R > ( r ) }; } }; just - stopped - sender just_stopped () noexcept ; -
Effects: Equivalent to
.just - stopped - sender {}
6.12. execution :: read
Change [exec.read]/p3 as follows:
-
is an exposition only class template equivalent to:read - sender template < class Tag > struct read - sender { // exposition only template < class R > struct operation - state { // exposition only R r_ ; friend void tag_invoke ( start_t , operation - state & s ) noexcept try { auto value = Tag {}( get_env ( s . r_ )); set_value ( std :: move ( s . r_ ), std :: move ( value )); } catch (...) { set_error ( std :: move ( s . r_ ), current_exception ()); } }; template < class Env > requires callable < Tag , Env > using completions = // exposition only completion_signatures < set_value_t ( call - result - t < Tag , Env > ), set_error_t ( exception_ptr ) > ; template < receiver R > requires callable < Tag , env_of_t < R >> && receiver_of < R , call - result - t < Tag , env_of_t < R >>> template < class R > requires receiver_of < R , completions < env_of_t < R >>> friend operation - state < decay_t < R >> tag_invoke ( connect_t , read - sender , R && r ) { return { std :: forward < R > ( r ) }; } friend empty - env tag_invoke ( get_completion_signatures_t , read - sender , auto ); template < class Env > friend auto tag_invoke ( get_completion_signatures_t , read - sender , Env ) -> dependent_completion_signatures < Env > ; template < class Env > requires ( ! same_as < Env , no_env > ) && callable < Tag , Env > friend auto tag_invoke ( get_completion_signatures_t , read - sender , Env ) -> completion_signatures < set_value_t ( call - result - t < Tag , Env > ), set_error_t ( exception_ptr ) > ; -> completions < Env > requires true; };
6.13. execution :: schedule_from
Replace [exec.schedule_from]/3.3, which begins with "Given an expression
, let
be
," with the following:
-
Given subexpressions
ands2
, wheree
is a sender returned froms2
or a copy of such, letschedule_from
beS2
and letdecltype (( s2 ))
beE
. Then the type ofdecltype (( e ))
shall be:tag_invoke ( get_completion_signatures , s2 , e ) make_completion_signatures < copy_cvref_t < S2 , S > , E , make_completion_signatures < schedule_result_t < Sch > , E , completion_signatures < set_error_t ( exception_ptr ) > , no - value - completions >> ; where
names the typeno - value - completions < As ... >
for any set of typescompletion_signatures <>
.As ...
6.14. execution :: then
Replace [exec.then]/p2.3.3, which begins with "Given an expression
, let
be
," with the following:
-
Let
name the typecompl - sig - t < Tag , Args ... >
ifTag ()
is a template paramter pack containing the single typeArgs ...
; otherwise,void
. Given subexpressionsTag ( Args ...)
ands2
wheree
is a sender returned froms2
or a copy of such, letthen
beS2
and letdecltype (( s2 ))
beE
. The type ofdecltype (( e ))
shall be equivalent to:tag_invoke ( get_completion_signatures , s2 , e ) make_completion_signatures < copy_cvref_t < S2 , S > , E , set - error - signature , set - value - completions > ; where
is an alias for:set - value - completions template < class ... As > set - value - completions = completion_signatures < compl - sig - t < set_value_t , invoke_result_t < F , As ... >>> and
is an alias forset - error - signature
if any of the types in thecompletion_signatures < set_error_t ( exception_ptr ) >
named bytype - list
arevalue_types_of_t < copy_cvref_t < S2 , S > , E , potentially - throwing , type - list >
; otherwise,true_type
, wherecompletion_signatures <>
is the template alias:potentially - throwing template < class ... As > potentially - throwing = bool_constant < is_nothrow_invocable_v < F , As ... >> ;
6.15. execution :: upon_error
Replace [exec.upon_error]/p2.3.3, which begins with "Given an expression
, let
be
," with the following:
-
Let
name the typecompl - sig - t < Tag , Args ... >
ifTag ()
is a template paramter pack containing the single typeArgs ...
; otherwise,void
. Given subexpressionsTag ( Args ...)
ands2
wheree
is a sender returned froms2
or a copy of such, letupon_error
beS2
and letdecltype (( s2 ))
beE
. The type ofdecltype (( e ))
shall be equivalent to:tag_invoke ( get_completion_signatures , s2 , e ) make_completion_signatures < copy_cvref_t < S2 , S > , E , set - error - signature , default - set - value , set - error - completion > ; where
is the template alias:set - error - completion template < class E > set - error - completion = completion_signatures < compl - sig - t < set_value_t , invoke_result_t < F , E >>> and
is an alias forset - error - signature
if any of the types in thecompletion_signatures < set_error_t ( exception_ptr ) >
named bytype - list
areerror_types_of_t < copy_cvref_t < S2 , S > , E , potentially - throwing >
; otherwise,true_type
, wherecompletion_signatures <>
is the template alias:potentially - throwing template < class ... Es > potentially - throwing = type - list < bool_constant < is_nothrow_invocable_v < F , Es >> ... > ;
6.16. execution :: upon_stopped
Replace [exec.upon_stopped]/p2.3.3, which begins "Given some expression
, let
be
," with the following:
-
Let
name the typecompl - sig - t < Tag , Args ... >
ifTag ()
is a template paramter pack containing the single typeArgs ...
; otherwise,void
. Given subexpressionsTag ( Args ...)
ands2
wheree
is a sender returned froms2
or a copy of such, letupon_stopped
beS2
and letdecltype (( s2 ))
beE
. The type ofdecltype (( e ))
shall be equivalent to:tag_invoke ( get_completion_signatures , s2 , e ) make_completion_signatures < copy_cvref_t < S2 , S > , E , set - error - signature , default - set - value , default - set - error , set - stopped - completions > ; where
names the typeset - stopped - completions
, andcompletion_signatures < compl - sig - t < set_value_t , invoke_result_t < F >>
names the typeset - error - signature
ifcompletion_signatures < set_error_t ( exception_ptr ) >
isis_nothrow_invocable_v < F > true
, or
otherwise.completion_signatures <>
6.17. execution :: bulk
Replace [exec.bulk]/p2.4, which begins, "Given an expression
, let
be
," with the following:
-
Given subexpressions
ands2
wheree
is a sender returned froms2
or a copy of such, letbulk
beS2
and letdecltype (( s2 ))
beE
. The type ofdecltype (( e ))
shall be equivalent to:tag_invoke ( get_completion_signatures , s2 , e ) make_completion_signatures < copy_cvref_t < S2 , S > , E , completion_signatures < set_error_t ( exception_ptr ) >>
6.18. execution :: split
Replace [exec.split]/p3.4, which begins, "Given an expression
, let
be
," with the following:
-
Given subexpressions
ands2
wheree
is a sender returned froms2
or a copy of such, letsplit
beS2
and letdecltype (( s2 ))
beE
. The type ofdecltype (( e ))
shall be equivalent to:tag_invoke ( get_completion_signatures , s2 , e ) make_completion_signatures < copy_cvref_t < S2 , S > , E , completion_signatures < set_error_t ( exception_ptr ) > , value - signatures , error - signatures > ; where
is the alias template:value - signatures template < class ... Ts > using value - signatures = completion_signatures < set_value_t ( decay_t < Ts >& ...) > ; and
is the alias template:error - signatures template < class E > using error - signatures = completion_signatures < set_error_t ( decay_t < E >& ) > ;
6.19. execution :: when_all
Replace [exec.when_all]/p2.2.5, which begins, "Given some expression
, let
be
," with the following:
-
Given subexpressions
ands2
wheree
is a sender returned froms2
or a copy of such, letwhen_all
beS2
, letdecltype (( s2 ))
beE
, and letdecltype (( e ))
be the decayed types of the arguments to theSs ...
expression that createdwhen_all
. If the decayed type ofs2
ise
, letno_env
beWE
; otherwise, letno_env
be a type such thatWE
isstop_token_of_t < WE >
andin_place_stop_token
names the type, if any, oftag_invoke_result_t < Tag , WE , As ... >
for all typescall - result - t < Tag , E , As ... >
and all typesAs ...
besidesTag
. The type ofget_stop_token_t
shall be as follows:tag_invoke ( get_completion_signatures , s2 , e ) -
For each type
inS i
, letSs ...
name the typeS 'i
. If for any typecopy_cvref_t < S2 , S i >
, the typeS 'i
names a type other than an instantiation ofcompletion_signatures_of_t < S 'i , WE >
, the type ofcompletion_signatures
shall betag_invoke ( get_completion_signatures , s2 , e )
.dependent_completion_signatures < E > -
Otherwise, for each type
, letS 'i
be the set of template arguments in the instantiation ofSigs i ...
named bycompletion_signatures
, and letcompletion_signatures_of_t < S 'i , WE >
be the count of function types inC i
for which the return type isSigs i ...
. If anyset_value_t
is two or greater, then the type ofC i
shall betag_invoke ( get_completion_signatures , s2 , e )
.dependent_completion_signatures < E > -
Otherwise, let
be the set of function types inSigs2 i ...
whose return types are notSigs i ...
, and letset_value_t
be the unique set of types inWs ...
, where[ Sigs2 0 ..., Sigs2 1 ..., ... Sigs2 n -1 . .., set_error_t ( exception_ptr ), set_stopped_t ()]
isn
. If anysizeof ...( Ss )
isC i
, then the type of0
shall betag_invoke ( get_completion_signatures , s2 , e )
.completion_signatures < Ws ... > -
Otherwise, let
be the function argument types of the single type inV i ...
for which the return type isSigs i ...
. Then the type ofset_value_t
shall betag_invoke ( get_completion_signatures , s2 , e )
.completion_signatures < Ws ..., set_value_t ( V 0 ..., V 1 ..., ... V n -1 . ..) >
-
6.20. execution :: ensure_started
Replace [exec.ensure_started]/p2.4 which begins, "Given an expression
, let
be
," with the following:
-
Given subexpressions
ands2
wheree
is a sender returned froms2
or a copy of such, letensure_started
beS2
and letdecltype (( s2 ))
beE
. The type ofdecltype (( e ))
shall be equivalent to:tag_invoke ( get_completion_signatures , s2 , e ) make_completion_signatures < copy_cvref_t < S2 , S > , ensure - started - env , completion_signatures < set_error_t ( exception_ptr && ) > , set - value - signature , error - types > where
is the alias template:set - value - signature template < class ... Ts > using set - value - signature = completion_signatures < set_value_t ( decay_t < Ts >&& ...) > ; and
is the alias template:error - types template < class E > using error - types = completion_signatures < set_error_t ( decay_t < E >&& ) > ;
6.21. execution :: start_detached
Change [exec.start_detached]p2.3 as follows:
-
Otherwise:
-
Constructs a receiverLetr
be the type of a receiver, letR
be an rvalue of typer
, and letR
be a lvalue reference tocr
such that :const R -
WhenThe expression
is called, it does nothing.set_value ( r , ts ...)
is not potentially throwing and has no effect,set_value ( r ) -
WhenFor any subexpression
is called, it callsset_error ( r , e )
.std :: terminate
, the expressione
is expression-equivalent toset_error ( r , e )
,terminate () -
WhenThe expression
is called, it does nothing.set_stopped ( r )
is not potentially throwing and has no effect, andset_stopped ( r )
-
The expression
is expression-equivalent toget_env ( cr )
.empty - env {}
-
-
Calls
, resulting in an operation stateexecution :: connect ( s , r )
, then callsop_state
. The lifetime ofexecution :: start ( op_state )
lasts until one of the receiver completion-signals ofop_state
is called.r
-
6.22. this_thread :: sync_wait
Change [exec.sync_wait]/p4.3.3.1 as follows:
-
If
has been called, returnsexecution :: set_value ( r , ts ...)
. If that expression exits exceptionally, the exception is propagated to the caller ofsync - wait - type < S , sync - wait - env > { decayed - tuple < decltype ( ts )... > { ts ...}}
.sync_wait
6.23. execution :: receiver_adaptor
Remove [exec.utils.rcvr_adptr]/p2, which begins, "This section makes use of the following exposition-only entities," and renumber all subsequent paragraphs.
Change [exec.utils.rcvr_adptr]/p4-6 (now p3-5) as follows:
-
is equivalent to the following:receiver_adaptor < Derived , Base > template < class - type Derived , receiver Base = unspecified > // arguments are not associated entities ([lib.tmpl-heads]) class receiver_adaptor { friend Derived ; public : // Constructors receiver_adaptor () = default ; template < class B > requires HAS - BASE && constructible_from < Base , B > explicit receiver_adaptor ( B && base ) : base_ ( std :: forward < B > ( base )) {} private : using set_value = unspecified ; using set_error = unspecified ; using set_stopped = unspecified ; using get_env = unspecified ; // Member functions template < class Self > requires HAS - BASE copy_cvref_t < Self , Base >&& decltype ( auto ) base ( this Self && self ) noexcept { return static_cast < Self &&> ( self ). base_ ; return ( std :: forward < Self > ( self ). base_ ); } // [exec.utils.rcvr_adptr.nonmembers] Non-member functions template < class D = Derived , class ... As > friend void tag_invoke ( set_value_t , Derived && self , As && ... as ) noexcept ( see below ) ; template < class E , class D = Derived > friend void tag_invoke ( set_error_t , Derived && self , E && e ) noexcept ; template < class D = Derived > friend void tag_invoke ( set_stopped_t , Derived && self ) noexcept ; friend decltype ( auto ) tag_invoke ( get_env_t , const Derived & self ) noexcept ( see below ); template < forwarding - receiver - query Tag , class D = Derived , class ... As > requires callable < Tag , BASE - TYPE ( const D erived & ), As ... > friend auto tag_invoke ( Tag tag , const Derived & self , As && ... as ) noexcept ( nothrow - callable < Tag , BASE - TYPE ( const D erived & ), As ... > ) -> call - result - t < Tag , BASE - TYPE ( const D erived & ), As ... > { return std :: move ( tag )( GET - BASE ( self ), std :: forward < As > ( as )...); } [[ no_unique_address ]] Base base_ ; // present if and only if HAS-BASE is true }; -
[Note:
providesreceiver_adaptor
overloads on behalf of the derived classtag_invoke
, which is incomplete whenDerived
is instantiated.]receiver_adaptor -
[Example:
using _int_completion = execution :: completion_signatures < execution :: set_value_t ( int ) > ; template < execution :: receiver_of < int _int_completion > R > class my_receiver : execution :: receiver_adaptor < my_receiver < R > , R > { friend execution :: receiver_adaptor < my_receiver , R > ; void set_value () && { execution :: set_value ( std :: move ( * this ). base (), 42 ); } public : using execution :: receiver_adaptor < my_receiver , R >:: receiver_adaptor ; }; -- end example]
Replace section [exec.utils.rcvr_adptr.nonmembers] with the following:
template < class ... As > friend void tag_invoke ( set_value_t , Derived && self , As && ... as ) noexcept ;
-
Let
be the expressionSET - VALUE
.std :: move ( self ). set_value ( std :: forward < As > ( as )...) -
Constraints: Either
is a valid expression orSET - VALUE
denotes a type andtypename Derived :: set_value
iscallable < set_value_t , BASE - TYPE ( Derived ), As ... > true
. -
Mandates:
, if that expression is valid, is not potentially throwing.SET - VALUE -
Effects: Equivalent to:
-
If
is a valid expression,SET - VALUE
;SET - VALUE -
Otherwise,
.execution :: set_value ( GET - BASE ( std :: move ( self )), std :: forward < As > ( as )...)
-
template < class E > friend void tag_invoke ( set_error_t , Derived && self , E && e ) noexcept ;
-
Let
be the expressionSET - ERROR
.std :: move ( self ). set_error ( std :: forward < E > ( e )) -
Constraints: Either
is a valid expression orSET - ERROR
denotes a type andtypename Derived :: set_error
iscallable < set_error_t , BASE - TYPE ( Derived ), E > true
. -
Mandates:
, if that expression is valid, is not potentially throwing.SET - ERROR -
Effects: Equivalent to:
-
If
is a valid expression,SET - ERROR
;SET - ERROR -
Otherwise,
.execution :: set_error ( GET - BASE ( std :: move ( self )), std :: forward < E > ( e ))
-
friend void tag_invoke ( set_stopped_t , Derived && self ) noexcept ;
-
Let
be the expressionSET - STOPPED
.std :: move ( self ). set_stopped () -
Constraints: Either
is a valid expression orSET - STOPPED
denotes a type andtypename Derived :: set_stopped
iscallable < set_stopped_t , BASE - TYPE ( Derived ) > true
. -
Mandates:
, if that expression is valid, is not potentially throwing.SET - STOPPED -
Effects: Equivalent to:
-
If
is a valid expression,SET - STOPPED
;SET - STOPPED -
Otherwise,
.execution :: set_stopped ( GET - BASE ( std :: move ( self )))
-
friend decltype ( auto ) tag_invoke ( get_env_t , const Derived & self ) noexcept ( see below );
-
Constraints: Either
is a valid expression orself . get_env ()
denotes a type andtypename Derived :: get_env
iscallable < get_env_t , BASE - TYPE ( const Derived & ) > true
. -
Effects: Equivalent to:
-
If
is a valid expression,self . get_env ()
;self . get_env () -
Otherwise,
.execution :: get_env ( GET - BASE ( self ))
-
-
Remarks: The expression in the
clause is:noexcept -
If
is a valid expression,self . get_env ()
;noexcept ( self . get_env ()) -
Otherwise,
.noexcept ( execution :: get_env ( GET - BASE ( self )))
-
6.24. execution :: completion_signatures
Change [exec.utils.cmplsigs] as follows:
-
is used to define a type that implements the nestedcompletion_signatures
,value_types
, anderror_types
members that describe the ways a sender completes. Its arguments are a flat list of function types that describe the signatures of the receiver’s completion-signal operations that the sender invokes.sends_stopped
is used to describe the completion signals of a receiver that a sender may invoke. Its template argument list is a list of function types corresponding to the signatures of the receiver’s completion signals.completion_signatures -
[Example:
class my_sender { using completion_signatures = execution :: completion_signatures < execution :: set_value_t (), execution :: set_value_t ( int , float ), execution :: set_error_t ( exception_ptr ), execution :: set_error_t ( error_code ), execution :: set_stopped_t () > ; }; // completion_signatures_of_t<my_sender> // ::value_types<tuple, variant> names the type: // variant<tuple<>, tuple<int, float>> // // completion_signatures_of_t<my_sender> // ::error_types<variant> names the type: // variant<exception_ptr, error_code> // // completion_signatures_of_t<my_sender>::sends_stopped is true // Declares my_sender to be a sender that can complete by calling // one of the following for a receiver expression R: // execution::set_value(R) // execution::set_value(R, int{...}, float{...}) // execution::set_error(R, exception_ptr{...}) // execution::set_error(R, error_code{...}) // execution::set_stopped(R) -- end example]
-
This section makes use of the following exposition-only concept:
template < class Fn > concept completion - signature = see below ; -
A type
satisfiesFn
if it is a function type with one of the following forms:completion - signature -
, whereset_value_t ( Vs ...)
is an arbitrary parameter pack.Vs -
, whereset_error_t ( E )
is an arbitrary type.E -
set_stopped_t ()
-
-
Otherwise,
does not satisfyFn
.completion - signature
-
-
template < completion - signature ... Fns > // arguments are not associated entities ([lib.tmpl-heads]) struct completion_signatures { }; template < template < class ... > class Tuple , template < class ... > class Variant > using value_types = see below ; template < template < class ... > class Variant > using error_types = see below ; static constexpr bool sends_stopped = see below ; }; -
Let
be a template parameter pack of the function types inValueFns
whose return types areFns
, and letexecution :: set_value_t
be a template parameter pack of the function argument types in theValues n
-th type inn
. Then, given two variadic templatesValueFns
andTuple
, the typeVariant
names the typecompletion_signatures < Fns ... >:: value_types < Tuple , Variant >
, whereVariant < Tuple < Values 0 ... > , Tuple < Values 1 ... > , ... Tuple < Values m -1 . .. >>
is the size of the parameter packm
.ValueFns -
Let
be a template parameter pack of the function types inErrorFns
whose return types areFns
, and letexecution :: set_error_t
be the function argument type in theError n
-th type inn
. Then, given a variadic templateErrorFns
, the typeVariant
names the typecompletion_signatures < Fns ... >:: error_types < Variant >
, whereVariant < Error 0 , Error 1 , ... Error m -1 >
is the size of the parameter packm
.ErrorFns -
iscompletion_signatures < Fns ... >:: sends_stopped true
if at least one of the types in
isFns
; otherwise,execution :: set_stopped_t () false
.
-
-
template < class S , class E = no_env , template < class ... > class Tuple = decayed - tuple , template < class ... > class Variant = variant - or - empty > requires sender < S , E > using value_types_of_t = see below ; -
Let
be a template parameter pack of the arguments of theFns ...
instantiation named bycompletion_signatures
, letcompletion_signatures_of_t < S , E >
be a template parameter pack of the function types inValueFns
whose return types areFns
, and letexecution :: set_value_t
be a template parameter pack of the function argument types in theValues n
-th type inn
. Then, given two variadic templatesValueFns
andTuple
, the typeVariant
names the typevalue_types_of_t < S , E , Tuple , Variant >
, whereVariant < Tuple < Values 0 ... > , Tuple < Values 1 ... > , ... Tuple < Values m -1 . .. >>
is the size of the parameter packm
.ValueFns
-
-
template < class S , class E = no_env , template < class ... > class Variant = variant - or - empty > requires sender < S , E > using error_types_of_t = see below ; -
Let
be a template parameter pack of the arguments of theFns ...
instantiation named bycompletion_signatures
, letcompletion_signatures_of_t < S , E >
be a template parameter pack of the function types inErrorFns
whose return types areFns
, and letexecution :: set_error_t
be the function argument type in theError n
-th type inn
. Then, given a variadic templateErrorFns
, the typeVariant
names the typeerror_types_of_t < S , E , Variant >
, whereVariant < Error 0 , Error 1 , ... Error m -1 >
is the size of the parameter packm
.ErrorFns
-
-
template < class S , class E = no_env > requires sender < S , E > inline constexpr bool sends_stopped = see below ; -
Let
be a template parameter pack of the arguments of theFns ...
instantiation named bycompletion_signatures
.completion_signatures_of_t < S , E >
issends_stopped < S , E > true
if at least one of the types in
isFns
; otherwise,execution :: set_stopped_t () false
.
-
6.25. execution :: make_completion_signatures
Change [exec.utils.mkcmplsigs] as follows:
-
is an alias template used to adapt the completion signatures of a sender. It takes a sender, and environment, and several other template arguments that apply modifications to the sender’s completion signatures to generate a new instantiation ofmake_completion_signatures
.execution :: completion_signatures -
[Example:
// Given a sender S and an environment Env, adapt a S’s completion // signatures by lvalue-ref qualifying the values, adding an additional // exception_ptr error completion if its not already there, and leaving the // other signals alone. template < class ... Args > using my_set_value_t = execution :: completion_signatures < execution :: set_value_t ( add_lvalue_reference_t < Args > ...) > ; using my_completion_signals = execution :: make_completion_signatures < S , Env , execution :: completion_signatures < execution :: set_error_t ( exception_ptr ) > , my_set_value_t > ; -- end example]
-
This section makes use of the following exposition-only entities:
template < class ... As > using default - set - value = execution :: completion_signatures < execution :: set_value_t ( As ...) > ; template < class Err > using default - set - error = execution :: completion_signatures < execution :: set_error_t ( Err ) > ; -
template < execution :: sender Sndr , class Env = execution :: no_env , class valid - completion - signatures < Env > AddlSigs = execution :: completion_signatures <> , template < class ... > class SetValue = default - set - value , template < class > class SetError = default - set - error , bool SendsStopped = execution :: completion_signatures_of_t < Sndr , Env >:: sends_stopped > valid - completion - signatures < Env > SetStopped = execution :: completion_signatures < set_stopped_t () >> requires sender < Sndr , Env > using make_completion_signatures = execution :: completion_signatures < /* see below */ > ; -
shall name an instantiation of theAddlSigs
class template.execution :: completion_signatures -
shall name an alias template such that for any template parameter packSetValue
, the typeAs ...
is either ill-formedSetValue < As ... > ,or else
or an alias for a function type whose return type isvoid execution :: set_value_t
is satisfied .valid - completion - signatures < SetValue < As ... > , E > -
shall name an alias template such that for any typeSetError
,Err
is either ill-formedSetError < Err > ,or else
or an alias for a function type whose return type isvoid execution :: set_error_t
is satisfied .valid - completion - signatures < SetError < Err > , E >
-
Let
be a pack of theVs ... non-types in thevoid
named bytype - list
.value_types_of_t < Sndr , Env , SetValue , type - list > -
Let
be a pack of theEs ... non-types in thevoid
named bytype - list
, whereerror_types_of_t < Sndr , Env , error - list >
is an alias template such thaterror - list
nameserror - list < Ts ... >
.type - list < SetError < Ts > ... > -
Let
Ss be an empty pack ifname the type
isSendsStopped false
; otherwise, a pack containing the single typeexecution :: set_stopped_t ()
ifcompletion_signatures <>
issends_stopped < Sndr , Env > false
; otherwise,
.SetStopped
-
Let
be a pack of the template arguments of theMoreSigs ...
instantiation named byexecution :: completion_signatures
.AddlSigs -
If any of the above types are ill-formed, then
is an alias formake_completion_signatures < Sndr , Env , AddlSigs , SetValue , SetDone , SendsStopped >
.dependent_completion_signatures < Env > -
Otherwise,
names the typemake_completion_signatures < Sndr , Env , AddlSigs , SetValue , SetDone , SendsStopped >
wherecompletion_signatures < Sigs ... >
is the unique set of types inSigs ...
.[ Vs ..., Es ..., Ss ..., MoreSigs ...]
-
If any of the above types are ill-formed, then
is ill-formed,make_completion_signatures < Sndr , Env , AddlSigs , SetValue , SetError , SetStopped > -
Otherwise, if any type in
is not an instantiation of[ AddlSigs , Vs ..., Es ..., Ss ]
, thencompletion_signatures
is an alias formake_completion_signatures < Sndr , Env , AddlSigs , SetValue , SetError , SetStopped >
,dependent_completion_signatures < no_env > -
Otherwise,
names the typemake_completion_signatures < Sndr , Env , AddlSigs , SetValue , SetError , SetStopped >
wherecompletion_signatures < Sigs ... >
is the unique set of types in all the template arguments of all theSigs ...
instantiations incompletion_signatures
.[ AddlSigs , Vs ..., Es ..., Ss ]
-
6.26. execution :: as_awaitable
Change [exec.as_awaitable]/p1.2.1 as follows:
-
is equivalent to the following:awaitable - receiver struct awaitable - receiver { variant < monostate , result_t , exception_ptr >* result_ptr_ ; coroutine_handle < P > continuation_ ; // ... see below }; Let
be an rvalue expression of typer
, letawaitable - receiver
be acr
lvalue that refers toconst
, letr v
be anvs ... expression of typearbitrary function parameter pack of typesresult_t
, and letVs ...
be an arbitrary expression of typeerr
. Then:Err -
If
isvalue_t
, thenvoid
is expression-equivalent toexecution :: set_value ( r )
; otherwise,( r . result_ptr_ -> emplace < 1 > (), r . continuation_ . resume ())
is expression-equivalent toexecution :: set_value ( r , v )
.( r . result_ptr_ -> emplace < 1 > ( v ), r . continuation_ . resume ())
If
is satisfied, the expressionconstructible_from < result_t , Vs ... >
is not potentially throwing and is equivalent to:execution :: set_value ( r , vs ...) try { r . result_ptr_ -> emplace < 1 > ( vs ...); } catch (...) { r . result_ptr_ -> emplace < 2 > ( current_exception ()); } r . continuation_ . resume ();
is ill-formed.execution :: set_value ( r , vs ...) -
The expression
is not potentially throwing and isexecution :: set_error ( r , err ) expression-equivalent to:
,( r . result_ptr_ -> emplace < 2 > ( AS_EXCEPT_PTR ( err )), r . continuation_ . resume ()) r . result_ptr_ -> emplace < 2 > ( AS_EXCEPT_PTR ( err )); r . continuation_ . resume (); where
is:AS_EXCEPT_PTR ( err ) -
iferr
names the same type asdecay_t < Err >
,exception_ptr -
Otherwise,
ifmake_exception_ptr ( system_error ( err ))
names the same type asdecay_t < Err >
,error_code -
Otherwise,
.make_exception_ptr ( err )
-
-
The expression
is not potentially throwing and isexecution :: set_stopped ( r ) expression-equivalent to
.static_cast < coroutine_handle <>> ( r . continuation_ . promise (). unhandled_stopped ()). resume () -
is expression-equivalent totag_invoke ( tag , cr , as ...)
for any expressiontag ( as_const ( cr . continuation_ . promise ()), as ...)
whose type satisfiestag
and for any set of argumentsforwarding - receiver - query
.as ...
-