Skip to content

Commit be83f39

Browse files
committed
P3373R4 Of Operation States and Their Lifetimes
Fixes NB CA-338 (C++26 CD).
1 parent b536020 commit be83f39

1 file changed

Lines changed: 118 additions & 44 deletions

File tree

source/exec.tex

Lines changed: 118 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3900,6 +3900,8 @@
39003900
\tcode{set_value}, \tcode{set_error}, and \tcode{set_stopped}, respectively.
39013901
Let the expression \exposid{let-cpo} be one of
39023902
\tcode{let_value}, \tcode{let_error}, or \tcode{let_stopped}.
3903+
Let \exposid{let-tag} denote a unique, empty class type for each of
3904+
\tcode{let_value}, \tcode{let_error}, and \tcode{let_stopped}.
39033905
For a subexpression \tcode{sndr},
39043906
let \tcode{\exposid{let-env}(sndr)} be expression-equivalent to
39053907
the first well-formed expression below:
@@ -3931,19 +3933,34 @@
39313933
\end{codeblock}
39323934
except that \tcode{sndr} is evaluated only once.
39333935

3936+
\pnum
3937+
Let \exposid{let-data} denote the following exposition-only class template:
3938+
\begin{codeblock}
3939+
template<class Sndr, class Fn>
3940+
struct @\exposid{let-data}@ {
3941+
Sndr @\exposid{sndr}@; // \expos
3942+
Fn @\exposid{fn}@; // \expos
3943+
};
3944+
\end{codeblock}
3945+
3946+
\pnum
3947+
Then let the expression \tcode{\exposid{let-cpo}.transform_sender(s, es...)}
3948+
be expression-equivalent to:
3949+
\begin{codeblock}
3950+
@\exposid{make-sender}@(@\exposid{let-tag}@{}, @\exposid{let-data}@{s.template @\exposid{get}@<2>(), s.template @\exposid{get}@<1>()})
3951+
\end{codeblock}
3952+
except that \tcode{s} is evaluated only once.
3953+
39343954
\pnum
39353955
The exposition-only class template \exposid{impls-for}\iref{exec.snd.expos}
3936-
is specialized for \exposid{let-cpo} as follows:
3956+
is specialized for \exposid{let-tag} as follows:
39373957
\indexlibraryglobal{\exposid{impls-for}<\exposid{decayed-typeof}<\exposid{let-cpo}>>}
39383958
\begin{codeblock}
39393959
namespace std::execution {
3940-
template<class State, class Rcvr, class... Args>
3941-
void @\exposid{let-bind}@(State& state, Rcvr& rcvr, Args&&... args); // \expos
3942-
39433960
template<>
3944-
struct @\exposid{impls-for}@<@\exposid{decayed-typeof}@<@\exposid{let-cpo}@>> : @\exposid{default-impls}@ {
3961+
struct @\exposid{impls-for}@<@\exposid{let-tag}@> : @\exposid{default-impls}@ {
39453962
static constexpr auto @\exposid{get-state}@ = @\seebelow@;
3946-
static constexpr auto @\exposid{complete}@ = @\seebelow@;
3963+
static constexpr auto @\exposid{start}@ = @\seebelow@;
39473964

39483965
template<class Sndr, class... Env>
39493966
static consteval void @\exposid{check-types}@();
@@ -4032,24 +4049,98 @@
40324049
\end{itemdescr}
40334050

40344051
\pnum
4035-
\tcode{\exposid{impls-for}<\exposid{decayed-typeof}<\exposid{let-cpo}>>::\exposid{get-state}}
4052+
Let \exposid{let-state} denote the following exposition-only class template:
4053+
\begin{codeblock}
4054+
template<class Cpo, class Sndr, class Fn, class Rcvr, class ArgsVariant, class OpsVariant>
4055+
struct @\exposid{let-state}@ {
4056+
using @\exposid{env_t}@ = decltype(@\exposid{let-env}@(declval<Sndr>())); // \expos
4057+
Fn @\exposid{fn}@; // \expos
4058+
env_t @\exposid{env}@; // \expos
4059+
ArgsVariant @\exposid{args}@; // \expos
4060+
OpsVariant @\exposid{ops}@; // \expos
4061+
4062+
template<class Tag, class... Ts>
4063+
constexpr void @\exposid{impl}@(Rcvr& rcvr, Tag tag, Ts&&... ts) noexcept // \expos
4064+
{
4065+
using args_t = @\exposid{decayed-tuple}@<Ts...>;
4066+
using receiver_type = @\exposid{receiver2}@<Rcvr, env_t>;
4067+
using sender_type = apply_result_t<Fn, args_t&>;
4068+
if constexpr (is_same_v<Tag, Cpo>) {
4069+
try {
4070+
auto& tuple = @\exposid{args}@.template emplace<args_t>(std::forward<Ts>(ts)...);
4071+
@\exposid{ops}@.template emplace<monostate>();
4072+
auto&& sndr = apply(std::move(@\exposid{fn}@), tuple);
4073+
using op_t = connect_result_t<sender_type, receiver_type>;
4074+
auto mkop2 = [&] {
4075+
return connect(std::forward<sender_type>(sndr),
4076+
receiver_type{@\exposid{rcvr}@, @\exposid{env}@});
4077+
};
4078+
auto& op = @\exposid{ops}@.template emplace<op_t>(@\exposid{emplace-from}@{mkop2});
4079+
start(op);
4080+
} catch (...) {
4081+
constexpr bool nothrow =
4082+
is_nothrow_constructible_v<args_t, Ts...> &&
4083+
is_nothrow_applicable_v<Fn, args_t&> &&
4084+
noexcept(connect(declval<sender_type>(), receiver_type{@\exposid{rcvr}@, @\exposid{env}@}));
4085+
if constexpr (!nothrow) {
4086+
set_error(std::move(@\exposid{rcvr}@), current_exception());
4087+
}
4088+
}
4089+
} else {
4090+
tag(std::move(@\exposid{rcvr}@), std::forward<Ts>(ts)...);
4091+
}
4092+
}
4093+
4094+
struct @\exposid{receiver}@ { // \expos
4095+
@\exposid{let-state}@& state; // \expos
4096+
Rcvr& @\exposid{rcvr}@; // \expos
4097+
4098+
using receiver_concept = receiver_t;
4099+
4100+
template<class... Args>
4101+
constexpr void set_value(Args&&... args) noexcept {
4102+
@\exposid{state}@.@\exposid{impl}@(@\exposid{rcvr}@, execution::set_value, std::forward<Args>(args)...);
4103+
}
4104+
template<class... Args>
4105+
constexpr void set_error(Args&&... args) noexcept {
4106+
@\exposid{state}@.@\exposid{impl}@(@\exposid{rcvr}@, execution::set_error, std::forward<Args>(args)...);
4107+
}
4108+
template<class... Args>
4109+
constexpr void set_stopped(Args&&... args) noexcept {
4110+
@\exposid{state}@.@\exposid{impl}@(@\exposid{rcvr}@, execution::set_stopped, std::forward<Args>(args)...);
4111+
}
4112+
4113+
constexpr env_of_t<const Rcvr&> get_env() const noexcept {
4114+
return execution::get_env(@\exposid{rcvr}@);
4115+
}
4116+
4117+
};
4118+
4119+
using @\exposid{op_t}@ = connect_result_t<Sndr, @\exposid{receiver}@>; // \expos
4120+
4121+
constexpr @\exposid{let-state}@(Sndr&& sndr, Fn fn, Rcvr& rcvr) // \expos
4122+
: fn(std::move(fn)), env(@\exposid{let-env}@(sndr)),
4123+
ops(in_place_type<@\exposid{op_t}@>, std::forward<Sndr>(sndr),
4124+
@\exposid{receiver}@{*this, rcvr})
4125+
{}
4126+
4127+
};
4128+
\end{codeblock}
4129+
4130+
\pnum
4131+
\tcode{\exposid{impls-for}<\exposid{let-tag}>::\exposid{get-state}}
40364132
is initialized with a callable object equivalent to the following:
40374133
\begin{codeblock}
40384134
[]<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) requires @\seebelow@ {
4039-
auto& [_, fn, child] = sndr;
4135+
auto& [_, data] = sndr;
4136+
auto& [child, fn] = data;
4137+
using child_t = decltype(std::forward_like<Sndr>(child));
40404138
using fn_t = decay_t<decltype(fn)>;
4041-
using env_t = decltype(@\exposid{let-env}@(child));
40424139
using args_variant_t = @\seebelow@;
4043-
using ops2_variant_t = @\seebelow@;
4044-
4045-
struct @\exposid{state-type}@ {
4046-
fn_t @\exposid{fn}@; // \expos
4047-
env_t @\exposid{env}@; // \expos
4048-
args_variant_t @\exposid{args}@; // \expos
4049-
ops2_variant_t @\exposid{ops2}@; // \expos
4050-
};
4051-
return @\exposid{state-type}@{@\exposid{allocator-aware-forward}@(std::forward_like<Sndr>(fn), rcvr),
4052-
@\exposid{let-env}@(child), {}, {}};
4140+
using ops_variant_t = @\seebelow@;
4141+
using state_t = @\exposid{let-state}@<@\exposid{decayed-typeof}@<@\exposid{set-cpo}@>, child_t, fn_t, Rcvr,
4142+
args_variant_t, ops_variant_t>;
4143+
return state_t(std::forward_like<Sndr>(child), std::forward_like<Sndr>(fn), @\exposid{rcvr}@);
40534144
}
40544145
\end{codeblock}
40554146

@@ -4071,10 +4162,12 @@
40714162
let \exposid{as-sndr2} be an alias template
40724163
such that \tcode{\exposid{as-sndr2}<Tag(Args...)>} denotes
40734164
the type \tcode{\exposid{call-result-t}<F, decay_t<Args>\&...>}.
4074-
Then \tcode{ops2_variant_t} denotes
4165+
Then \tcode{ops_variant_t} denotes
40754166
the type
40764167
\begin{codeblock}
4077-
variant<monostate, connect_result_t<@\exposid{as-sndr2}@<LetSigs>, @\exposid{receiver2}@<Rcvr, env_t>>...>
4168+
variant<monostate,
4169+
connect_result_t<child_t, @\exposid{let-state}@::@\exposid{receiver}@>,
4170+
connect_result_t<@\exposid{as-sndr2}@<LetSigs>, @\exposid{receiver2}@<Rcvr, env_t>>...>
40784171
\end{codeblock}
40794172
except with duplicate types removed.
40804173

@@ -4084,31 +4177,12 @@
40844177
the types \tcode{args_variant_t} and \tcode{ops2_variant_t} are well-formed.
40854178

40864179
\pnum
4087-
The exposition-only function template \exposid{let-bind}
4088-
has effects equivalent to:
4089-
\begin{codeblock}
4090-
using args_t = @\exposid{decayed-tuple}@<Args...>;
4091-
auto mkop2 = [&] {
4092-
return connect(
4093-
apply(std::move(state.fn),
4094-
state.args.template emplace<args_t>(std::forward<Args>(args)...)),
4095-
@\exposid{receiver2}@{rcvr, std::move(state.env)});
4096-
};
4097-
start(state.ops2.template emplace<decltype(mkop2())>(@\exposid{emplace-from}@{mkop2}));
4098-
\end{codeblock}
4099-
4100-
\pnum
4101-
\tcode{\exposid{impls-for}<\exposid{decayed-typeof}<\exposid{let-cpo}>>::\exposid{complete}}
4180+
\tcode{\exposid{impls-for}<\exposid{let-tag}>::\exposid{start}}
41024181
is initialized with a callable object equivalent to the following:
41034182
\begin{codeblock}
4104-
[]<class Tag, class... Args>
4105-
(auto, auto& state, auto& rcvr, Tag, Args&&... args) noexcept -> void {
4106-
if constexpr (@\libconcept{same_as}@<Tag, @\exposid{decayed-typeof}@<@\exposid{set-cpo}@>>) {
4107-
@\exposid{TRY-EVAL}@(rcvr, @\exposid{let-bind}@(state, rcvr, std::forward<Args>(args)...));
4108-
} else {
4109-
Tag()(std::move(rcvr), std::forward<Args>(args)...);
4110-
}
4111-
}
4183+
[]<class State, class Rcvr>(State& state, Rcvr&) noexcept {
4184+
start(get<typename State::@\exposid{op_t}@>(state.@\exposid{ops}@));
4185+
}
41124186
\end{codeblock}
41134187

41144188
\pnum

0 commit comments

Comments
 (0)