|
3286 | 3286 | forwards all other completion operations unchanged.
|
3287 | 3287 | \end{itemize}
|
3288 | 3288 |
|
| 3289 | +\rSec3[exec.let]{\tcode{execution::let_value}, \tcode{execution::let_error}, \tcode{execution::let_stopped}} |
| 3290 | + |
| 3291 | +\pnum |
| 3292 | +\tcode{let_value}, \tcode{let_error}, and \tcode{let_stopped} transform |
| 3293 | +a sender's value, error, and stopped completions, respectively, |
| 3294 | +into a new child asynchronous operation |
| 3295 | +by passing the sender's result datums to a user-specified callable, |
| 3296 | +which returns a new sender that is connected and started. |
| 3297 | + |
| 3298 | +\pnum |
| 3299 | +For \tcode{let_value}, \tcode{let_error}, and \tcode{let_stopped}, |
| 3300 | +let \exposid{set-cpo} be |
| 3301 | +\tcode{set_value}, \tcode{set_error}, and \tcode{set_stopped}, respectively. |
| 3302 | +Let the expression \exposid{let-cpo} be one of |
| 3303 | +\tcode{let_value}, \tcode{let_error}, or \tcode{let_stopped}. |
| 3304 | +For a subexpression \tcode{sndr}, |
| 3305 | +let \tcode{\exposid{let-env}(sndr)} be expression-equivalent to |
| 3306 | +the first well-formed expression below: |
| 3307 | +\begin{itemize} |
| 3308 | +\item |
| 3309 | +\tcode{SCHED-ENV(get_completion_scheduler<\exposid{decayed-typeof}<\exposid{set-cpo}>>(get_env(sndr)))} |
| 3310 | +\item |
| 3311 | +\tcode{\exposid{MAKE-ENV}(get_domain, get_domain(get_env(sndr)))} |
| 3312 | +\item |
| 3313 | +\tcode{(void(sndr), empty_env\{\})} |
| 3314 | +\end{itemize} |
| 3315 | + |
| 3316 | +\pnum |
| 3317 | +The names \tcode{let_value}, \tcode{let_error}, and \tcode{let_stopped} denote |
| 3318 | +pipeable sender adaptor objects. |
| 3319 | +For subexpressions \tcode{sndr} and \tcode{f}, |
| 3320 | +let \tcode{F} be the decayed type of \tcode{f}. |
| 3321 | +If \tcode{decltype((sndr))} does not satisfy \libconcept{sender} or |
| 3322 | +if \tcode{decltype((f))} does not satisfy \exposconcept{movable-value}, |
| 3323 | +the expression \tcode{\exposid{let-cpo}(sndr, f)} is ill-formed. |
| 3324 | +If \tcode{F} does not satisfy \libconcept{invocable}, |
| 3325 | +the expression \tcode{let_stopped(sndr, f)} is ill-formed. |
| 3326 | + |
| 3327 | +\pnum |
| 3328 | +Otherwise, |
| 3329 | +the expression \tcode{\exposid{let-cpo}(sndr, f)} is expression-equivalent to: |
| 3330 | +\begin{codeblock} |
| 3331 | +transform_sender(@\exposid{get-domain-early}@(sndr), @\exposid{make-sender}@(@\exposid{let-cpo}@, f, sndr)) |
| 3332 | +\end{codeblock} |
| 3333 | +except that \tcode{sndr} is evaluated only once. |
| 3334 | + |
| 3335 | +\pnum |
| 3336 | +The exposition-only class template \exposid{impls-for}\iref{exec.snd.general} |
| 3337 | +is specialized for \exposid{let-cpo} as follows: |
| 3338 | +\begin{codeblock} |
| 3339 | +namespace std::execution { |
| 3340 | + template<class State, class Rcvr, class... Args> |
| 3341 | + void @\exposid{let-bind}@(State& state, Rcvr& rcvr, Args&&... args); // \expos |
| 3342 | + |
| 3343 | + template<> |
| 3344 | + struct @\exposid{impls-for}@<@\exposid{decayed-typeof}@<@\exposid{let-cpo}@>> : @\exposid{default-impls}@ { |
| 3345 | + static constexpr auto @\exposid{get-state}@ = @\seebelow@; |
| 3346 | + static constexpr auto @\exposid{complete}@ = @\seebelow@; |
| 3347 | + }; |
| 3348 | +} |
| 3349 | +\end{codeblock} |
| 3350 | + |
| 3351 | +\pnum |
| 3352 | +Let \exposid{receiver2} denote the following exposition-only class template: |
| 3353 | +\begin{codeblock} |
| 3354 | +namespace std::execution { |
| 3355 | + template<class Rcvr, class Env> |
| 3356 | + struct @\exposid{receiver2}@ { |
| 3357 | + using receiver_concept = receiver_t; |
| 3358 | + |
| 3359 | + template<class... Args> |
| 3360 | + void set_value(Args&&... args) && noexcept { |
| 3361 | + execution::set_value(std::move(@\exposid{rcvr}@), std::forward<Args>(args)...); |
| 3362 | + } |
| 3363 | + |
| 3364 | + template<class Error> |
| 3365 | + void set_error(Error&& err) && noexcept { |
| 3366 | + execution::set_error(std::move(@\exposid{rcvr}@), std::forward<Error>(err)); |
| 3367 | + } |
| 3368 | + |
| 3369 | + void set_stopped() && noexcept { |
| 3370 | + execution::set_stopped(std::move(@\exposid{rcvr}@)); |
| 3371 | + } |
| 3372 | + |
| 3373 | + decltype(auto) get_env() const noexcept { |
| 3374 | + return @\exposid{JOIN-ENV}@(@\exposid{env}@, @\exposid{FWD-ENV}@(execution::get_env(@\exposid{rcvr}@))); |
| 3375 | + } |
| 3376 | + |
| 3377 | + Rcvr& @\exposid{rcvr}@; // exposition only |
| 3378 | + Env @\exposid{env}@; // exposition only |
| 3379 | + }; |
| 3380 | +} |
| 3381 | +\end{codeblock} |
| 3382 | + |
| 3383 | +\pnum |
| 3384 | +\tcode{\exposid{impls-for}<\exposid{decayed-typeof}<\exposid{let-cpo}>>::\exposid{get-state}} |
| 3385 | +is initialized with a callable object equivalent to the following: |
| 3386 | +\begin{codeblock} |
| 3387 | +[]<class Sndr, class Rcvr>(Sndr&& sndr, Rcvr& rcvr) requires @\seebelow@ { |
| 3388 | + auto& [_, fn, child] = sndr; |
| 3389 | + using fn_t = decay_t<decltype(fn)>; |
| 3390 | + using env_t = decltype(@\exposid{let-env}@(child)); |
| 3391 | + using args_variant_t = @\seebelow@; |
| 3392 | + using ops2_variant_t = @\seebelow@; |
| 3393 | + |
| 3394 | + struct @\exposid{state-type}@ { |
| 3395 | + fn_t @\exposid{fn}@; // exposition only |
| 3396 | + env_t @\exposid{env}@; // exposition only |
| 3397 | + args_variant_t @\exposid{args}@; // exposition only |
| 3398 | + ops2_variant_t @\exposid{ops2}@; // exposition only |
| 3399 | + }; |
| 3400 | + return @\exposid{state-type}@{std::forward_like<Sndr>(fn), @\exposid{let-env}@(child), {}, {}}; |
| 3401 | +} |
| 3402 | +\end{codeblock} |
| 3403 | + |
| 3404 | +\pnum |
| 3405 | +Let \tcode{Sigs} be a pack of the arguments |
| 3406 | +to the \tcode{completion_signatures} specialization named by |
| 3407 | +\tcode{completion_signatures_of_t<\exposid{child-type}<Sndr>, env_of_t<Rcvr>>}. |
| 3408 | +Let \tcode{LetSigs} be a pack of those types in \tcode{Sigs} |
| 3409 | +with a return type of \tcode{\exposid{decayed-typeof}<\exposid{set-cpo}>}. |
| 3410 | +Let \exposid{as-tuple} be an alias template |
| 3411 | +such that \tcode{\exposid{as-tuple}<Tag(Args...)>} denotes |
| 3412 | +the type \tcode{\exposid{decayed-tuple}<Args...>}. |
| 3413 | +Then \tcode{args_variant_t} denotes |
| 3414 | +the type \tcode{variant<monostate, \exposid{as-tuple}<LetSigs>...>} |
| 3415 | +except with duplicate types removed. |
| 3416 | + |
| 3417 | +\pnum |
| 3418 | +Given a type \tcode{Tag} and a pack \tcode{Args}, |
| 3419 | +let \exposid{as-sndr2} be an alias template |
| 3420 | +such that \tcode{\exposid{as-sndr2}<Tag(Args...)>} denotes |
| 3421 | +the type \tcode{\exposid{call-result-t}<Fn, decay_t<Args>\&...>}. |
| 3422 | +Then \tcode{ops2_variant_t} denotes |
| 3423 | +the type \tcode{variant<monostate, connect_result_t<\exposid{as-sndr2}<LetSigs>, \exposid{receiver2}<Rcvr, Env>>...>} |
| 3424 | +except with duplicate types removed. |
| 3425 | + |
| 3426 | +\pnum |
| 3427 | +The \grammarterm{requires-clause} constraining the above lambda is satisfied |
| 3428 | +if and only if |
| 3429 | +the types \tcode{args_variant_t} and \tcode{ops2_variant_t} are well-formed. |
| 3430 | + |
| 3431 | +\pnum |
| 3432 | +The exposition-only function template \exposid{let-bind} |
| 3433 | +has effects equivalent to: |
| 3434 | +\begin{codeblock} |
| 3435 | +using args_t = @\exposid{decayed-tuple}@<Args...>; |
| 3436 | +auto mkop2 = [&] { |
| 3437 | + return connect( |
| 3438 | + apply(std::move(state.fn), |
| 3439 | + state.args.template emplace<args_t>(std::forward<Args>(args)...)), |
| 3440 | + @\exposid{receiver2}@{rcvr, std::move(state.env)}); |
| 3441 | +}; |
| 3442 | +start(state.ops2.template emplace<decltype(mkop2())>(@\exposid{emplace-from}@{mkop2})); |
| 3443 | +\end{codeblock} |
| 3444 | + |
| 3445 | +\pnum |
| 3446 | +\tcode{\exposid{impls-for}<\exposid{decayed-typeof}<let-cpo>>::\exposid{complete}} |
| 3447 | +is initialized with a callable object equivalent to the following: |
| 3448 | +\begin{codeblock} |
| 3449 | +[]<class Tag, class... Args> |
| 3450 | + (auto, auto& state, auto& rcvr, Tag, Args&&... args) noexcept -> void { |
| 3451 | + if constexpr (@\libconcept{same_as}@<Tag, @\exposid{decayed-typeof}@<@\exposid{set-cpo}@>>) { |
| 3452 | + @\exposid{TRY-EVAL}@(rcvr, @\exposid{let-bind}@(state, rcvr, std::forward<Args>(args)...)); |
| 3453 | + } else { |
| 3454 | + Tag()(std::move(rcvr), std::forward<Args>(args)...); |
| 3455 | + } |
| 3456 | + } |
| 3457 | +\end{codeblock} |
| 3458 | + |
| 3459 | +\pnum |
| 3460 | +Let \tcode{sndr} and \tcode{env} be subexpressions, and |
| 3461 | +let \tcode{Sndr} be \tcode{decltype((sndr))}. |
| 3462 | +If |
| 3463 | +\tcode{\exposconcept{sender-for<}Sndr, \exposid{decayed-typeof}<\exposid{let-cpo}>>} |
| 3464 | +is \tcode{false}, |
| 3465 | +then the expression \tcode{\exposid{let-cpo}.transform_env(sndr, env)} |
| 3466 | +is ill-formed. |
| 3467 | +Otherwise, it is equal to |
| 3468 | +\tcode{\exposid{JOIN-ENV}(\exposid{let-env}(sndr), \exposid{FWD-ENV}(env))}. |
| 3469 | + |
| 3470 | +\pnum |
| 3471 | +Let the subexpression \tcode{out_sndr} denote |
| 3472 | +the result of the invocation \tcode{\exposid{let-cpo}(sndr, f)} or |
| 3473 | +an object equal to such, and |
| 3474 | +let the subexpression \tcode{rcvr} denote a receiver |
| 3475 | +such that the expression \tcode{connect(out_sndr, rcvr)} is well-formed. |
| 3476 | +The expression \tcode{connect(out_sndr, rcvr)} has undefined behavior |
| 3477 | +unless it creates an asynchronous operation\iref{exec.async.ops} that, |
| 3478 | +when started: |
| 3479 | +\begin{itemize} |
| 3480 | +\item |
| 3481 | +invokes \tcode{f} when \exposid{set-cpo} is called |
| 3482 | +with \tcode{sndr}'s result datums, |
| 3483 | +\item |
| 3484 | +makes its completion dependent on |
| 3485 | +the completion of a sender returned by \tcode{f}, and |
| 3486 | +\item |
| 3487 | +propagates the other completion operations sent by \tcode{sndr}. |
| 3488 | +\end{itemize} |
| 3489 | + |
3289 | 3490 | \rSec1[exec.util]{Sender/receiver utilities}
|
3290 | 3491 |
|
3291 | 3492 |
|
|
0 commit comments