On Thu, 28 Aug 2025 at 11:13, Tomasz Kamiński <[email protected]> wrote:
>
> This patch adds two new internal helpers for ordering types:
> * __cmp_cat::__ord to retrieve an internal _Ord value,
> * __cmp_cat::__make<Ordering> to create an ordering from an _Ord value.
>
> Conversions between ordering types are now handled by __cmp_cat::__make. As a
> result, ordering types no longer need to befriend each other, only the new
> helpers.
>
> The __fp_weak_ordering implementation has also been simplified by:
> * using the new helpers to convert partial_ordering to weak_ordering,
> * using strong_ordering to weak_ordering conversion operator,
> for the __isnan_sign comparison,
> * removing the unused __cat local variable.
>
> Finally, the _Ncmp enum is removed, and the unordered enumerator is added
> to the existing _Ord enum.
>
> libstdc++-v3/ChangeLog:
>
> * libsupc++/compare (__cmp_cat::_Ord): Add unordered enumerator.
> (__cmp_cat::_Ncmp): Remove.
> (__cmp_cat::__ord, __cmp_cat::__make): Define.
> (partial_ordering::partial_ordering(__cmp_cat::_Ncmp)): Remove.
> (operator<=>(__cmp_cat::__unspec, partial_ordering))
> (partial_ordering::unordered): Replace _Ncmp with _Ord.
> (std::partial_ordering, std::weak_ordering, std::strong_ordering):
> Befriend __ord and __make helpers, remove friend declartions for
> other orderings.
> (__compare::__fp_weak_ordering): Remove unused __cat variable.
> Simplify ordering conversions.
> ---
> I bundled the removal of _N_cmp and putting unordered in _Ord here,
> it is related, as new helpers now cleary can operate with one enumerator.
>
> Tested on x86_64-linux locally. Testing on powerpc64le.
> OK for trunk?
>
> libstdc++-v3/libsupc++/compare | 79 +++++++++++++++-------------------
> 1 file changed, 35 insertions(+), 44 deletions(-)
>
> diff --git a/libstdc++-v3/libsupc++/compare b/libstdc++-v3/libsupc++/compare
> index ef0f0376964..7722e205190 100644
> --- a/libstdc++-v3/libsupc++/compare
> +++ b/libstdc++-v3/libsupc++/compare
> @@ -54,9 +54,24 @@ namespace std _GLIBCXX_VISIBILITY(default)
> {
> using type = signed char;
>
> - enum class _Ord : type { equivalent = 0, less = -1, greater = 1 };
> + enum class _Ord : type
> + {
> + equivalent = 0, less = -1, greater = 1,
> + // value remains unchanged when negated
> + unordered = -__SCHAR_MAX__ - 1
> + };
>
> - enum class _Ncmp : type { unordered = -__SCHAR_MAX__ - 1 };
> + template<typename _Ordering>
> + [[__gnu__::__always_inline__]]
> + constexpr _Ord
> + __ord(_Ordering __o)
> + { return _Ord(__o._M_value); }
> +
> + template<typename _Ordering>
> + [[__gnu__::__always_inline__]]
> + constexpr _Ordering
> + __make(_Ord __o)
> + { return _Ordering(__o); }
I think we might as well add noexcept to these helper functions. The
constructors that use them are noexcept already, and it might mean
Clang doesn't add a try-catch to those constructors.
OK with that change.
>
> struct __unspec
> {
> @@ -74,22 +89,17 @@ namespace std _GLIBCXX_VISIBILITY(default)
> : _M_value(__cmp_cat::type(__v))
> { }
>
> - constexpr explicit
> - partial_ordering(__cmp_cat::_Ncmp __v) noexcept
> - : _M_value(__cmp_cat::type(__v))
> - { }
> -
> - friend class weak_ordering;
> - friend class strong_ordering;
> -
> [[__gnu__::__always_inline__]]
> constexpr __cmp_cat::type
> _M_reverse() const
> {
> - // leaves _Ncmp::unordered unchanged
> + // leaves _Ord::unordered unchanged
> return static_cast<__cmp_cat::type>(-_M_value);
> }
>
> + friend constexpr __cmp_cat::_Ord __cmp_cat::__ord<>(partial_ordering);
> + friend constexpr partial_ordering __cmp_cat::__make<>(__cmp_cat::_Ord);
> +
> public:
> // valid values
> static const partial_ordering less;
> @@ -155,7 +165,7 @@ namespace std _GLIBCXX_VISIBILITY(default)
> [[nodiscard]]
> friend constexpr partial_ordering
> operator<=>(__cmp_cat::__unspec, partial_ordering __v) noexcept
> - { return partial_ordering(__cmp_cat::_Ncmp(__v._M_reverse())); }
> + { return partial_ordering(__cmp_cat::_Ord(__v._M_reverse())); }
> };
>
> // valid values' definitions
> @@ -169,7 +179,7 @@ namespace std _GLIBCXX_VISIBILITY(default)
> partial_ordering::greater(__cmp_cat::_Ord::greater);
>
> inline constexpr partial_ordering
> - partial_ordering::unordered(__cmp_cat::_Ncmp::unordered);
> + partial_ordering::unordered(__cmp_cat::_Ord::unordered);
>
> class weak_ordering
> {
> @@ -179,7 +189,8 @@ namespace std _GLIBCXX_VISIBILITY(default)
> weak_ordering(__cmp_cat::_Ord __v) noexcept :
> _M_value(__cmp_cat::type(__v))
> { }
>
> - friend class strong_ordering;
> + friend constexpr __cmp_cat::_Ord __cmp_cat::__ord<>(weak_ordering);
> + friend constexpr weak_ordering __cmp_cat::__make<>(__cmp_cat::_Ord);
>
> public:
> // valid values
> @@ -189,7 +200,7 @@ namespace std _GLIBCXX_VISIBILITY(default)
>
> [[nodiscard]]
> constexpr operator partial_ordering() const noexcept
> - { return partial_ordering(__cmp_cat::_Ord(_M_value)); }
> + { return __cmp_cat::__make<partial_ordering>(__cmp_cat::_Ord(_M_value));
> }
>
> // comparisons
> [[nodiscard]]
> @@ -271,6 +282,9 @@ namespace std _GLIBCXX_VISIBILITY(default)
> : _M_value(__cmp_cat::type(__v))
> { }
>
> + friend constexpr __cmp_cat::_Ord __cmp_cat::__ord<>(strong_ordering);
> + friend constexpr strong_ordering __cmp_cat::__make<>(__cmp_cat::_Ord);
> +
> public:
> // valid values
> static const strong_ordering less;
> @@ -280,11 +294,11 @@ namespace std _GLIBCXX_VISIBILITY(default)
>
> [[nodiscard]]
> constexpr operator partial_ordering() const noexcept
> - { return partial_ordering(__cmp_cat::_Ord(_M_value)); }
> + { return __cmp_cat::__make<partial_ordering>(__cmp_cat::_Ord(_M_value));
> }
>
> [[nodiscard]]
> constexpr operator weak_ordering() const noexcept
> - { return weak_ordering(__cmp_cat::_Ord(_M_value)); }
> + { return __cmp_cat::__make<weak_ordering>(__cmp_cat::_Ord(_M_value)); }
>
> // comparisons
> [[nodiscard]]
> @@ -584,26 +598,9 @@ namespace std _GLIBCXX_VISIBILITY(default)
> constexpr weak_ordering
> __fp_weak_ordering(_Tp __e, _Tp __f)
> {
> - // Returns an integer with the same sign as the argument, and
> magnitude
> - // indicating the classification: zero=1 subnorm=2 norm=3 inf=4 nan=5
> - auto __cat = [](_Tp __fp) -> int {
> - const int __sign = __builtin_signbit(__fp) ? -1 : 1;
> - if (__builtin_isnormal(__fp))
> - return (__fp == 0 ? 1 : 3) * __sign;
> - if (__builtin_isnan(__fp))
> - return 5 * __sign;
> - if (int __inf = __builtin_isinf_sign(__fp))
> - return 4 * __inf;
> - return 2 * __sign;
> - };
> -
> - auto __po = __e <=> __f;
> - if (is_lt(__po))
> - return weak_ordering::less;
> - else if (is_gt(__po))
> - return weak_ordering::greater;
> - else if (__po == partial_ordering::equivalent)
> - return weak_ordering::equivalent;
> + auto __po = __cmp_cat::__ord(__e <=> __f);
> + if (__po != __cmp_cat::_Ord::unordered)
> + return __cmp_cat::__make<weak_ordering>(__po);
> else // unordered, at least one argument is NaN
> {
> // return -1 for negative nan, +1 for positive nan, 0 otherwise.
> @@ -612,13 +609,7 @@ namespace std _GLIBCXX_VISIBILITY(default)
> ? __builtin_signbit(__fp) ? -1 : 1
> : 0;
> };
> - auto __ord = __isnan_sign(__e) <=> __isnan_sign(__f);
> - if (is_eq(__ord))
> - return weak_ordering::equivalent;
> - else if (is_lt(__ord))
> - return weak_ordering::less;
> - else
> - return weak_ordering::greater;
> + return __isnan_sign(__e) <=> __isnan_sign(__f);
> }
> }
>
> --
> 2.50.1
>