From 3e4824757bf9e81295b78db48926f436907e868a Mon Sep 17 00:00:00 2001 From: Eric Niebler Date: Tue, 24 Feb 2026 12:43:06 -0800 Subject: [PATCH 1/2] rework the `STDEXEC_EXPLICIT_THIS_[BEGIN|END]` portability macros for better reusability --- include/stdexec/__detail/__basic_sender.hpp | 2 +- include/stdexec/__detail/__config.hpp | 52 +++++++++---------- include/stdexec/__detail/__connect.hpp | 17 +++--- .../__detail/__get_completion_signatures.hpp | 4 +- include/stdexec/__detail/__preprocessor.hpp | 6 ++- include/stdexec/__detail/__starts_on.hpp | 22 +------- include/stdexec/functional.hpp | 19 +++++++ 7 files changed, 63 insertions(+), 59 deletions(-) diff --git a/include/stdexec/__detail/__basic_sender.hpp b/include/stdexec/__detail/__basic_sender.hpp index 93c4bdf17..e6c2fb2bb 100644 --- a/include/stdexec/__detail/__basic_sender.hpp +++ b/include/stdexec/__detail/__basic_sender.hpp @@ -368,7 +368,7 @@ namespace STDEXEC // Non-standard extension: template STDEXEC_ATTRIBUTE(nodiscard, always_inline) - static constexpr auto static_connect(_Self&& __self, _Receiver __rcvr) noexcept( + static constexpr auto __static_connect(_Self&& __self, _Receiver __rcvr) noexcept( __noexcept_of<__sexpr_impl<__tag_t>::__connect, __copy_cvref_t<_Self, __sexpr>, _Receiver>) -> __result_of<__sexpr_impl<__tag_t>::__connect, __copy_cvref_t<_Self, __sexpr>, _Receiver> { diff --git a/include/stdexec/__detail/__config.hpp b/include/stdexec/__detail/__config.hpp index 001a0861a..9455aa6c2 100644 --- a/include/stdexec/__detail/__config.hpp +++ b/include/stdexec/__detail/__config.hpp @@ -776,51 +776,51 @@ namespace STDEXEC #else -# define STDEXEC_EXPLICIT_THIS_BEGIN(...) \ - static STDEXEC_PP_EXPAND(STDEXEC_PP_CAT(STDEXEC_EXPLICIT_THIS_MANGLE_, __VA_ARGS__) \ - STDEXEC_PP_RPAREN) STDEXEC_PP_LPAREN STDEXEC_EXPLICIT_THIS_ARGS +# define STDEXEC_EXPLICIT_THIS_CALL_OPERATOR_PROBE_operator() STDEXEC_PP_PROBE(~, 1) + +# define STDEXEC_EXPLICIT_THIS_CALL_OPERATOR_PROBE(_NAME) \ + STDEXEC_PP_CHECK(STDEXEC_PP_CAT(STDEXEC_EXPLICIT_THIS_CALL_OPERATOR_PROBE_, _NAME)) + +# define STDEXEC_EXPLICIT_THIS_MANGLE(_NAME) \ + STDEXEC_PP_CAT(__static_, \ + STDEXEC_PP_IIF(STDEXEC_EXPLICIT_THIS_CALL_OPERATOR_PROBE(_NAME), \ + _call, \ + _NAME)) + +# define STDEXEC_EXPLICIT_THIS_EAT_this +# define STDEXEC_EXPLICIT_THIS_MANGLE_auto auto STDEXEC_EXPLICIT_THIS_MANGLE STDEXEC_PP_LPAREN +# define STDEXEC_EXPLICIT_THIS_MANGLE_void void STDEXEC_EXPLICIT_THIS_MANGLE STDEXEC_PP_LPAREN +# define STDEXEC_EXPLICIT_THIS_MANGLE_bool bool STDEXEC_EXPLICIT_THIS_MANGLE STDEXEC_PP_LPAREN # define STDEXEC_EXPLICIT_THIS_ARGS(...) \ STDEXEC_PP_CAT(STDEXEC_EXPLICIT_THIS_EAT_, __VA_ARGS__) STDEXEC_PP_RPAREN -# define STDEXEC_EXPLICIT_THIS_END(_FN) \ +# define STDEXEC_EXPLICIT_THIS_BEGIN(...) \ + static STDEXEC_PP_EXPAND(STDEXEC_PP_CAT(STDEXEC_EXPLICIT_THIS_MANGLE_, __VA_ARGS__) \ + STDEXEC_PP_RPAREN) STDEXEC_PP_LPAREN STDEXEC_EXPLICIT_THIS_ARGS + +# define STDEXEC_EXPLICIT_THIS_END(_NAME) \ template \ STDEXEC_ATTRIBUTE(always_inline) \ - auto _FN(_Ts&&... __args) && STDEXEC_AUTO_RETURN \ + auto _NAME(_Ts&&... __args) && STDEXEC_AUTO_RETURN \ ( \ - decltype(STDEXEC::__get_self<_Ts...>(*this)) \ - ::STDEXEC_PP_CAT(static_, _FN)(std::move(*this), static_cast<_Ts&&>(__args)...) \ + STDEXEC_EXPLICIT_THIS_MANGLE(_NAME)(std::move(*this), static_cast<_Ts&&>(__args)...) \ ) \ \ template \ STDEXEC_ATTRIBUTE(always_inline) \ - auto _FN(_Ts&&... __args) & STDEXEC_AUTO_RETURN \ + auto _NAME(_Ts&&... __args) & STDEXEC_AUTO_RETURN \ ( \ - decltype(STDEXEC::__get_self<_Ts...>(*this)) \ - ::STDEXEC_PP_CAT(static_, _FN)(*this, static_cast<_Ts&&>(__args)...) \ + STDEXEC_EXPLICIT_THIS_MANGLE(_NAME)(*this, static_cast<_Ts&&>(__args)...) \ ) \ \ template \ STDEXEC_ATTRIBUTE(always_inline) \ - auto _FN(_Ts&&... __args) const & STDEXEC_AUTO_RETURN \ + auto _NAME(_Ts&&... __args) const & STDEXEC_AUTO_RETURN \ ( \ - decltype(STDEXEC::__get_self<_Ts...>(*this)) \ - ::STDEXEC_PP_CAT(static_, _FN)(*this, static_cast<_Ts&&>(__args)...) \ + STDEXEC_EXPLICIT_THIS_MANGLE(_NAME)(*this, static_cast<_Ts&&>(__args)...) \ ) -# define STDEXEC_EXPLICIT_THIS_EAT_this -# define STDEXEC_EXPLICIT_THIS_EAT_auto -# define STDEXEC_EXPLICIT_THIS_EAT_void -# define STDEXEC_EXPLICIT_THIS_MANGLE_auto auto STDEXEC_EXPLICIT_THIS_MANGLE STDEXEC_PP_LPAREN -# define STDEXEC_EXPLICIT_THIS_MANGLE_void void STDEXEC_EXPLICIT_THIS_MANGLE STDEXEC_PP_LPAREN -# define STDEXEC_EXPLICIT_THIS_MANGLE(_NAME) STDEXEC_PP_CAT(static_, _NAME) - -namespace STDEXEC -{ - template - constexpr auto __get_self(_Self const &) -> _Self; -} // namespace STDEXEC - #endif // STDEXEC_NO_STDCPP_EXPLICIT_THIS_PARAMETER() //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/include/stdexec/__detail/__connect.hpp b/include/stdexec/__detail/__connect.hpp index f9b0cddd5..4b09205fc 100644 --- a/include/stdexec/__detail/__connect.hpp +++ b/include/stdexec/__detail/__connect.hpp @@ -33,7 +33,7 @@ namespace STDEXEC template concept __with_static_member = requires(__declfn_t<_Sender&&> __sndr, __declfn_t<_Receiver&&> __rcvr) { - STDEXEC_REMOVE_REFERENCE(_Sender)::static_connect(__sndr(), __rcvr()); + STDEXEC_REMOVE_REFERENCE(_Sender)::__static_connect(__sndr(), __rcvr()); }; template @@ -67,8 +67,8 @@ namespace STDEXEC template requires __with_static_member<_Sender, _Receiver> extern STDEXEC_CONNECT_DECLFN_FOR(STDEXEC_REMOVE_REFERENCE(_Sender) // - ::static_connect(__declval<_Sender>(), - __declval<_Receiver>())) + ::__static_connect(__declval<_Sender>(), + __declval<_Receiver>())) __connect_declfn_v<_Sender, _Receiver, true>; template @@ -97,7 +97,7 @@ namespace STDEXEC template requires __with_static_member<_Sender, _Receiver> extern __declfn_t(), __declval<_Receiver>())), + ::__static_connect(__declval<_Sender>(), __declval<_Receiver>())), false> __connect_declfn_v<_Sender, _Receiver, false>; @@ -149,7 +149,7 @@ namespace STDEXEC { return STDEXEC_CONNECT_DECLFN_FOR( STDEXEC_REMOVE_REFERENCE(_Sender) // - ::static_connect(__declval<_Sender>(), __declval<_Receiver>())); + ::__static_connect(__declval<_Sender>(), __declval<_Receiver>())); } else if constexpr (__with_member<_Sender, _Receiver>) { @@ -180,7 +180,8 @@ namespace STDEXEC if constexpr (__with_static_member<_Sender, _Receiver>) { return __declfn(), __declval<_Receiver>())), + ::__static_connect(__declval<_Sender>(), + __declval<_Receiver>())), false>(); } else if constexpr (__with_member<_Sender, _Receiver>) @@ -237,8 +238,8 @@ namespace STDEXEC if constexpr (__connect::__with_static_member<__new_sndr_t, _Receiver>) { return STDEXEC_REMOVE_REFERENCE( - __new_sndr_t)::static_connect(static_cast<__new_sndr_t&&>(__new_sndr), - static_cast<_Receiver&&>(__rcvr)); + __new_sndr_t)::__static_connect(static_cast<__new_sndr_t&&>(__new_sndr), + static_cast<_Receiver&&>(__rcvr)); } else if constexpr (__connect::__with_member<__new_sndr_t, _Receiver>) { diff --git a/include/stdexec/__detail/__get_completion_signatures.hpp b/include/stdexec/__detail/__get_completion_signatures.hpp index 018451614..6138e5c77 100644 --- a/include/stdexec/__detail/__get_completion_signatures.hpp +++ b/include/stdexec/__detail/__get_completion_signatures.hpp @@ -110,7 +110,7 @@ namespace STDEXEC template using __legacy_static_member_result_t = // decltype(STDEXEC_REMOVE_REFERENCE(_Sender) // - ::static_get_completion_signatures(__declval<_Sender>(), __declval<_Env>()...)); + ::__static_get_completion_signatures(__declval<_Sender>(), __declval<_Env>()...)); template using __legacy_member_alias_t = STDEXEC_REMOVE_REFERENCE(_Sender)::completion_signatures; @@ -125,7 +125,7 @@ namespace STDEXEC concept __with_legacy_static_member = requires(__declfn_t<_Sender &&> __sndr, __declfn_t<_Env &&>... __env) { STDEXEC_REMOVE_REFERENCE(_Sender) - ::static_get_completion_signatures(__sndr(), __env()...); + ::__static_get_completion_signatures(__sndr(), __env()...); }; // [WAR]: see nvbugs#5813160 diff --git a/include/stdexec/__detail/__preprocessor.hpp b/include/stdexec/__detail/__preprocessor.hpp index 2d5c7e9e8..fb19d2c87 100644 --- a/include/stdexec/__detail/__preprocessor.hpp +++ b/include/stdexec/__detail/__preprocessor.hpp @@ -36,8 +36,9 @@ #define STDEXEC_PP_IIF_0(_YP, ...) __VA_ARGS__ #define STDEXEC_PP_IIF_1(_YP, ...) _YP #define STDEXEC_PP_IIF_EVAL(_MACRO, ...) _MACRO(__VA_ARGS__) +#define STDEXEC_PP_IIF_CAT(_XP, ...) _XP##__VA_ARGS__ #define STDEXEC_PP_IIF(_XP, _YP, ...) \ - STDEXEC_PP_IIF_EVAL(STDEXEC_PP_CAT(STDEXEC_PP_IIF_, _XP), _YP, __VA_ARGS__) + STDEXEC_PP_IIF_EVAL(STDEXEC_PP_IIF_CAT(STDEXEC_PP_IIF_, _XP), _YP, __VA_ARGS__) #define STDEXEC_PP_COMPL_0 1 // NOLINT(modernize-macro-to-enum) #define STDEXEC_PP_COMPL_1 0 // NOLINT(modernize-macro-to-enum) @@ -49,8 +50,9 @@ #define STDEXEC_PP_COUNT_I(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _NP, ...) _NP // Used to check various properties of arguments +#define STDEXEC_PP_CHECK_EXPAND(...) __VA_ARGS__ #define STDEXEC_PP_CHECK_I(_XP, _NP, ...) _NP -#define STDEXEC_PP_CHECK(...) STDEXEC_PP_EXPAND(STDEXEC_PP_CHECK_I(__VA_ARGS__, 0, )) +#define STDEXEC_PP_CHECK(...) STDEXEC_PP_CHECK_EXPAND(STDEXEC_PP_CHECK_I(__VA_ARGS__, 0, )) #define STDEXEC_PP_PROBE_I(_XP, _NP, ...) _XP, _NP, #define STDEXEC_PP_PROBE(...) STDEXEC_PP_PROBE_I(__VA_ARGS__, 1) diff --git a/include/stdexec/__detail/__starts_on.hpp b/include/stdexec/__detail/__starts_on.hpp index 50ed53bec..4e31ddf69 100644 --- a/include/stdexec/__detail/__starts_on.hpp +++ b/include/stdexec/__detail/__starts_on.hpp @@ -18,6 +18,7 @@ #include "__execution_fwd.hpp" // include these after __execution_fwd.hpp +#include "../functional.hpp" #include "__completion_signatures_of.hpp" #include "__concepts.hpp" #include "__diagnostics.hpp" @@ -30,25 +31,6 @@ namespace STDEXEC { - namespace __detail - { - //! Constant function object always returning `__val_`. - template >> - struct __always - { - _Ty __val_; - - STDEXEC_ATTRIBUTE(always_inline) - constexpr auto operator()() noexcept(__nothrow_move_constructible<_Ty>) -> _Ty - { - return static_cast<_Ty&&>(__val_); - } - }; - - template - __always(_Ty) -> __always<_Ty>; - } // namespace __detail - ///////////////////////////////////////////////////////////////////////////// // [execution.senders.adaptors.starts_on] struct starts_on_t @@ -66,7 +48,7 @@ namespace STDEXEC { auto& [__tag, __sched, __child] = __sndr; return let_value(continues_on(just(), __sched), - __detail::__always{STDEXEC::__forward_like<_Sender>(__child)}); + __always{STDEXEC::__forward_like<_Sender>(__child)}); } template diff --git a/include/stdexec/functional.hpp b/include/stdexec/functional.hpp index d6bc9d179..a6cb2eb49 100644 --- a/include/stdexec/functional.hpp +++ b/include/stdexec/functional.hpp @@ -255,6 +255,25 @@ namespace STDEXEC template STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE __for_each(_Fn) -> __for_each<_Fn>; + //! Constant function object always returning `__value_`. + template + struct __always + { + template + STDEXEC_ATTRIBUTE(host, device, always_inline) + constexpr STDEXEC_EXPLICIT_THIS_BEGIN(auto operator())(this _Self&& __self, _Ts&&...) noexcept + -> auto&& + { + return (static_cast<_Self&&>(__self).__value_); + } + STDEXEC_EXPLICIT_THIS_END(operator()) + + _Ty __value_; + }; + + template + STDEXEC_HOST_DEVICE_DEDUCTION_GUIDE __always(_Ty) -> __always>; + template struct __construct { From 45a4d69789c67cd6319b55ff1381e663bf7a62d2 Mon Sep 17 00:00:00 2001 From: Eric Niebler Date: Tue, 24 Feb 2026 13:13:42 -0800 Subject: [PATCH 2/2] fix the CUDA build --- include/nvexec/stream/let_xxx.cuh | 15 +++++++++------ include/stdexec/__detail/__config.hpp | 6 +++--- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/include/nvexec/stream/let_xxx.cuh b/include/nvexec/stream/let_xxx.cuh index 3d21b965c..9a69c1da7 100644 --- a/include/nvexec/stream/let_xxx.cuh +++ b/include/nvexec/stream/let_xxx.cuh @@ -47,8 +47,11 @@ namespace nv::execution::_strm using __decay_ref_t = STDEXEC::__decay_t&; template - using _mk_result_sender_t = - __mtransform<__q<__decay_ref_t>, __mbind_front_q<__call_result_t, Fun>>; + struct _mk_result_sender + { + template + using __f = __remove_rvalue_reference_t<__call_result_t...>>; + }; template requires sender_in> @@ -57,7 +60,7 @@ namespace nv::execution::_strm struct _result_sender_size { template - using __f = __msize_t, Args...>)>; + using __f = __msize_t, Args...>)>; }; static constexpr std::size_t value = __gather_completions_of_t using opstate_for_t = __mcompose<__mbind_back_q>, - _mk_result_sender_t>; + _mk_result_sender>; template struct __tfx_signal_fn @@ -83,7 +86,7 @@ namespace nv::execution::_strm { template using __f = transform_completion_signatures< - __completion_signatures_of_t<__minvoke<_mk_result_sender_t, Args...>, StreamEnv...>, + __completion_signatures_of_t<__minvoke<_mk_result_sender, Args...>, StreamEnv...>, completion_signatures>; }; @@ -105,7 +108,7 @@ namespace nv::execution::_strm template <__same_as Tag, class... Args> void _complete(Tag, Args&&... args) noexcept { - using result_sender_t = __minvoke<_mk_result_sender_t, Args...>; + using result_sender_t = __minvoke<_mk_result_sender, Args...>; using opstate_t = __minvoke, Args...>; cudaStream_t stream = opstate_->get_stream(); diff --git a/include/stdexec/__detail/__config.hpp b/include/stdexec/__detail/__config.hpp index 9455aa6c2..294226160 100644 --- a/include/stdexec/__detail/__config.hpp +++ b/include/stdexec/__detail/__config.hpp @@ -801,21 +801,21 @@ namespace STDEXEC # define STDEXEC_EXPLICIT_THIS_END(_NAME) \ template \ - STDEXEC_ATTRIBUTE(always_inline) \ + STDEXEC_ATTRIBUTE(always_inline, host, device) \ auto _NAME(_Ts&&... __args) && STDEXEC_AUTO_RETURN \ ( \ STDEXEC_EXPLICIT_THIS_MANGLE(_NAME)(std::move(*this), static_cast<_Ts&&>(__args)...) \ ) \ \ template \ - STDEXEC_ATTRIBUTE(always_inline) \ + STDEXEC_ATTRIBUTE(always_inline, host, device) \ auto _NAME(_Ts&&... __args) & STDEXEC_AUTO_RETURN \ ( \ STDEXEC_EXPLICIT_THIS_MANGLE(_NAME)(*this, static_cast<_Ts&&>(__args)...) \ ) \ \ template \ - STDEXEC_ATTRIBUTE(always_inline) \ + STDEXEC_ATTRIBUTE(always_inline, host, device) \ auto _NAME(_Ts&&... __args) const & STDEXEC_AUTO_RETURN \ ( \ STDEXEC_EXPLICIT_THIS_MANGLE(_NAME)(*this, static_cast<_Ts&&>(__args)...) \