From 85fcc086b82a4d40db6067afeff353f5880ee8f1 Mon Sep 17 00:00:00 2001 From: Ben FrantzDale Date: Wed, 9 Oct 2024 08:04:21 -0400 Subject: [PATCH 1/8] Add doxy comments to some of the metafunctions with breadcrumbs back to MAINTAINERS.md. --- include/stdexec/__detail/__meta.hpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/include/stdexec/__detail/__meta.hpp b/include/stdexec/__detail/__meta.hpp index ddd5d9ef2..2ab262043 100644 --- a/include/stdexec/__detail/__meta.hpp +++ b/include/stdexec/__detail/__meta.hpp @@ -28,6 +28,9 @@ #include "__utility.hpp" namespace stdexec { + //! Convenience metafunction getting the dependant type `__t` out of `_Tp`. + //! That is, `typename _Tp::__t`. + //! See MAINTAINERS.md#class-template-parameters for details. template using __t = typename _Tp::__t; @@ -373,6 +376,9 @@ namespace stdexec { typename __i<_Ok<_Args...>> // ::template __g<_Fn, _Args...>; // + //! Metafunction invocation + //! Given a metafunction, `_Fn`, and args. + //! We expect `_Fn::__f` to be type alias template "implementing" the metafunction `_Fn`. template using __minvoke = // typename __i<_Ok<_Args...>, _Ok<_Fn>> // @@ -818,9 +824,13 @@ namespace stdexec { template using __msingle_or = __mbind_front_q<__msingle_or_, _Default>; + //! A concept checking if `_Ty` has a dependent type `_Ty::__id`. + //! See MAINTAINERS.md#class-template-parameters. template concept __has_id = requires { typename _Ty::__id; }; + //! Identity mapping `_Ty` to itself. + //! That is, `std::is_same_v::__t>`. template struct _Id { using __t = _Ty; @@ -833,6 +843,7 @@ namespace stdexec { //static_assert(!__has_id>); }; + //! Helper metafunction detail of `__id`, below. template struct __id_ { template @@ -845,6 +856,10 @@ namespace stdexec { using __f = _Id<_Ty>; }; + //! Metafunction mapping `_Ty` to either + //! * `typename _Ty::__id` if that exists, or to + //! * `_Ty` (itself) otherwise. + //! See MAINTAINERS.md#class-template-parameters. template using __id = __minvoke<__id_<__has_id<_Ty>>, _Ty>; From 7ea1f5ece78b71a1d0172089f9be1ec72f048fbe Mon Sep 17 00:00:00 2001 From: Ben FrantzDale Date: Wed, 9 Oct 2024 09:38:55 -0400 Subject: [PATCH 2/8] Add comments to particularly `let_*` and `starts_on`, among others. --- include/stdexec/__detail/__basic_sender.hpp | 7 +++++++ include/stdexec/__detail/__domain.hpp | 5 +++++ include/stdexec/__detail/__let.hpp | 17 +++++++++++++++++ include/stdexec/__detail/__meta.hpp | 10 ++++++++++ include/stdexec/__detail/__starts_on.hpp | 2 ++ 5 files changed, 41 insertions(+) diff --git a/include/stdexec/__detail/__basic_sender.hpp b/include/stdexec/__detail/__basic_sender.hpp index 8811ed840..965264534 100644 --- a/include/stdexec/__detail/__basic_sender.hpp +++ b/include/stdexec/__detail/__basic_sender.hpp @@ -504,14 +504,18 @@ namespace stdexec { // __basic_sender template struct __basic_sender { + // See MAINTAINERS.md#class-template-parameters for `__id` and `__t`. using __id = __basic_sender; using __t = __basic_sender; }; + //! A struct template to aid in creating senders. + //! A `__sexpr` template struct __sexpr { using sender_concept = sender_t; + // See MAINTAINERS.md#class-template-parameters for `__id` and `__t`. using __id = __sexpr; using __t = __sexpr; using __desc_t = decltype(_DescriptorFn()); @@ -595,6 +599,9 @@ namespace stdexec { ////////////////////////////////////////////////////////////////////////////////////////////////// // __make_sexpr + //! A tagged function-object + //! Takes data and children and + //! returns `__sexpr_t<_Tag, _Data, _Child...>{_Tag(), data, children...}`. namespace __detail { template struct __make_sexpr_t { diff --git a/include/stdexec/__detail/__domain.hpp b/include/stdexec/__detail/__domain.hpp index ec42d30e7..1448a5d2d 100644 --- a/include/stdexec/__detail/__domain.hpp +++ b/include/stdexec/__detail/__domain.hpp @@ -197,6 +197,11 @@ namespace stdexec { } // namespace __detail ///////////////////////////////////////////////////////////////////////////// + //! Function object implementing `get-domain-early(snd)` + //! from [exec.snd.general] item 3.9. It is the first well-formed expression of + //! a) `get_domain(get_env(sndr))` + //! b) `completion-domain(sndr)` + //! c) `default_domain()` inline constexpr struct __get_early_domain_t { template auto operator()(const _Sender&, _Default __def = {}) const noexcept { diff --git a/include/stdexec/__detail/__let.hpp b/include/stdexec/__detail/__let.hpp index c872028b2..18742ebcd 100644 --- a/include/stdexec/__detail/__let.hpp +++ b/include/stdexec/__detail/__let.hpp @@ -337,6 +337,8 @@ namespace stdexec { }; } + //! Metafunction creating the operation state needed to connect the result of calling + //! the sender factory function, `_Fun`, and passing its result to a receiver. template struct __op_state_for { template @@ -346,6 +348,8 @@ namespace stdexec { _Receiver>; }; + //! The core of the operation state for `let_*`. + //! This gets bundled up into a larger operation state (`__detail::__op_state<...>`). template struct __let_state { using __fun_t = _Fun; @@ -385,10 +389,14 @@ namespace stdexec { _Fun __fun_; STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS _Sched __sched_; + //! Variant to hold the results passed from upstream before passing them to the function: __result_variant __args_{}; + //! Variant type for holding the operation state from connecting + //! the function result to the downstream receiver: __op_state_variant __op_state3_{}; }; + //! Implementation of the `let_*_t` types, where `_Set` is, e.g., `set_value_t` for `let_value`. template struct __let_t { using __domain_t = _Domain; @@ -471,11 +479,18 @@ namespace stdexec { }); }; + //! Helper function to actually invoke the function to produce `let_*`'s sender, + //! connect it to the downstream receiver, and start it. + //! This is the heart of `let_*`. template static void __bind_(_State& __state, _OpState& __op_state, _As&&... __as) { + // Store the passed-in (received) args: auto& __args = __state.__args_.emplace_from(__tup::__mktuple, static_cast<_As&&>(__as)...); + // Apply the function to the args to get the sender: auto __sndr2 = __args.apply(std::move(__state.__fun_), __args); + // Create a receiver based on the state, the computed sender, and the operation state: auto __rcvr2 = __state.__get_result_receiver(__sndr2, __op_state); + // Connect the sender to the receiver and start it: auto& __op2 = __state.__op_state3_.emplace_from( stdexec::connect, std::move(__sndr2), std::move(__rcvr2)); stdexec::start(__op2); @@ -514,8 +529,10 @@ namespace stdexec { _Tag, _As&&... __as) noexcept -> void { if constexpr (__same_as<_Tag, _Set>) { + // Intercept the channel of interest to compute the sender and connect it: __bind(__op_state, static_cast<_As&&>(__as)...); } else { + // Forward the other channels downstream: using _Receiver = decltype(__op_state.__rcvr_); _Tag()(static_cast<_Receiver&&>(__op_state.__rcvr_), static_cast<_As&&>(__as)...); } diff --git a/include/stdexec/__detail/__meta.hpp b/include/stdexec/__detail/__meta.hpp index 2ab262043..f793ca24b 100644 --- a/include/stdexec/__detail/__meta.hpp +++ b/include/stdexec/__detail/__meta.hpp @@ -94,9 +94,11 @@ namespace stdexec { using __msize_t = __muchar (*)[_Np + 1]; // +1 to avoid zero-size array #endif + //! Metafunction selects the first of two type arguments. template using __mfirst = _Tp; + //! Metafunction selects the second of two type arguments. template using __msecond = _Up; @@ -327,6 +329,9 @@ namespace stdexec { template concept _Ok = (STDEXEC_IS_SAME(__ok_t<_Args>, __msuccess) && ...); + //! If both are true: + //! Then __i::__g is an alias for F + //! and __i::__f is an alias for F. template struct __i; @@ -419,6 +424,7 @@ namespace stdexec { using __f = _Fn; }; + //! Metafunction takes a function and provides a checked version of it? template