diff --git a/libs/core/functional/include/hpx/functional/tag_fallback_invoke.hpp b/libs/core/functional/include/hpx/functional/tag_fallback_invoke.hpp index 5e3d8bd3be23..e5c950df738d 100644 --- a/libs/core/functional/include/hpx/functional/tag_fallback_invoke.hpp +++ b/libs/core/functional/include/hpx/functional/tag_fallback_invoke.hpp @@ -116,7 +116,7 @@ namespace hpx { namespace functional { /////////////////////////////////////////////////////////////////////////// namespace tag_fallback_invoke_t_ns { - // MSVC needs this, don't ask + // poison pill void tag_fallback_invoke(); struct tag_fallback_invoke_t @@ -146,7 +146,7 @@ namespace hpx { namespace functional { }; } // namespace tag_fallback_invoke_t_ns - inline namespace tag_fallback_invoke_ns { + namespace tag_fallback_invoke_ns { #if !defined(HPX_COMPUTE_DEVICE_CODE) HPX_INLINE_CONSTEXPR_VARIABLE tag_fallback_invoke_t_ns::tag_fallback_invoke_t tag_fallback_invoke = @@ -160,7 +160,8 @@ namespace hpx { namespace functional { /////////////////////////////////////////////////////////////////////////// template using is_tag_fallback_invocable = - hpx::is_invocable; + hpx::is_invocable; template constexpr bool is_tag_fallback_invocable_v = @@ -178,9 +179,10 @@ namespace hpx { namespace functional { template struct is_nothrow_tag_fallback_invocable_impl< - decltype(hpx::functional::tag_fallback_invoke)(Tag, Args...), true> + decltype(tag_fallback_invoke_ns::tag_fallback_invoke)(Tag, Args...), + true> : std::integral_constant(), std::declval()...))> { }; @@ -189,7 +191,7 @@ namespace hpx { namespace functional { template struct is_nothrow_tag_fallback_invocable : detail::is_nothrow_tag_fallback_invocable_impl< - decltype(hpx::functional::tag_fallback_invoke)(Tag, Args...), + decltype(tag_fallback_invoke_ns::tag_fallback_invoke)(Tag, Args...), is_tag_fallback_invocable_v> { }; @@ -199,119 +201,152 @@ namespace hpx { namespace functional { is_nothrow_tag_fallback_invocable::value; template - using tag_fallback_invoke_result = - hpx::util::invoke_result; + using tag_fallback_invoke_result = hpx::util::invoke_result< + decltype(tag_fallback_invoke_ns::tag_fallback_invoke), Tag, Args...>; template using tag_fallback_invoke_result_t = typename tag_fallback_invoke_result::type; - /////////////////////////////////////////////////////////////////////////// - /// Helper base class implementing the tag_invoke logic for CPOs that fall - /// back to directly invoke its fallback. - /// - /// This base class is in many cases preferable to the plain tag base class. - /// With the normal tag base class a default, unconstrained, default - /// tag_invoke overload will take precedence over user-defined tag_invoke - /// overloads that are not perfect matches. For example, with a default - /// overload: - /// - /// template auto tag_invoke(tag_t, T&& t) {...} - /// - /// and a user-defined overload in another namespace: - /// - /// auto tag_invoke(my_type t) - /// - /// the user-defined overload will only be considered when it is an exact - /// match. This means const and reference qualifiers must match exactly, and - /// conversions to a base class are not considered. - /// - /// With tag_fallback one can define the default implementation in terms of - /// a tag_fallback_invoke overload instead of tag_invoke: - /// - /// template auto tag_fallback_invoke(tag_t, T&& t) {...} - /// - /// With the same user-defined tag_invoke overload, the user-defined - /// overload will now be used if it is a match even if it isn't an exact - /// match. - /// This is because tag_fallback will dispatch to tag_fallback_invoke only - /// if there are no matching tag_invoke overloads. - template - struct tag_fallback - { - // is tag-invocable - template >::type> - HPX_HOST_DEVICE HPX_FORCEINLINE constexpr auto operator()( - Args&&... args) const - noexcept(is_nothrow_tag_invocable_v) - -> tag_invoke_result_t - { - return hpx::functional::tag_invoke( - static_cast(*this), std::forward(args)...); - } - - // is not tag-invocable - template >::type> - HPX_HOST_DEVICE HPX_FORCEINLINE constexpr auto operator()( - Args&&... args) const - noexcept(is_nothrow_tag_fallback_invocable_v) - -> tag_fallback_invoke_result_t - { - return hpx::functional::tag_fallback_invoke( - static_cast(*this), std::forward(args)...); - } - }; + /////////////////////////////////////////////////////////////////////////////// + namespace tag_fallback_ns { - /////////////////////////////////////////////////////////////////////////// - // helper base class implementing the tag_invoke logic for CPOs that fall - // back to directly invoke its fallback. Either invocation has to be noexcept. - template - struct tag_fallback_noexcept - { - private: - // is nothrow tag-fallback invocable - template - HPX_HOST_DEVICE HPX_FORCEINLINE constexpr auto tag_fallback_invoke_impl( - std::true_type, Args&&... args) const noexcept - -> tag_fallback_invoke_result_t - { - return hpx::functional::tag_fallback_invoke( - static_cast(*this), std::forward(args)...); - } - - public: - // is nothrow tag-invocable - template >::type> - HPX_HOST_DEVICE HPX_FORCEINLINE constexpr auto operator()( - Args&&... args) const noexcept - -> tag_invoke_result_t + template + struct not_tag_fallback_noexcept_invocable; + + // poison pill + void tag_fallback_invoke(); + + /////////////////////////////////////////////////////////////////////////// + /// Helper base class implementing the tag_invoke logic for CPOs that fall + /// back to directly invoke its fallback. + /// + /// This base class is in many cases preferable to the plain tag base class. + /// With the normal tag base class a default, unconstrained, default + /// tag_invoke overload will take precedence over user-defined tag_invoke + /// overloads that are not perfect matches. For example, with a default + /// overload: + /// + /// template auto tag_invoke(tag_t, T&& t) {...} + /// + /// and a user-defined overload in another namespace: + /// + /// auto tag_invoke(my_type t) + /// + /// the user-defined overload will only be considered when it is an exact + /// match. This means const and reference qualifiers must match exactly, and + /// conversions to a base class are not considered. + /// + /// With tag_fallback one can define the default implementation in terms of + /// a tag_fallback_invoke overload instead of tag_invoke: + /// + /// template auto tag_fallback_invoke(tag_t, T&& t) {...} + /// + /// With the same user-defined tag_invoke overload, the user-defined + /// overload will now be used if it is a match even if it isn't an exact + /// match. + /// This is because tag_fallback will dispatch to tag_fallback_invoke only + /// if there are no matching tag_invoke overloads. + template + struct tag_fallback { - return hpx::functional::tag_invoke( - static_cast(*this), std::forward(args)...); - } - - // is not nothrow tag-invocable - template , - typename Enable = typename std::enable_if< - !is_nothrow_tag_invocable_v>::type> - HPX_HOST_DEVICE HPX_FORCEINLINE constexpr auto operator()( - Args&&... args) const noexcept - -> decltype(tag_fallback_invoke_impl( - IsFallbackInvocable{}, std::forward(args)...)) + // is tag-invocable + template >::type> + HPX_HOST_DEVICE HPX_FORCEINLINE constexpr auto operator()( + Args&&... args) const + noexcept(is_nothrow_tag_invocable_v) + -> tag_invoke_result_t + { + return tag_invoke(static_cast(*this), + std::forward(args)...); + } + + // is not tag-invocable + template >::type> + HPX_HOST_DEVICE HPX_FORCEINLINE constexpr auto operator()( + Args&&... args) const + noexcept(is_nothrow_tag_fallback_invocable_v) + -> tag_fallback_invoke_result_t + { + return tag_fallback_invoke(static_cast(*this), + std::forward(args)...); + } + }; + + /////////////////////////////////////////////////////////////////////////// + // helper base class implementing the tag_invoke logic for CPOs that fall + // back to directly invoke its fallback. Either invocation has to be noexcept. + template + struct tag_fallback_noexcept { - return tag_fallback_invoke_impl( - IsFallbackInvocable{}, std::forward(args)...); - } - }; -}} // namespace hpx::functional + private: + // is nothrow tag-fallback invocable + template + HPX_HOST_DEVICE constexpr auto tag_fallback_invoke_impl( + std::false_type, Args&&... args) const noexcept + -> not_tag_fallback_noexcept_invocable + { + return not_tag_fallback_noexcept_invocable{}; + } + + template + HPX_HOST_DEVICE HPX_FORCEINLINE constexpr auto + tag_fallback_invoke_impl( + std::true_type, Args&&... args) const noexcept + -> tag_fallback_invoke_result_t + { + return tag_fallback_invoke(static_cast(*this), + std::forward(args)...); + } + + public: + // is nothrow tag-invocable + template >::type> + HPX_HOST_DEVICE HPX_FORCEINLINE constexpr auto operator()( + Args&&... args) const noexcept + -> tag_invoke_result_t + { + return tag_invoke(static_cast(*this), + std::forward(args)...); + } + + // is not nothrow tag-invocable + template , + typename Enable = typename std::enable_if< + !is_nothrow_tag_invocable_v>::type> + HPX_HOST_DEVICE HPX_FORCEINLINE constexpr auto operator()( + Args&&... args) const noexcept + -> decltype(tag_fallback_invoke_impl( + IsFallbackInvocable{}, std::forward(args)...)) + { + return tag_fallback_invoke_impl( + IsFallbackInvocable{}, std::forward(args)...); + } + }; + } // namespace tag_fallback_ns + + inline namespace tag_invoke_base_ns { + + template + using tag_fallback = tag_fallback_ns::tag_fallback; + + template + using tag_fallback_noexcept = + tag_fallback_ns::tag_fallback_noexcept; + } // namespace tag_invoke_base_ns + + inline namespace tag_fallback_invoke_f_ns { + + using tag_fallback_invoke_ns::tag_fallback_invoke; + } // namespace tag_fallback_invoke_f_ns +}} // namespace hpx::functional #endif diff --git a/libs/core/functional/include/hpx/functional/tag_invoke.hpp b/libs/core/functional/include/hpx/functional/tag_invoke.hpp index 4567760e9e0d..29b7d9d6272b 100644 --- a/libs/core/functional/include/hpx/functional/tag_invoke.hpp +++ b/libs/core/functional/include/hpx/functional/tag_invoke.hpp @@ -114,7 +114,7 @@ namespace hpx { namespace functional { namespace tag_invoke_t_ns { - // MSVC needs this, don't ask + // poison pill void tag_invoke(); struct tag_invoke_t @@ -142,7 +142,7 @@ namespace hpx { namespace functional { }; } // namespace tag_invoke_t_ns - inline namespace tag_invoke_ns { + namespace tag_invoke_ns { #if !defined(HPX_COMPUTE_DEVICE_CODE) HPX_INLINE_CONSTEXPR_VARIABLE tag_invoke_t_ns::tag_invoke_t tag_invoke = {}; @@ -154,7 +154,7 @@ namespace hpx { namespace functional { /////////////////////////////////////////////////////////////////////////// template using is_tag_invocable = - hpx::is_invocable; + hpx::is_invocable; template constexpr bool is_tag_invocable_v = is_tag_invocable::value; @@ -170,9 +170,9 @@ namespace hpx { namespace functional { template struct is_nothrow_tag_invocable_impl< - decltype(hpx::functional::tag_invoke)(Tag, Args...), true> + decltype(tag_invoke_ns::tag_invoke)(Tag, Args...), true> : std::integral_constant(), std::declval()...))> { }; @@ -181,7 +181,7 @@ namespace hpx { namespace functional { template struct is_nothrow_tag_invocable : detail::is_nothrow_tag_invocable_impl< - decltype(hpx::functional::tag_invoke)(Tag, Args...), + decltype(tag_invoke_ns::tag_invoke)(Tag, Args...), is_tag_invocable_v> { }; @@ -192,42 +192,63 @@ namespace hpx { namespace functional { template using tag_invoke_result = - hpx::util::invoke_result; template using tag_invoke_result_t = typename tag_invoke_result::type; - /////////////////////////////////////////////////////////////////////////// - // helper base class implementing the tag_invoke logic for CPOs - template - struct tag - { - template - HPX_HOST_DEVICE HPX_FORCEINLINE constexpr auto operator()( - Args&&... args) const - noexcept(is_nothrow_tag_invocable_v) - -> tag_invoke_result_t + /////////////////////////////////////////////////////////////////////////////// + namespace tag_ns { + + // poison pill + void tag_invoke(); + + /////////////////////////////////////////////////////////////////////////// + // helper base class implementing the tag_invoke logic for CPOs + template + struct tag { - return hpx::functional::tag_invoke( - static_cast(*this), std::forward(args)...); - } - }; + template + HPX_HOST_DEVICE HPX_FORCEINLINE constexpr auto operator()( + Args&&... args) const + noexcept(is_nothrow_tag_invocable_v) + -> tag_invoke_result_t + { + return tag_invoke(static_cast(*this), + std::forward(args)...); + } + }; - template - struct tag_noexcept - { - template >::type> - HPX_HOST_DEVICE HPX_FORCEINLINE constexpr auto operator()( - Args&&... args) const noexcept - -> tag_invoke_result_t + template + struct tag_noexcept { - return hpx::functional::tag_invoke( - static_cast(*this), std::forward(args)...); - } - }; -}} // namespace hpx::functional + template >::type> + HPX_HOST_DEVICE HPX_FORCEINLINE constexpr auto operator()( + Args&&... args) const noexcept + -> tag_invoke_result_t + { + return tag_invoke(static_cast(*this), + std::forward(args)...); + } + }; + } // namespace detail + + inline namespace tag_invoke_base_ns { + + template + using tag = tag_ns::tag; + + template + using tag_noexcept = tag_ns::tag_noexcept; + } // namespace tag_invoke_base_ns + + inline namespace tag_invoke_f_ns { + + using tag_invoke_ns::tag_invoke; + } // namespace tag_invoke_f_ns +}} // namespace hpx::functional #endif diff --git a/libs/core/functional/include/hpx/functional/tag_priority_invoke.hpp b/libs/core/functional/include/hpx/functional/tag_priority_invoke.hpp index 33297ee9be4e..6e9f7ce60c16 100644 --- a/libs/core/functional/include/hpx/functional/tag_priority_invoke.hpp +++ b/libs/core/functional/include/hpx/functional/tag_priority_invoke.hpp @@ -118,7 +118,7 @@ namespace hpx { namespace functional { /////////////////////////////////////////////////////////////////////////// namespace tag_override_invoke_t_ns { - // MSVC needs this, don't ask + // poison pill void tag_override_invoke(); struct tag_override_invoke_t @@ -148,7 +148,7 @@ namespace hpx { namespace functional { }; } // namespace tag_override_invoke_t_ns - inline namespace tag_override_invoke_ns { + namespace tag_override_invoke_ns { #if !defined(HPX_COMPUTE_DEVICE_CODE) HPX_INLINE_CONSTEXPR_VARIABLE tag_override_invoke_t_ns::tag_override_invoke_t tag_override_invoke = @@ -162,7 +162,8 @@ namespace hpx { namespace functional { /////////////////////////////////////////////////////////////////////////// template using is_tag_override_invocable = - hpx::is_invocable; + hpx::is_invocable; template constexpr bool is_tag_override_invocable_v = @@ -180,9 +181,10 @@ namespace hpx { namespace functional { template struct is_nothrow_tag_override_invocable_impl< - decltype(hpx::functional::tag_override_invoke)(Tag, Args...), true> + decltype(tag_override_invoke_ns::tag_override_invoke)(Tag, Args...), + true> : std::integral_constant(), std::declval()...))> { }; @@ -191,7 +193,7 @@ namespace hpx { namespace functional { template struct is_nothrow_tag_override_invocable : detail::is_nothrow_tag_override_invocable_impl< - decltype(hpx::functional::tag_override_invoke)(Tag, Args...), + decltype(tag_override_invoke_ns::tag_override_invoke)(Tag, Args...), is_tag_override_invocable_v> { }; @@ -201,122 +203,143 @@ namespace hpx { namespace functional { is_nothrow_tag_override_invocable::value; template - using tag_override_invoke_result = - hpx::util::invoke_result; + using tag_override_invoke_result = hpx::util::invoke_result< + decltype(tag_override_invoke_ns::tag_override_invoke), Tag, Args...>; template using tag_override_invoke_result_t = typename tag_override_invoke_result::type; - /////////////////////////////////////////////////////////////////////////// - /// Helper base class implementing the tag_invoke logic for CPOs that allow - /// overriding user-defined tag_invoke overloads with tag_override_invoke, - /// and that allow setting a fallback with tag_fallback_invoke. - /// - /// This helper class is otherwise identical to tag_fallback, but allows - /// defining an implementation that will always take priority if it is - /// feasible. This is useful for example in cases where a member function - /// should always take priority over any free function tag_invoke overloads, - /// when available, like this: - /// - /// template - /// auto tag_override_invoke(T&& t) -> decltype(t.foo()){ return t.foo(); } - template - struct tag_priority - { - // Is tag-override-invocable - template >::type> - HPX_HOST_DEVICE HPX_FORCEINLINE constexpr auto operator()( - Args&&... args) const - noexcept(is_nothrow_tag_override_invocable_v) - -> tag_override_invoke_result_t + /////////////////////////////////////////////////////////////////////////////// + namespace tag_priority_ns { + + // poison pill + void tag_override_invoke(); + + /////////////////////////////////////////////////////////////////////////// + /// Helper base class implementing the tag_invoke logic for CPOs that allow + /// overriding user-defined tag_invoke overloads with tag_override_invoke, + /// and that allow setting a fallback with tag_fallback_invoke. + /// + /// This helper class is otherwise identical to tag_fallback, but allows + /// defining an implementation that will always take priority if it is + /// feasible. This is useful for example in cases where a member function + /// should always take priority over any free function tag_invoke overloads, + /// when available, like this: + /// + /// template + /// auto tag_override_invoke(T&& t) -> decltype(t.foo()){ return t.foo(); } + template + struct tag_priority { - return hpx::functional::tag_override_invoke( - static_cast(*this), std::forward(args)...); - } - - // Is not tag-override-invocable, but tag-invocable - template && - is_tag_invocable_v>::type> - HPX_HOST_DEVICE HPX_FORCEINLINE constexpr auto operator()( - Args&&... args) const - noexcept(is_nothrow_tag_invocable_v) - -> tag_invoke_result_t + // Is tag-override-invocable + template >::type> + HPX_HOST_DEVICE HPX_FORCEINLINE constexpr auto operator()( + Args&&... args) const + noexcept(is_nothrow_tag_override_invocable_v) + -> tag_override_invoke_result_t + { + return tag_override_invoke(static_cast(*this), + std::forward(args)...); + } + + // Is not tag-override-invocable, but tag-invocable + template && + is_tag_invocable_v>::type> + HPX_HOST_DEVICE HPX_FORCEINLINE constexpr auto operator()( + Args&&... args) const + noexcept(is_nothrow_tag_invocable_v) + -> tag_invoke_result_t + { + return tag_invoke(static_cast(*this), + std::forward(args)...); + } + + // Is not tag-override-invocable, not tag-invocable, but + // tag-fallback-invocable + template && + !is_tag_invocable_v && + is_tag_fallback_invocable_v>::type> + HPX_HOST_DEVICE HPX_FORCEINLINE constexpr auto operator()( + Args&&... args) const + noexcept(is_nothrow_tag_fallback_invocable_v) + -> tag_fallback_invoke_result_t + { + return tag_fallback_invoke(static_cast(*this), + std::forward(args)...); + } + }; + + /////////////////////////////////////////////////////////////////////////// + // Helper base class implementing the tag_invoke logic for noexcept CPOs + // that allow overriding user-defined tag_invoke overloads with + // tag_override_invoke, and that allow setting a fallback with + // tag_fallback_invoke. + template + struct tag_priority_noexcept { - return hpx::functional::tag_invoke( - static_cast(*this), std::forward(args)...); - } - - // Is not tag-override-invocable, not tag-invocable, but - // tag-fallback-invocable - template && - !is_tag_invocable_v && - is_tag_fallback_invocable_v>::type> - HPX_HOST_DEVICE HPX_FORCEINLINE constexpr auto operator()( - Args&&... args) const - noexcept(is_nothrow_tag_fallback_invocable_v) + // Is nothrow tag-override-invocable + template >::type> + HPX_HOST_DEVICE HPX_FORCEINLINE constexpr auto operator()( + Args&&... args) const noexcept + -> tag_override_invoke_result_t + { + return tag_override_invoke(static_cast(*this), + std::forward(args)...); + } + + // Is not nothrow tag-override-invocable, but nothrow tag-invocable + template && + is_nothrow_tag_invocable_v>::type> + HPX_HOST_DEVICE HPX_FORCEINLINE constexpr auto operator()( + Args&&... args) const noexcept + -> tag_invoke_result_t + { + return tag_invoke(static_cast(*this), + std::forward(args)...); + } + + // Is not nothrow tag-override-invocable, not nothrow tag-invocable, but + // nothrow tag-fallback-invocable + template && + !is_nothrow_tag_invocable_v && + is_nothrow_tag_fallback_invocable_v>::type> + HPX_HOST_DEVICE HPX_FORCEINLINE constexpr auto operator()( + Args&&... args) const noexcept -> tag_fallback_invoke_result_t - { - return hpx::functional::tag_fallback_invoke( - static_cast(*this), std::forward(args)...); - } - }; + { + return tag_fallback_invoke(static_cast(*this), + std::forward(args)...); + } + }; + } // namespace tag_priority_ns - /////////////////////////////////////////////////////////////////////////// - // Helper base class implementing the tag_invoke logic for noexcept CPOs - // that allow overriding user-defined tag_invoke overloads with - // tag_override_invoke, and that allow setting a fallback with - // tag_fallback_invoke. - template - struct tag_priority_noexcept - { - // Is nothrow tag-override-invocable - template >::type> - HPX_HOST_DEVICE HPX_FORCEINLINE constexpr auto operator()( - Args&&... args) const noexcept - -> tag_override_invoke_result_t - { - return hpx::functional::tag_override_invoke( - static_cast(*this), std::forward(args)...); - } - - // Is not nothrow tag-override-invocable, but nothrow tag-invocable - template && - is_nothrow_tag_invocable_v>::type> - HPX_HOST_DEVICE HPX_FORCEINLINE constexpr auto operator()( - Args&&... args) const noexcept - -> tag_invoke_result_t - { - return hpx::functional::tag_invoke( - static_cast(*this), std::forward(args)...); - } - - // Is not nothrow tag-override-invocable, not nothrow tag-invocable, but - // nothrow tag-fallback-invocable - template && - !is_nothrow_tag_invocable_v && - is_nothrow_tag_fallback_invocable_v>::type> - HPX_HOST_DEVICE HPX_FORCEINLINE constexpr auto operator()( - Args&&... args) const noexcept - -> tag_fallback_invoke_result_t - { - return hpx::functional::tag_fallback_invoke( - static_cast(*this), std::forward(args)...); - } - }; -}} // namespace hpx::functional + inline namespace tag_invoke_base_ns { + + template + using tag_priority = tag_priority_ns::tag_priority; + + template + using tag_priority_noexcept = + tag_priority_ns::tag_priority_noexcept; + } // namespace tag_invoke_base_ns + + inline namespace tag_override_invoke_f_ns { + + using tag_override_invoke_ns::tag_override_invoke; + } // namespace tag_override_invoke_f_ns +}} // namespace hpx::functional #endif