3333#include " __variant.hpp"
3434
3535#include < exception>
36+ #include < type_traits>
3637
3738namespace stdexec {
3839 // ////////////////////////////////////////////////////////////////////////////
@@ -41,6 +42,11 @@ namespace stdexec {
4142 template <class _SetTag >
4243 struct __let_t ;
4344
45+ template <class _SetTag >
46+ struct __let_tag {
47+ using __t = _SetTag;
48+ };
49+
4450 template <class _SetTag >
4551 inline constexpr __mstring __in_which_let_msg{" In stdexec::let_value(Sender, Function)..." };
4652
@@ -282,32 +288,86 @@ namespace stdexec {
282288
283289 // ! The core of the operation state for `let_*`.
284290 // ! This gets bundled up into a larger operation state (`__detail::__op_state<...>`).
285- template <class _Receiver , class _Fun , class _SetTag , class _Env2 , class ... _Tuples>
291+ template <class _SetTag , class _Sender , class _Fun , class _Receiver , class ... _Tuples>
286292 struct __let_state {
287- using __fun_t = _Fun;
288- using __env2_t = _Env2;
289- using __env_t = __join_env_t <_Env2, env_of_t <_Receiver>>;
290- using __rcvr_t = __receiver_with_env_t <_Receiver, _Env2>;
293+ using __env2_t =
294+ __let::__env2_t <_SetTag, env_of_t <const _Sender&>, env_of_t <const _Receiver&>>;
295+ using __second_rcvr_t = __receiver_with_env_t <_Receiver, __env2_t >;
296+ struct __first_rcvr_t {
297+ using receiver_concept = ::stdexec::receiver_t ;
298+ __let_state& __state;
299+ _Receiver& __rcvr;
300+ template <typename _Tag, typename ... _Args>
301+ constexpr void __impl (_Tag __tag, _Args&&... __args) noexcept {
302+ if constexpr (std::is_same_v<_SetTag, _Tag>) {
303+ using __sender_t = __call_result_t <_Fun, __decay_t <_Args>&...>;
304+ using __submit_t = __submit_result<__sender_t , __env2_t , _Receiver>;
305+ constexpr bool __nothrow_store = (__nothrow_decay_copyable<_Args> && ...);
306+ constexpr bool __nothrow_invoke = __nothrow_callable<_Fun, __decay_t <_Args>&...>;
307+ constexpr bool __nothrow_submit = noexcept (
308+ __state.__storage_ .template emplace <__submit_t >(
309+ __declval<__sender_t >(), __declval<__second_rcvr_t >()));
310+ STDEXEC_TRY {
311+ auto & __tuple =
312+ __state.__args_ .emplace_from (__mktuple, static_cast <_Args&&>(__args)...);
313+ auto && __sender = ::stdexec::__apply (static_cast <_Fun&&>(__state.__fun_ ), __tuple);
314+ __state.__storage_ .template emplace <__monostate>();
315+ __second_rcvr_t __r{__rcvr, static_cast <__env2_t &&>(__state.__env2_ )};
316+ auto & __op = __state.__storage_ .template emplace <__submit_t >(
317+ static_cast <__sender_t &&>(__sender), static_cast <__second_rcvr_t &&>(__r));
318+ __op.submit (static_cast <__sender_t &&>(__sender), static_cast <__second_rcvr_t &&>(__r));
319+ }
320+ STDEXEC_CATCH_ALL {
321+ if constexpr (!(__nothrow_store && __nothrow_invoke && __nothrow_submit)) {
322+ ::stdexec::set_error (static_cast <_Receiver&&>(__rcvr), std::current_exception());
323+ }
324+ }
325+ } else {
326+ __tag (static_cast <_Receiver&&>(__rcvr), static_cast <_Args&&>(__args)...);
327+ }
328+ }
329+ template <typename ... _Args>
330+ constexpr void set_value (_Args&&... __args) noexcept {
331+ __impl (::stdexec::set_value, static_cast <_Args&&>(__args)...);
332+ }
333+ template <typename ... _Args>
334+ constexpr void set_error (_Args&&... __args) noexcept {
335+ __impl (::stdexec::set_error, static_cast <_Args&&>(__args)...);
336+ }
337+ template <typename ... _Args>
338+ constexpr void set_stopped (_Args&&... __args) noexcept {
339+ __impl (::stdexec::set_stopped, static_cast <_Args&&>(__args)...);
340+ }
341+ constexpr decltype (auto ) get_env() const noexcept {
342+ return ::stdexec::get_env (__rcvr);
343+ }
344+ };
345+
291346 using __result_variant = __variant_for<__monostate, _Tuples...>;
292- using __submit_variant = __variant_for<
347+ using __op_state_variant = __variant_for<
293348 __monostate,
294- __mapply<__submit_datum_for<_Receiver, _Fun, _SetTag, _Env2>, _Tuples>...
295- >;
296-
297- template <class _ResultSender , class _OpState >
298- auto __get_result_receiver (const _ResultSender&, _OpState& __op_state) -> decltype(auto ) {
299- return __rcvr_t {__op_state.__rcvr_ , __env2_};
349+ ::stdexec::connect_result_t <_Sender, __first_rcvr_t >,
350+ __mapply<__submit_datum_for<_Receiver, _Fun, _SetTag, __env2_t >, _Tuples>...>;
351+
352+ constexpr explicit __let_state (_Sender&& __sender, _Fun __fun, _Receiver& __r) noexcept (
353+ __nothrow_connectable<_Sender, __first_rcvr_t >
354+ && std::is_nothrow_move_constructible_v<_Fun>)
355+ : __fun_(static_cast <_Fun&&>(__fun))
356+ , __env2_(
357+ // TODO(ericniebler): this needs a fallback
358+ __let::__mk_env2<_SetTag>(::stdexec::get_env(__sender), ::stdexec::get_env(__r))) {
359+ __storage_.emplace_from (
360+ ::stdexec::connect, static_cast <_Sender&&>(__sender), __first_rcvr_t {*this , __r});
300361 }
301362
302363 STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS
303364 _Fun __fun_;
304365 STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS
305- _Env2 __env2_;
366+ __env2_t __env2_;
306367 // ! Variant to hold the results passed from upstream before passing them to the function:
307368 __result_variant __args_{};
308- // ! Variant type for holding the operation state from connecting
309- // ! the function result to the downstream receiver:
310- __submit_variant __storage_{};
369+ // ! Variant type for holding the operation state of the currently in flight operation
370+ __op_state_variant __storage_{};
311371 };
312372
313373 // The set_value completions of:
@@ -504,11 +564,20 @@ namespace stdexec {
504564 }
505565 };
506566
567+ template <class _Sender , class _Fun >
568+ struct __data_t {
569+ _Sender __sndr;
570+ _Fun __fun;
571+ };
572+
573+ template <typename _Sender>
574+ using __sender_of = decltype ((__declval<__data_of<_Sender>>().__sndr));
575+ template <typename _Sender>
576+ using __fun_of = decltype ((__declval<__data_of<_Sender>>().__fun));
577+
507578 // ! Implementation of the `let_*_t` types, where `_SetTag` is, e.g., `set_value_t` for `let_value`.
508579 template <class _SetTag >
509580 struct __let_t {
510- using __t = _SetTag;
511-
512581 template <sender _Sender, __movable_value _Fun>
513582 auto operator ()(_Sender&& __sndr, _Fun __fun) const -> __well_formed_sender auto {
514583 return __make_sexpr<__let_t <_SetTag>>(
@@ -520,117 +589,62 @@ namespace stdexec {
520589 auto operator ()(_Fun __fun) const {
521590 return __closure (*this , static_cast <_Fun&&>(__fun));
522591 }
592+
593+ template <class _Sender >
594+ auto transform_sender (set_value_t , _Sender&& __sndr, __ignore) {
595+ return __sexpr_apply (
596+ static_cast <_Sender&&>(__sndr),
597+ []<class _Fun , class _Child >(__ignore, _Fun&& __fun, _Child&& __child) {
598+ return __make_sexpr<__let_tag<_SetTag>>(
599+ __data_t {static_cast <_Child&&>(__child), static_cast <_Fun&&>(__fun)});
600+ });
601+ }
523602 };
524603
525604 template <class _SetTag >
526605 struct __let_impl : __sexpr_defaults {
527- static constexpr auto get_attrs = []<class _Fun , class _Child >(
528- const _Fun&,
529- [[maybe_unused]]
530- const _Child& __child) noexcept {
531- // BUGBUG:
532- return stdexec::get_env (__child);
533- // return __attrs<__let_t<_SetTag>, _Child, _Fun>{};
534- };
606+ static constexpr auto get_attrs =
607+ []<class _Child , class _Fun >(const __data_t <_Child, _Fun>& __data) noexcept {
608+ // BUGBUG:
609+ return stdexec::get_env (__data.__sndr );
610+ };
535611
536612 static constexpr auto get_completion_signatures =
537613 []<class _Self , class _Env >(_Self&&, _Env&&...) noexcept {
538- static_assert (sender_expr_for<_Self, __let_t <_SetTag>>);
614+ static_assert (sender_expr_for<_Self, __let_tag <_SetTag>>);
539615 if constexpr (__decay_copyable<_Self>) {
540- using __fn_t = __decay_t <__data_of<_Self>>;
541- return __completions_t <__let_t <_SetTag>, __fn_t , __child_of<_Self>, _Env>{};
616+ using __fn_t = __decay_t <__fun_of<_Self>>;
617+ using __result_t =
618+ __completions_t <__let_tag<_SetTag>, __fn_t , __sender_of<_Self>, _Env>;
619+ return __result_t {};
542620 } else {
543621 return __mexception<_SENDER_TYPE_IS_NOT_COPYABLE_, _WITH_SENDER_<_Self>>{};
544622 }
545623 };
546624
547625 static constexpr auto get_state =
548- []<class _Receiver , __decay_copyable _Sender>(_Sender&& __sndr, const _Receiver& __rcvr)
549- requires sender_in<__child_of <_Sender>, env_of_t <_Receiver>>
626+ []<class _Receiver , __decay_copyable _Sender>(_Sender&& __sndr, _Receiver& __rcvr)
627+ requires sender_in<__sender_of <_Sender>, env_of_t <_Receiver>>
550628 {
551- static_assert (sender_expr_for<_Sender, __let_t <_SetTag>>);
552- using _Fun = __decay_t <__data_of<_Sender>>;
553- using _Child = __child_of<_Sender>;
554- using _Env2 = __env2_t <_SetTag, env_of_t <_Child>, env_of_t <_Receiver>>;
555- using __mk_let_state = __mbind_front_q<__let_state, _Receiver, _Fun, _SetTag, _Env2>;
556-
629+ static_assert (sender_expr_for<_Sender, __let_tag<_SetTag>>);
630+ using _Child = __sender_of<_Sender>;
631+ using _Fun = __decay_t <__fun_of<_Sender>>;
632+ using __mk_let_state = __mbind_front_q<__let_state, _SetTag, _Child, _Fun, _Receiver>;
557633 using __let_state_t = __gather_completions_of<
558634 _SetTag,
559635 _Child,
560636 env_of_t <_Receiver>,
561637 __q<__decayed_tuple>,
562- __mk_let_state
563- >;
564-
565- return __sndr.apply (
566- static_cast <_Sender&&>(__sndr),
567- [&]<class _Fn , class _Child >(__ignore, _Fn&& __fn, _Child&& __child) {
568- // TODO(ericniebler): this needs a fallback
569- _Env2 __env2 =
570- __let::__mk_env2<_SetTag>(stdexec::get_env (__child), stdexec::get_env (__rcvr));
571- return __let_state_t {static_cast <_Fn&&>(__fn), static_cast <_Env2&&>(__env2)};
572- });
638+ __mk_let_state>;
639+ auto && [__tag, __data] = static_cast <_Sender&&>(__sndr);
640+ return __let_state_t (
641+ __forward_like<_Sender>(__data).__sndr , __forward_like<_Sender>(__data).__fun , __rcvr);
573642 };
574643
575- // ! Helper function to actually invoke the function to produce `let_*`'s sender,
576- // ! connect it to the downstream receiver, and start it. This is the heart of
577- // ! `let_*`.
578- template <class _State , class _OpState , class ... _As>
579- static void __bind_ (_State& __state, _OpState& __op_state, _As&&... __as) {
580- // Store the passed-in (received) args:
581- auto & __args = __state.__args_ .emplace_from (__mktuple, static_cast <_As&&>(__as)...);
582- // Apply the function to the args to get the sender:
583- auto __sndr2 = stdexec::__apply (std::move (__state.__fun_ ), __args);
584- // Create a receiver based on the state, the computed sender, and the operation state:
585- auto __rcvr2 = __state.__get_result_receiver (__sndr2, __op_state);
586- // Connect the sender to the receiver and start it:
587- using __result_t = decltype (submit_result{std::move (__sndr2), std::move (__rcvr2)});
588- auto & __op = __state.__storage_
589- .template emplace <__result_t >(std::move (__sndr2), std::move (__rcvr2));
590- __op.submit (std::move (__sndr2), std::move (__rcvr2));
591- }
592-
593- template <class _OpState , class ... _As>
594- static void __bind (_OpState& __op_state, _As&&... __as) noexcept {
595- using _State = decltype (__op_state.__state_ );
596- using _Receiver = decltype (__op_state.__rcvr_ );
597- using _Fun = _State::__fun_t ;
598- using _Env2 = _State::__env2_t ;
599- using _JoinEnv2 = __join_env_t <_Env2, env_of_t <_Receiver>>;
600- using _ResultSender = __mcall<__result_sender_fn<_SetTag, _Fun, _JoinEnv2>, _As...>;
601-
602- _State& __state = __op_state.__state_ ;
603- _Receiver& __rcvr = __op_state.__rcvr_ ;
604-
605- if constexpr (
606- (__nothrow_decay_copyable<_As> && ...) && __nothrow_callable<_Fun, __decay_t <_As>&...>
607- && __nothrow_connectable<_ResultSender, __result_receiver_t <_Receiver, _Env2>>) {
608- __bind_ (__state, __op_state, static_cast <_As&&>(__as)...);
609- } else {
610- STDEXEC_TRY {
611- __bind_ (__state, __op_state, static_cast <_As&&>(__as)...);
612- }
613- STDEXEC_CATCH_ALL {
614- using _Receiver = decltype (__op_state.__rcvr_ );
615- stdexec::set_error (static_cast <_Receiver&&>(__rcvr), std::current_exception ());
616- }
617- }
618- }
619-
620- static constexpr auto complete = []<class _OpState , class _Tag , class ... _As>(
621- __ignore,
622- _OpState& __op_state,
623- _Tag,
624- _As&&... __as) noexcept -> void {
625- if constexpr (__same_as<_Tag, _SetTag>) {
626- // Intercept the channel of interest to compute the sender and connect it:
627- __bind (__op_state, static_cast <_As&&>(__as)...);
628- } else {
629- // Forward the other channels downstream:
630- using _Receiver = decltype (__op_state.__rcvr_ );
631- _Tag ()(static_cast <_Receiver&&>(__op_state.__rcvr_ ), static_cast <_As&&>(__as)...);
632- }
633- };
644+ static constexpr auto start =
645+ []<typename _State, typename _Receiver>(_State& __state, _Receiver&) noexcept {
646+ ::stdexec::start (__state.__storage_.template get<1 >());
647+ };
634648 };
635649 } // namespace __let
636650
@@ -639,5 +653,19 @@ namespace stdexec {
639653 inline constexpr let_stopped_t let_stopped{};
640654
641655 template <class _SetTag >
642- struct __sexpr_impl <__let::__let_t <_SetTag>> : __let::__let_impl<_SetTag> { };
656+ struct __sexpr_impl <__let::__let_tag<_SetTag>> : __let::__let_impl<_SetTag> { };
657+
658+ template <class _SetTag >
659+ struct __sexpr_impl <__let::__let_t <_SetTag>> : __sexpr_defaults {
660+ static constexpr auto get_attrs = []<class _Child >(__ignore, const _Child& __child) noexcept {
661+ // BUGBUG:
662+ return stdexec::get_env (__child);
663+ // return __attrs<__let_t<_SetTag>, _Child, _Fun>{};
664+ };
665+
666+ static constexpr auto get_completion_signatures =
667+ []<class _Sender , class ... _Env>(_Sender&&, const _Env&...) noexcept
668+ -> __completion_signatures_of_t <transform_sender_result_t <_Sender, _Env...>, _Env...> {
669+ };
670+ };
643671} // namespace stdexec
0 commit comments