From c39fff0da59ac23a4b185aada4ee653f69a705d5 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Thu, 30 Nov 2023 01:38:39 +0100 Subject: [PATCH] Add missing <=> 0 operator to Qt ordering types It's required by the standard, see e.g. [1] and the eel.is links in the code. [ChangeLog][QtCore][QPartialOrdering] Added three-way comparison operator (<=>) against literal zero, available when compiling in C++20 mode. [1] https://en.cppreference.com/w/cpp/utility/compare/partial_ordering#Comparisons Change-Id: I8a3b76661400930c6e247cf5b138ff52bf784395 Reviewed-by: Qt CI Bot Reviewed-by: Ivan Solovev --- src/corelib/global/qcompare.h | 57 ++++++++++++++++ src/corelib/global/qcomparehelpers.h | 5 ++ .../corelib/global/qcompare/tst_qcompare.cpp | 68 +++++++++++++++++++ 3 files changed, 130 insertions(+) diff --git a/src/corelib/global/qcompare.h b/src/corelib/global/qcompare.h index 35123dcb36f..9e67bdaa94e 100644 --- a/src/corelib/global/qcompare.h +++ b/src/corelib/global/qcompare.h @@ -47,6 +47,19 @@ enum class Uncomparable : CompareUnderlyingType } // namespace QtPrivate +namespace QtOrderingPrivate { + +template +constexpr O reversed(O o) noexcept +{ + // https://eel.is/c++draft/cmp.partialord#5 + return is_lt(o) ? O::greater : + is_gt(o) ? O::less : + /*else*/ o ; +} + +} // namespace QtOrderingPrivate + namespace Qt { class partial_ordering @@ -107,6 +120,17 @@ public: { return rhs.isOrdered() && 0 >= rhs.m_order; } +#ifdef __cpp_lib_three_way_comparison + friend constexpr std::partial_ordering + operator<=>(partial_ordering lhs, QtPrivate::CompareAgainstLiteralZero) noexcept + { return lhs; } // https://eel.is/c++draft/cmp.partialord#4 + + friend constexpr std::partial_ordering + operator<=>(QtPrivate::CompareAgainstLiteralZero, partial_ordering rhs) noexcept + { return QtOrderingPrivate::reversed(rhs); } +#endif // __cpp_lib_three_way_comparison + + friend constexpr bool operator==(partial_ordering lhs, partial_ordering rhs) noexcept { return lhs.m_order == rhs.m_order; } @@ -253,6 +277,17 @@ public: { return 0 >= rhs.m_order; } +#ifdef __cpp_lib_three_way_comparison + friend constexpr std::weak_ordering + operator<=>(weak_ordering lhs, QtPrivate::CompareAgainstLiteralZero) noexcept + { return lhs; } // https://eel.is/c++draft/cmp.weakord#5 + + friend constexpr std::weak_ordering + operator<=>(QtPrivate::CompareAgainstLiteralZero, weak_ordering rhs) noexcept + { return QtOrderingPrivate::reversed(rhs); } +#endif // __cpp_lib_three_way_comparison + + friend constexpr bool operator==(weak_ordering lhs, weak_ordering rhs) noexcept { return lhs.m_order == rhs.m_order; } @@ -425,6 +460,17 @@ public: { return 0 >= rhs.m_order; } +#ifdef __cpp_lib_three_way_comparison + friend constexpr std::strong_ordering + operator<=>(strong_ordering lhs, QtPrivate::CompareAgainstLiteralZero) noexcept + { return lhs; } // https://eel.is/c++draft/cmp.strongord#6 + + friend constexpr std::strong_ordering + operator<=>(QtPrivate::CompareAgainstLiteralZero, strong_ordering rhs) noexcept + { return QtOrderingPrivate::reversed(rhs); } +#endif // __cpp_lib_three_way_comparison + + friend constexpr bool operator==(strong_ordering lhs, strong_ordering rhs) noexcept { return lhs.m_order == rhs.m_order; } @@ -699,6 +745,17 @@ public: { return rhs.isOrdered() && 0 >= rhs.m_order; } +#ifdef __cpp_lib_three_way_comparison + friend constexpr std::partial_ordering + operator<=>(QPartialOrdering lhs, QtPrivate::CompareAgainstLiteralZero) noexcept + { return lhs; } // https://eel.is/c++draft/cmp.partialord#4 + + friend constexpr std::partial_ordering + operator<=>(QtPrivate::CompareAgainstLiteralZero, QPartialOrdering rhs) noexcept + { return QtOrderingPrivate::reversed(rhs); } +#endif // __cpp_lib_three_way_comparison + + friend constexpr bool operator==(QPartialOrdering lhs, QPartialOrdering rhs) noexcept { return lhs.m_order == rhs.m_order; } diff --git a/src/corelib/global/qcomparehelpers.h b/src/corelib/global/qcomparehelpers.h index d7e007b8b0a..0394703e170 100644 --- a/src/corelib/global/qcomparehelpers.h +++ b/src/corelib/global/qcomparehelpers.h @@ -27,6 +27,8 @@ QT_BEGIN_NAMESPACE +class QPartialOrdering; + namespace QtOrderingPrivate { #ifdef __cpp_lib_three_way_comparison @@ -44,6 +46,9 @@ QT_STD_MAP(weak) QT_STD_MAP(strong) #undef QT_STD_MAP +template <> struct StdOrdering : q20::type_identity {}; +template <> struct QtOrdering : q20::type_identity< Qt::partial_ordering> {}; + template constexpr auto to_std(In in) noexcept -> typename QtOrderingPrivate::StdOrdering::type { return in; } diff --git a/tests/auto/corelib/global/qcompare/tst_qcompare.cpp b/tests/auto/corelib/global/qcompare/tst_qcompare.cpp index 8dc3cf5a37a..b9baad6a119 100644 --- a/tests/auto/corelib/global/qcompare/tst_qcompare.cpp +++ b/tests/auto/corelib/global/qcompare/tst_qcompare.cpp @@ -18,6 +18,7 @@ private slots: void partialOrdering(); void weakOrdering(); void strongOrdering(); + void threeWayCompareWithLiteralZero(); void conversions(); void is_eq_overloads(); void compareThreeWay(); @@ -500,6 +501,73 @@ void tst_QCompare::strongOrdering() static_assert(!(0 >= Qt::strong_ordering::greater)); } +void tst_QCompare::threeWayCompareWithLiteralZero() +{ +#ifndef __cpp_lib_three_way_comparison + QSKIP("This test requires C++20 <=> support enabled in the compiler and the stdlib."); +#else + // the result of <=> is _always_ a std::_ordering type: +#define CHECK(O) do { \ + using StdO = typename QtOrderingPrivate::StdOrdering::type; \ + static_assert(std::is_same_v std::declval()), StdO>); \ + static_assert(std::is_same_v() <=> 0), StdO>); \ + } while (false) + + CHECK(Qt::partial_ordering); + CHECK(Qt::weak_ordering); + CHECK(Qt::strong_ordering); + CHECK(QPartialOrdering); + // API symmetry check: + CHECK(std::partial_ordering); + CHECK(std::weak_ordering); + CHECK(std::strong_ordering); + +#undef CHECK + +#define CHECK(O, what, reversed) do { \ + using StdO = typename QtOrderingPrivate::StdOrdering::type; \ + static_assert((O :: what <=> 0) == StdO:: what); \ + static_assert((0 <=> O :: what) == StdO:: reversed); \ + } while (false) + + CHECK(Qt::partial_ordering, unordered, unordered); + CHECK(Qt::partial_ordering, equivalent, equivalent); + CHECK(Qt::partial_ordering, less, greater); + CHECK(Qt::partial_ordering, greater, less); + + CHECK(Qt::weak_ordering, equivalent, equivalent); + CHECK(Qt::weak_ordering, less, greater); + CHECK(Qt::weak_ordering, greater, less); + + CHECK(Qt::strong_ordering, equal, equal); + CHECK(Qt::strong_ordering, less, greater); + CHECK(Qt::strong_ordering, greater, less); + + CHECK(QPartialOrdering, unordered, unordered); + CHECK(QPartialOrdering, equivalent, equivalent); + CHECK(QPartialOrdering, less, greater); + CHECK(QPartialOrdering, greater, less); + + // API symmetry check: + + CHECK(std::partial_ordering, unordered, unordered); + CHECK(std::partial_ordering, equivalent, equivalent); + CHECK(std::partial_ordering, less, greater); + CHECK(std::partial_ordering, greater, less); + + CHECK(std::weak_ordering, equivalent, equivalent); + CHECK(std::weak_ordering, less, greater); + CHECK(std::weak_ordering, greater, less); + + CHECK(std::strong_ordering, equal, equal); + CHECK(std::strong_ordering, less, greater); + CHECK(std::strong_ordering, greater, less); + +#undef CHECK +#endif // __cpp_lib_three_way_comparisons + +} + void tst_QCompare::conversions() { // Qt::weak_ordering -> Qt::partial_ordering