Document #: | P3303R0 |
Date: | 2024-05-21 |
Project: | Programming Language C++ |
Audience: |
LEWG Library Evolution |
Reply-to: |
Eric Niebler <eric.niebler@gmail.com> |
“Experience is simply the name we give our mistakes.”
—
Oscar Wilde
“To err is human, but to really foul things up you need a
computer.”
— Paul R. Ehrlich
The paper [P2999R3] “Sender
Algorithm Customization” proposed, among other things, to make sender
algorithms customizable lazily; that is, when their senders are
connected with receivers. LEWG agreed and forwarded P2999 to LWG. Due to
a gross oversight, however, P2999 didn’t propose the wording changes to
connect
and
get_completion_signatures
that
actually implement the design that LEWG approved. This paper corrects
the oversight by using the new
transform_sender
utility in
connect
and
get_completion_signatures
as
P2999 promised and failed to do.
The changes this paper proposes are:
get_completion_signatures(sndr, env)
first transforms sndr
with transform_sender(
and then uses the result in place of
get-domain-late
(sndr, env), sndr, env)sndr
.
connect(sndr, rcvr)
first
transforms sndr
with transform_sender(
and then uses the result in place of
get-domain-late
(sndr, get_env(rcvr)), sndr, get_env(rcvr))sndr
.
Table
2 in [P2999R3] shows how
that paper proposed to change the
connect
customization point. The
table is reproduced below. Note that P2999R3 was targetting a version of
P2300 that still employed
tag_invoke
, which has since been
removed.
Before | After |
---|---|
|
|
The design shown above is the one that was discussed and voted on in LEWG.
A glance at the proposed wording from P2999R3 shows that no such change was ever made. This most critical part of that paper’s design intent was inadvertantly left out of the wording. Face, meet palm.
This paper proposes no design changes from those described in P2999R3. It “merely” corrects the wording to agree with the design.
[ Editor's note: The changes in this paper are relative to [P2300R9]. ]
[ Editor's note: Change [exec.getcomplsigs] as follows: ]
execution::get_completion_signatures
[exec.getcomplsigs]
get_completion_signatures
is a customization point object. Letsndr
be an expression such thatdecltype((sndr))
isSndr
, and letenv
be an expression such thatdecltype((env))
isEnv
. Letnew_sndr
be the expressiontransform_sender(
, and letget-domain-late
(sndr, env), sndr, env)NewSndr
bedecltype((new_sndr))
. Thenget_completion_signatures(sndr, env)
is expression-equivalent to:
decltype(
if that expression is well-formed,new_
sndr.get_completion_signatures(env)){}Otherwise,
remove_cvref_t<NewSndr>::completion_signatures{}
if that expression is well-formed,Otherwise, if
is
is-awaitable
<NewSndr,env-promise
<Env>>true
, then:completion_signatures<
SET-VALUE-SIG
(await-result-type
<NewSndr,env-promise
<Env>>),// see [exec.snd.concepts]
set_error_t(exception_ptr), set_stopped_t()>{}Otherwise,
get_completion_signatures(sndr, env)
is ill-formed.Let
rcvr
be an rvalue receiver of typeRcvr
, and letSndr
be the type of a sender such thatsender_in<Sndr, env_of_t<Rcvr>>
istrue
. LetSigs...
be the template arguments of thecompletion_signatures
specialization named bycompletion_signatures_of_t<Sndr, env_of_t<Rcvr>>
. LetCSO
be a completion function. If senderSndr
or its operation state cause the expressionto be potentially evaluated ([basic.def.odr]) then there shall be a signature
CSO
(rcvr, args...)Sig
inSigs...
such thatis
MATCHING-SIG
(decayed-typeof
<CSO
>(decltype(args)...), Sig)true
([exec.general]).
[ Editor's note: Change [exec.connect] paragraphs 2 and 6 as shown below. Paragraphs 3-5 are unchanged but are shown here in their entirety to give context to the surrounding changes. ]
execution::connect
[exec.connect]
connect
connects ([async.ops]) a sender with a receiver.The name
connect
denotes a customization point object. For subexpressionssndr
andrcvr
, letSndr
bedecltype((sndr))
andRcvr
bedecltype((rcvr))
, letnew_sndr
be the expressiontransform_sender(
, letget-domain-late
(sndr, get_env(rcvr)), sndr, get_env(rcvr))NewSndr
bedecltype((new_sndr))
,, and letDS
andDR
be the decayed types ofand
New
SndrRcvr
, respectively.Let
connect-awaitable-promise
be the following class:namespace std::execution {
connect-awaitable-promise
structwith-await-transform
<connect-awaitable-promise
> { :rcvr
;// exposition only
DR&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 {rcvr
); set_stopped((DR&&) return noop_coroutine(); }operation-state-task
get_return_object() noexcept {operation-state-task
{ returnconnect-awaitable-promise
>::from_promise(*this)}; coroutine_handle< } env_of_t<const DR&> get_env() const noexcept {rcvr
); return execution::get_env( } }; }Let
operation-state-task
be the following class:namespace std::execution {
operation-state-task
{ struct using operation_state_concept = operation_state_t;connect-awaitable-promise
; using promise_type =coro
;// exposition only
coroutine_handle<>operation-state-task
(coroutine_handle<> h) noexcept :coro
(h) {} explicitoperation-state-task
(operation-state-task
&& o) noexceptcoro
(exchange(o.coro
, {})) {} :operation-state-task
() { if (coro
)coro
.destroy(); } ~ void start() & noexcept {coro
.resume(); } }; }Let
V
name the type, let
await-result-type
<DS,connect-awaitable-promise
>Sigs
name the type:completion_signatures<
SET-VALUE-SIG
(V),// see [exec.snd.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>
suspend-complete
(Fun fun, Ts&&... as) noexcept {// exposition only
auto auto fn = [&, fun]() noexcept { fun(std::forward<Ts>(as)...); }; struct awaiter {fn
; decltype(fn) static constexpr bool await_ready() noexcept { return false; }fn
(); } void await_suspend(coroutine_handle<>) noexcept { [[noreturn]] void await_resume() noexcept { unreachable(); } }; return awaiter{fn}; };operation-state-task
connect-awaitable
(DS sndr, DR rcvr) requires receiver_of<DR, Sigs> { exception_ptr ep; try { if constexpr (same_as<V, void>) { co_await std::move(sndr);suspend-complete
(set_value, std::move(rcvr)); co_await } else {suspend-complete
(set_value, std::move(rcvr), co_await std::move(sndr)); co_await } } catch(...) { ep = current_exception(); }suspend-complete
(set_error, std::move(rcvr), std::move(ep)); co_await } }If
Sndr
does not satisfysender
or ifRcvr
does not satisfyreceiver
,connect(sndr, rcvr)
is ill-formed. Otherwise, the expressionconnect(sndr, rcvr)
is expression-equivalent to:
if that expression is well-formed.
new_
sndr.connect(rcvr)
- Mandates: The type of the expression above satisfies
operation_state
.Otherwise,
if that expression is well-formed.
connect-awaitable
(new_
sndr, rcvr)Otherwise,
connect(sndr, rcvr)
is ill-formed.