From acde355bb58a81f89b68d522e6ef534b9113d4b0 Mon Sep 17 00:00:00 2001 From: Eric Niebler Date: Fri, 1 Mar 2024 09:20:59 -0800 Subject: [PATCH] start work of porting stdexec to use member functions instead of tag_invoke --- .clang-format | 2 +- .github/workflows/ci.cpu.yml | 1 + .github/workflows/ci.gpu.yml | 1 + include/exec/__detail/__bwos_lifo_queue.hpp | 10 ++- include/exec/static_thread_pool.hpp | 2 +- include/stdexec/__detail/__basic_sender.hpp | 87 +++++++++++---------- include/stdexec/__detail/__cpo.hpp | 42 +++++----- include/stdexec/__detail/__meta.hpp | 9 +++ include/stdexec/execution.hpp | 2 +- 9 files changed, 84 insertions(+), 72 deletions(-) diff --git a/.clang-format b/.clang-format index 0ae8c7b9a..096accfa8 100644 --- a/.clang-format +++ b/.clang-format @@ -81,7 +81,7 @@ KeepEmptyLinesAtTheStartOfBlocks: true LambdaBodyIndentation: Signature LineEnding: LF Macros: [ - 'STDEXEC_MEMFN_DECL(X)=X', + 'STDEXEC_MEMFN_DECL(...)=__VA_ARGS__', 'STDEXEC_ATTRIBUTE(X)=__attribute__(X) //', 'STDEXEC_NO_UNIQUE_ADDRESS=[[no_unique_address]]', 'STDEXEC_IMMOVABLE_NO_UNIQUE_ADDRESS=[[no_unique_address]]', diff --git a/.github/workflows/ci.cpu.yml b/.github/workflows/ci.cpu.yml index ca5d2f06d..ee7934999 100644 --- a/.github/workflows/ci.cpu.yml +++ b/.github/workflows/ci.cpu.yml @@ -4,6 +4,7 @@ on: push: branches: - main + - "member-function-customization" - "pull-request/[0-9]+" concurrency: diff --git a/.github/workflows/ci.gpu.yml b/.github/workflows/ci.gpu.yml index 6f8d3192b..09f644411 100644 --- a/.github/workflows/ci.gpu.yml +++ b/.github/workflows/ci.gpu.yml @@ -4,6 +4,7 @@ on: push: branches: - main + - "member-function-customization" - "pull-request/[0-9]+" concurrency: diff --git a/include/exec/__detail/__bwos_lifo_queue.hpp b/include/exec/__detail/__bwos_lifo_queue.hpp index b589a730a..26fff05ea 100644 --- a/include/exec/__detail/__bwos_lifo_queue.hpp +++ b/include/exec/__detail/__bwos_lifo_queue.hpp @@ -455,12 +455,14 @@ namespace exec::bwos { auto lifo_queue::block_type::takeover() noexcept -> takeover_result { std::uint64_t spos = steal_tail_.exchange(block_size(), std::memory_order_relaxed); if (spos == block_size()) [[unlikely]] { - return {static_cast(head_.load(std::memory_order_relaxed)), - static_cast(tail_.load(std::memory_order_relaxed))}; + return { + static_cast(head_.load(std::memory_order_relaxed)), + static_cast(tail_.load(std::memory_order_relaxed))}; } head_.store(spos, std::memory_order_relaxed); - return {static_cast(spos), - static_cast(tail_.load(std::memory_order_relaxed))}; + return { + static_cast(spos), + static_cast(tail_.load(std::memory_order_relaxed))}; } template diff --git a/include/exec/static_thread_pool.hpp b/include/exec/static_thread_pool.hpp index 78f17e3a6..47429140d 100644 --- a/include/exec/static_thread_pool.hpp +++ b/include/exec/static_thread_pool.hpp @@ -1446,7 +1446,7 @@ namespace exec { using NextReceiver = stdexec::__t>; using ItemOperation = connect_result_t; - using ItemAllocator = std::allocator_traits::template rebind_alloc< + using ItemAllocator = typename std::allocator_traits::template rebind_alloc< __manual_lifetime>; std::vector<__manual_lifetime, ItemAllocator> items_; diff --git a/include/stdexec/__detail/__basic_sender.hpp b/include/stdexec/__detail/__basic_sender.hpp index ace8a9d55..ce16178b8 100644 --- a/include/stdexec/__detail/__basic_sender.hpp +++ b/include/stdexec/__detail/__basic_sender.hpp @@ -148,11 +148,11 @@ namespace stdexec { using __state_type_t = __decay_t<__result_of<__sexpr_impl<_Tag>::get_state, _Sexpr, _Receiver&>>; - template + template using __env_type_t = __result_of< - __sexpr_impl<_Tag>::get_env, + __sexpr_impl<__meval<__msecond, _Self, _Tag>>::get_env, _Index, - __state_type_t<_Tag, _Sexpr, _Receiver>&, + __state_type_t<__meval<__msecond, _Self, _Tag>, _Sexpr, _Receiver>&, _Receiver&>; template @@ -245,18 +245,27 @@ namespace stdexec { // return __t{__parent}; // } - template <__completion_tag _Tag, class... _Args> + template STDEXEC_ATTRIBUTE((always_inline)) - friend void - tag_invoke(_Tag, __t&& __self, _Args&&... __args) noexcept { - __self.__op_->__complete(_Idx(), _Tag(), static_cast<_Args&&>(__args)...); + STDEXEC_MEMFN_DECL(void set_value)(this __t&& __self, _Args&&... __args) noexcept { + __self.__op_->__complete(_Idx(), stdexec::set_value, static_cast<_Args&&>(__args)...); } - template _Tag, class _SexprTag = __tag_t> + template STDEXEC_ATTRIBUTE((always_inline)) - friend auto - tag_invoke(_Tag, const __t& __self) noexcept - -> __env_type_t<_SexprTag, _Idx, _Sexpr, _Receiver> { + STDEXEC_MEMFN_DECL(void set_error)(this __t&& __self, _Error&& __err) noexcept { + __self.__op_->__complete(_Idx(), stdexec::set_error, static_cast<_Error&&>(__err)); + } + + STDEXEC_ATTRIBUTE((always_inline)) + STDEXEC_MEMFN_DECL(void set_stopped)(this __t&& __self) noexcept { + __self.__op_->__complete(_Idx(), stdexec::set_stopped); + } + + template <__same_as<__t> _Self> + STDEXEC_ATTRIBUTE((always_inline)) + STDEXEC_MEMFN_DECL(auto get_env)(this const _Self& __self) noexcept + -> __env_type_t<_Self, __tag_t, _Idx, _Sexpr, _Receiver> { return __self.__op_->__get_env(_Idx()); } }; @@ -283,6 +292,10 @@ namespace stdexec { auto __rcvr() & noexcept -> _Receiver& { return __rcvr_; } + + auto __rcvr() const & noexcept -> const _Receiver& { + return __rcvr_; + } }; // template @@ -381,10 +394,8 @@ namespace stdexec { __sexpr_apply(static_cast<_Sexpr&&>(__sexpr), __connect_fn<_Sexpr, _Receiver>{this})) { } - template _Tag2> STDEXEC_ATTRIBUTE((always_inline)) - friend void - tag_invoke(_Tag2, __op_state& __self) noexcept { + STDEXEC_MEMFN_DECL(void start)(this __op_state& __self) noexcept { using __tag_t = typename __op_state::__tag_t; auto&& __rcvr = __self.__rcvr(); __tup::__apply( @@ -407,7 +418,8 @@ namespace stdexec { template STDEXEC_ATTRIBUTE((always_inline)) auto - __get_env(_Index) noexcept -> __env_type_t<__tag_t, _Index, _Sexpr, _Receiver> { + __get_env(_Index) const noexcept + -> __env_type_t<_Index, __tag_t, _Index, _Sexpr, _Receiver> { const auto& __rcvr = this->__rcvr(); return __sexpr_impl<__tag_t>::get_env(_Index(), this->__state_, __rcvr); } @@ -516,43 +528,34 @@ namespace stdexec { static_cast<_Child&&>(__child)...)) { } - template - using __impl = __sexpr_impl<__meval<__msecond, _Tag, __tag_t>>; + template + using __impl = __sexpr_impl<__meval<__msecond, _Self, __tag_t>>; - template _Tag, same_as<__sexpr> _Self> + template _Self> STDEXEC_ATTRIBUTE((always_inline)) - friend auto - tag_invoke(_Tag, const _Self& __self) noexcept // - -> __msecond< - __if_c && same_as<_Self, __sexpr>>, // - __result_of<__sexpr_apply, const _Self&, __get_attrs_fn<__tag_t>>> { - return __sexpr_apply(__self, __detail::__drop_front(__impl<_Tag>::get_attrs)); + STDEXEC_MEMFN_DECL(auto get_env)(this const _Self& __self) noexcept // + -> __result_of<__sexpr_apply, const _Self&, __get_attrs_fn<__tag_t>> { + return __sexpr_apply(__self, __detail::__drop_front(__impl<_Self>::get_attrs)); } - template _Tag, __decays_to<__sexpr> _Self, class _Env> + template <__decays_to<__sexpr> _Self, class _Env> STDEXEC_ATTRIBUTE((always_inline)) - friend auto - tag_invoke(_Tag, _Self&& __self, _Env&& __env) noexcept // + STDEXEC_MEMFN_DECL(auto get_completion_signatures)(this _Self&& __self, _Env&& __env) noexcept -> __msecond< - __if_c && __decays_to<_Self, __sexpr>>, - __result_of<__impl<_Tag>::get_completion_signatures, _Self, _Env>> { + __if_c<__decays_to<_Self, __sexpr>>, + __result_of<__impl<_Self>::get_completion_signatures, _Self, _Env>> { return {}; } // BUGBUG fix receiver constraint here: - template < - same_as _Tag, - __decays_to<__sexpr> _Self, - /*receiver*/ class _Receiver> + template <__decays_to<__sexpr> _Self, /*receiver*/ class _Receiver> STDEXEC_ATTRIBUTE((always_inline)) - friend auto - tag_invoke(_Tag, _Self&& __self, _Receiver&& __rcvr) // - noexcept(noexcept( - __impl<_Tag>::connect(static_cast<_Self&&>(__self), static_cast<_Receiver&&>(__rcvr)))) // + STDEXEC_MEMFN_DECL(auto connect)(this _Self&& __self, _Receiver&& __rcvr) // + noexcept(__noexcept_of<__impl<_Self>::connect, _Self, _Receiver>) // -> __msecond< - __if_c && __decays_to<_Self, __sexpr>>, - __result_of<__impl<_Tag>::connect, _Self, _Receiver>> { - return __impl<_Tag>::connect(static_cast<_Self&&>(__self), static_cast<_Receiver&&>(__rcvr)); + __if_c<__decays_to<_Self, __sexpr>>, + __result_of<__impl<_Self>::connect, _Self, _Receiver>> { + return __impl<_Self>::connect(static_cast<_Self&&>(__self), static_cast<_Receiver&&>(__rcvr)); } template @@ -566,8 +569,8 @@ namespace stdexec { template _Self> STDEXEC_ATTRIBUTE((always_inline)) - friend decltype(auto) - get(_Self&& __self) noexcept + friend auto + get(_Self&& __self) noexcept -> decltype(auto) requires __detail::__in_range<_Idx, __desc_t> { if constexpr (_Idx == 0) { diff --git a/include/stdexec/__detail/__cpo.hpp b/include/stdexec/__detail/__cpo.hpp index 8a5771128..4711a2210 100644 --- a/include/stdexec/__detail/__cpo.hpp +++ b/include/stdexec/__detail/__cpo.hpp @@ -18,10 +18,6 @@ #include "__config.hpp" #include "__execution_fwd.hpp" -#define STDEXEC_EAT_THIS_this -#define STDEXEC_EAT_AUTO_auto -#define STDEXEC_EAT_VOID_void - /////////////////////////////////////////////////////////////////////////////// /// To hook a customization point like stdexec::get_env, first bring the names /// in stdexec::tags into scope: @@ -38,31 +34,31 @@ /// } /// @endcode #define STDEXEC_MEMFN_DECL(...) \ - friend STDEXEC_TAG_INVOKE(STDEXEC_IS_AUTO(__VA_ARGS__), __VA_ARGS__) STDEXEC_TAG_INVOKE_ARGS + friend STDEXEC_MEMFN_DECL_TAG_INVOKE( \ + STDEXEC_CHECK(STDEXEC_CAT(STDEXEC_MEMFN_DECL_PROBE_, __VA_ARGS__)), __VA_ARGS__) \ + STDEXEC_MEMFN_DECL_ARGS -#define STDEXEC_TAG_INVOKE(_ISAUTO, ...) \ - STDEXEC_IIF(_ISAUTO, STDEXEC_RETURN_AUTO, STDEXEC_RETURN_TYPE)(__VA_ARGS__) \ - tag_invoke( \ - STDEXEC_IIF(_ISAUTO, STDEXEC_TAG_AUTO, STDEXEC_TAG_WHAT)(__VA_ARGS__) +#define STDEXEC_MEMFN_DECL_TAG_INVOKE(_WHICH, ...) \ + STDEXEC_CAT(STDEXEC_MEMFN_DECL_RETURN_, _WHICH)(__VA_ARGS__) \ + tag_invoke(const STDEXEC_CAT(STDEXEC_MEMFN_DECL_TAG_, _WHICH)(__VA_ARGS__), -#define STDEXEC_PROBE_AUTO_auto STDEXEC_PROBE(~) -#define STDEXEC_IS_AUTO(_TY, ...) STDEXEC_CHECK(STDEXEC_CAT(STDEXEC_PROBE_AUTO_, _TY)) +#define STDEXEC_MEMFN_DECL_ARGS(...) \ + STDEXEC_CAT(STDEXEC_EAT_THIS_, __VA_ARGS__)) -#define STDEXEC_PROBE_VOID_void STDEXEC_PROBE(~) -#define STDEXEC_IS_VOID(_TY, ...) STDEXEC_CHECK(STDEXEC_CAT(STDEXEC_PROBE_VOID_, _TY)) - -#define STDEXEC_RETURN_AUTO(...) auto -#define STDEXEC_RETURN_TYPE(...) ::stdexec::__arg_type_t +#define STDEXEC_EAT_THIS_this +#define STDEXEC_EAT_AUTO_auto +#define STDEXEC_EAT_VOID_void -#define STDEXEC_TAG_AUTO(...) STDEXEC_CAT(STDEXEC_CAT(STDEXEC_EAT_AUTO_, __VA_ARGS__), _t) -#define STDEXEC_TAG_WHAT(...) \ - STDEXEC_IIF(STDEXEC_IS_VOID(__VA_ARGS__), STDEXEC_TAG_VOID, STDEXEC_TAG_TYPE)(__VA_ARGS__) +#define STDEXEC_MEMFN_DECL_PROBE_auto STDEXEC_PROBE(~, 1) +#define STDEXEC_MEMFN_DECL_PROBE_void STDEXEC_PROBE(~, 2) -#define STDEXEC_TAG_VOID(...) STDEXEC_CAT(STDEXEC_CAT(STDEXEC_EAT_VOID_, __VA_ARGS__), _t) -#define STDEXEC_TAG_TYPE(...) ::stdexec::__tag_type_t +#define STDEXEC_MEMFN_DECL_RETURN_0(...) ::stdexec::__arg_type_t +#define STDEXEC_MEMFN_DECL_RETURN_1(...) auto +#define STDEXEC_MEMFN_DECL_RETURN_2(...) void -#define STDEXEC_TAG_INVOKE_ARGS(...) \ - __VA_OPT__(,) STDEXEC_CAT(STDEXEC_EAT_THIS_, __VA_ARGS__)) +#define STDEXEC_MEMFN_DECL_TAG_0(...) ::stdexec::__tag_type_t& +#define STDEXEC_MEMFN_DECL_TAG_1(...) STDEXEC_CAT(STDEXEC_CAT(STDEXEC_EAT_AUTO_, __VA_ARGS__), _t)& +#define STDEXEC_MEMFN_DECL_TAG_2(...) STDEXEC_CAT(STDEXEC_CAT(STDEXEC_EAT_VOID_, __VA_ARGS__), _t)& #if STDEXEC_MSVC() # pragma deprecated(STDEXEC_CUSTOM) diff --git a/include/stdexec/__detail/__meta.hpp b/include/stdexec/__detail/__meta.hpp index dde607e11..0aae7670d 100644 --- a/include/stdexec/__detail/__meta.hpp +++ b/include/stdexec/__detail/__meta.hpp @@ -758,8 +758,17 @@ namespace stdexec { using __call_result_t = decltype(__declval<_Fun>()(__declval<_As>()...)); #endif +// BUGBUG TODO file this bug with nvc++ +#if STDEXEC_NVHPC() template using __result_of = __call_result_t; +#else + template + using __result_of = decltype(_Fun(__declval<_As>()...)); +#endif + + template + inline constexpr bool __noexcept_of = noexcept(_Fun(__declval<_As>()...)); // For working around clang's lack of support for CWG#2369: // http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#2369 diff --git a/include/stdexec/execution.hpp b/include/stdexec/execution.hpp index f73937824..42831af8a 100644 --- a/include/stdexec/execution.hpp +++ b/include/stdexec/execution.hpp @@ -4725,7 +4725,7 @@ namespace stdexec { }; template - auto __mkenv(_Env&& __env, in_place_stop_source& __stop_source) noexcept { + auto __mkenv(_Env&& __env, const in_place_stop_source& __stop_source) noexcept { return __env::__join( __env::__with(__stop_source.get_token(), get_stop_token), static_cast<_Env&&>(__env)); }