summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIvan Solovev <ivan.solovev@qt.io>2023-05-17 15:01:58 +0200
committerIvan Solovev <ivan.solovev@qt.io>2023-11-28 21:30:33 +0100
commit96f494bf92d246b741b6ae6261c93ef557ef392b (patch)
treedf95a915535a9acc26166c45a21006a76f996192
parent5a28aacd850da6ba4a4e4cd3067f60d0f82dd8c0 (diff)
Implement helper Qt::compareThreeWay() function for built-in types
The helper function RetType compareThreeWay(const T &left, const T &right) noexcept; is used for C++20-comparison macros. Normally it's the user's responsibility to provide this function as a hidden friend of the class which uses the comparison helper macros. For built-in types we provide the implementation inside the Qt namespace. We have to use custom IsIntegralType trait because libstdc++ only treats __{u}int128_t types as integral when compiling in -std=gnu++XX mode, and we compile Qt in -std=c++XX mode. This patch provides the implementations only for compareThreeWay() overloads, because there is no need to implement comparesEqual() for built-in types. It would just be equivalent to calling operator==(), so the user can do it directly. Task-number: QTBUG-104113 Change-Id: I7b3f395458e1ee4c64f442ad48bbf4fec4c19c52 Reviewed-by: Marc Mutz <marc.mutz@qt.io>
-rw-r--r--src/corelib/global/qcompare.cpp151
-rw-r--r--src/corelib/global/qcomparehelpers.h132
-rw-r--r--tests/auto/corelib/global/qcomparehelpers/CMakeLists.txt22
-rw-r--r--tests/auto/corelib/global/qcomparehelpers/tst_qcomparehelpers.cpp270
4 files changed, 548 insertions, 27 deletions
diff --git a/src/corelib/global/qcompare.cpp b/src/corelib/global/qcompare.cpp
index 9e4d958d16..9d1273c4e6 100644
--- a/src/corelib/global/qcompare.cpp
+++ b/src/corelib/global/qcompare.cpp
@@ -1098,4 +1098,155 @@ CHECK(strong, equivalent);
Q_DECLARE_EQUALITY_COMPARABLE
*/
+/*!
+ \fn template <typename LeftInt, typename RightInt> Qt::compareThreeWay(LeftInt lhs, RightInt rhs)
+ \since 6.7
+ \relates <QtCompare>
+ \overload
+
+ Implements three-way comparison of integral types.
+
+ \note This function participates in overload resolution only if both
+ \c LeftInt and \c RightInt are built-in integral types.
+
+ Returns \c {lhs <=> rhs}, provided \c LeftInt and \c RightInt are built-in
+ integral types. Unlike \c {operator<=>()}, this function template is also
+ available in C++17. See
+ \l {https://en.cppreference.com/w/cpp/language/operator_comparison#Three-way_comparison}
+ {cppreference} for more details.
+
+ This function can also be used in custom \c {compareThreeWay()} functions,
+ when ordering members of a custom class represented by built-in types:
+
+ \code
+ class MyClass {
+ public:
+ ...
+ private:
+ int value;
+ ...
+ friend Qt::strong_ordering
+ compareThreeWay(const MyClass &lhs, const MyClass &rhs) noexcept
+ { return Qt::compareThreeWay(lhs.value, rhs.value); }
+ Q_DECLARE_STRONGLY_ORDERED(MyClass)
+ };
+ \endcode
+
+ Returns an instance of \l Qt::strong_ordering that represents the relation
+ between \a lhs and \a rhs.
+*/
+
+/*!
+ \fn template <typename LeftFloat, typename RightFloat> Qt::compareThreeWay(LeftFloat lhs, RightFloat rhs)
+ \since 6.7
+ \relates <QtCompare>
+ \overload
+
+ Implements three-way comparison of floating point types.
+
+ \note This function participates in overload resolution only if both
+ \c LeftFloat and \c RightFloat are built-in floating-point types.
+
+ Returns \c {lhs <=> rhs}, provided \c LeftFloat and \c RightFloat are
+ built-in floating-point types. Unlike \c {operator<=>()}, this function
+ template is also available in C++17. See
+ \l {https://en.cppreference.com/w/cpp/language/operator_comparison#Three-way_comparison}
+ {cppreference} for more details.
+
+ This function can also be used in custom \c {compareThreeWay()} functions,
+ when ordering members of a custom class represented by built-in types:
+
+ \code
+ class MyClass {
+ public:
+ ...
+ private:
+ double value;
+ ...
+ friend Qt::partial_ordering
+ compareThreeWay(const MyClass &lhs, const MyClass &rhs) noexcept
+ { return Qt::compareThreeWay(lhs.value, rhs.value); }
+ Q_DECLARE_PARTIALLY_ORDERED(MyClass)
+ };
+ \endcode
+
+ Returns an instance of \l Qt::partial_ordering that represents the relation
+ between \a lhs and \a rhs. If \a lhs or \a rhs is not a number (NaN),
+ \l Qt::partial_ordering::unordered is returned.
+*/
+
+/*!
+ \fn template <typename IntType, typename FloatType> Qt::compareThreeWay(IntType lhs, FloatType rhs)
+ \since 6.7
+ \relates <QtCompare>
+ \overload
+
+ Implements three-way comparison of integral and floating point types.
+
+ \note This function participates in overload resolution only if \c IntType
+ is a built-in integral type and \c FloatType is a built-in floating-point
+ type.
+
+ This function converts \a lhs to \c FloatType and calls the overload for
+ floating-point types.
+
+ Returns an instance of \l Qt::partial_ordering that represents the relation
+ between \a lhs and \a rhs. If \a rhs is not a number (NaN),
+ \l Qt::partial_ordering::unordered is returned.
+*/
+
+/*!
+ \fn template <typename FloatType, typename IntType> Qt::compareThreeWay(FloatType lhs, IntType rhs)
+ \since 6.7
+ \relates <QtCompare>
+ \overload
+
+ Implements three-way comparison of floating point and integral types.
+
+ \note This function participates in overload resolution only if \c FloatType
+ is a built-in floating-point type and \c IntType is a built-in integral
+ type.
+
+ This function converts \a rhs to \c FloatType and calls the overload for
+ floating-point types.
+
+ Returns an instance of \l Qt::partial_ordering that represents the relation
+ between \a lhs and \a rhs. If \a lhs is not a number (NaN),
+ \l Qt::partial_ordering::unordered is returned.
+*/
+
+/*!
+ \fn template <typename LeftType, typename RightType> Qt::compareThreeWay(const LeftType *lhs, const RightType *rhs)
+ \since 6.7
+ \relates <QtCompare>
+ \overload
+
+ Implements three-way comparison of pointers.
+
+ \note This function participates in overload resolution if \c LeftType and
+ \c RightType are the same type, or base and derived types. It is also used
+ to compare any pointer to \c {std::nullptr_t}.
+
+ Returns an instance of \l Qt::strong_ordering that represents the relation
+ between \a lhs and \a rhs.
+*/
+
+/*!
+ \fn template <class Enum> Qt::compareThreeWay(Enum lhs, Enum rhs)
+ \since 6.7
+ \relates <QtCompare>
+ \overload
+
+ Implements three-way comparison of enum types.
+
+ \note This function participates in overload resolution only if \c Enum
+ is an enum type.
+
+ This function converts \c Enum to its underlying type and calls the
+ overload for integral types.
+
+ Returns an instance of \l Qt::strong_ordering that represents the relation
+ between \a lhs and \a rhs.
+*/
+
QT_END_NAMESPACE
diff --git a/src/corelib/global/qcomparehelpers.h b/src/corelib/global/qcomparehelpers.h
index f445e29b00..d0991e91b3 100644
--- a/src/corelib/global/qcomparehelpers.h
+++ b/src/corelib/global/qcomparehelpers.h
@@ -15,11 +15,15 @@
#endif
#include <QtCore/qoverload.h>
+#include <QtCore/qttypetraits.h>
+#include <QtCore/qtypes.h>
#ifdef __cpp_lib_three_way_comparison
#include <compare>
#endif
+#include <functional> // std::less
+
QT_BEGIN_NAMESPACE
/*
@@ -302,6 +306,134 @@ QT_BEGIN_NAMESPACE
#define Q_DECLARE_STRONGLY_ORDERED_LITERAL_TYPE(...) \
QT_OVERLOADED_MACRO(QT_DECLARE_STRONGLY_ORDERED_LITERAL_TYPE, __VA_ARGS__)
+namespace QtPrivate {
+
+template <typename T>
+constexpr bool IsIntegralType_v = std::numeric_limits<std::remove_const_t<T>>::is_specialized
+ && std::numeric_limits<std::remove_const_t<T>>::is_integer;
+
+} // namespace QtPrivate
+
+namespace Qt {
+
+template <typename T, typename U>
+using if_integral =
+ std::enable_if_t<QtPrivate::IsIntegralType_v<std::remove_reference_t<T>>
+ && QtPrivate::IsIntegralType_v<std::remove_reference_t<U>>,
+ bool>;
+
+template <typename T, typename U>
+using if_floating_point =
+ std::enable_if_t<std::conjunction_v<std::is_floating_point<std::remove_reference_t<T>>,
+ std::is_floating_point<std::remove_reference_t<U>>>,
+ bool>;
+
+template <typename T, typename U>
+using if_integral_and_floating_point =
+ std::enable_if_t<QtPrivate::IsIntegralType_v<std::remove_reference_t<T>>
+ && std::is_floating_point_v<std::remove_reference_t<U>>,
+ bool>;
+
+template <typename T, typename U>
+using if_compatible_pointers =
+ std::enable_if_t<std::disjunction_v<std::is_same<T, U>,
+ std::is_base_of<T, U>,
+ std::is_base_of<U, T>>,
+ bool>;
+
+template <typename Enum>
+using if_enum = std::enable_if_t<std::is_enum_v<Enum>, bool>;
+
+template <typename LeftInt, typename RightInt,
+ if_integral<LeftInt, RightInt> = true>
+constexpr Qt::strong_ordering compareThreeWay(LeftInt lhs, RightInt rhs) noexcept
+{
+ static_assert(std::is_signed_v<LeftInt> == std::is_signed_v<RightInt>,
+ "Qt::compareThreeWay() does not allow mixed-sign comparison.");
+
+#ifdef __cpp_lib_three_way_comparison
+ return lhs <=> rhs;
+#else
+ if (lhs == rhs)
+ return Qt::strong_ordering::equivalent;
+ else if (lhs < rhs)
+ return Qt::strong_ordering::less;
+ else
+ return Qt::strong_ordering::greater;
+#endif // __cpp_lib_three_way_comparison
+}
+
+template <typename LeftFloat, typename RightFloat,
+ if_floating_point<LeftFloat, RightFloat> = true>
+constexpr Qt::partial_ordering compareThreeWay(LeftFloat lhs, RightFloat rhs) noexcept
+{
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_FLOAT_COMPARE
+#ifdef __cpp_lib_three_way_comparison
+ return lhs <=> rhs;
+#else
+ if (lhs < rhs)
+ return Qt::partial_ordering::less;
+ else if (lhs > rhs)
+ return Qt::partial_ordering::greater;
+ else if (lhs == rhs)
+ return Qt::partial_ordering::equivalent;
+ else
+ return Qt::partial_ordering::unordered;
+#endif // __cpp_lib_three_way_comparison
+QT_WARNING_POP
+}
+
+template <typename IntType, typename FloatType,
+ if_integral_and_floating_point<IntType, FloatType> = true>
+constexpr Qt::partial_ordering compareThreeWay(IntType lhs, FloatType rhs) noexcept
+{
+ return compareThreeWay(FloatType(lhs), rhs);
+}
+
+template <typename FloatType, typename IntType,
+ if_integral_and_floating_point<IntType, FloatType> = true>
+constexpr Qt::partial_ordering compareThreeWay(FloatType lhs, IntType rhs) noexcept
+{
+ return compareThreeWay(lhs, FloatType(rhs));
+}
+
+template <typename LeftType, typename RightType,
+ if_compatible_pointers<LeftType, RightType> = true>
+constexpr Qt::strong_ordering compareThreeWay(const LeftType *lhs, const RightType *rhs) noexcept
+{
+#ifdef __cpp_lib_three_way_comparison
+ return std::compare_three_way{}(lhs, rhs);
+#else
+ if (lhs == rhs)
+ return Qt::strong_ordering::equivalent;
+ else if (std::less<>{}(lhs, rhs))
+ return Qt::strong_ordering::less;
+ else
+ return Qt::strong_ordering::greater;
+#endif // __cpp_lib_three_way_comparison
+}
+
+template <typename T>
+constexpr Qt::strong_ordering compareThreeWay(const T *lhs, std::nullptr_t rhs) noexcept
+{
+ return compareThreeWay(lhs, static_cast<const T *>(rhs));
+}
+
+template <typename T>
+constexpr Qt::strong_ordering compareThreeWay(std::nullptr_t lhs, const T *rhs) noexcept
+{
+ return compareThreeWay(static_cast<const T *>(lhs), rhs);
+}
+
+template <class Enum, if_enum<Enum> = true>
+constexpr Qt::strong_ordering compareThreeWay(Enum lhs, Enum rhs) noexcept
+{
+ return compareThreeWay(qToUnderlying(lhs), qToUnderlying(rhs));
+}
+
+} // namespace Qt
+
QT_END_NAMESPACE
#endif // QCOMPAREHELPERS_H
diff --git a/tests/auto/corelib/global/qcomparehelpers/CMakeLists.txt b/tests/auto/corelib/global/qcomparehelpers/CMakeLists.txt
index ab0620e8b1..4647bb5c7b 100644
--- a/tests/auto/corelib/global/qcomparehelpers/CMakeLists.txt
+++ b/tests/auto/corelib/global/qcomparehelpers/CMakeLists.txt
@@ -7,3 +7,25 @@ qt_internal_add_test(tst_qcomparehelpers
LIBRARIES
Qt::TestPrivate
)
+
+# CMake recognizes CXX_STANDARD=23 only starting from version 3.20
+# macOS has some issues with concepts, see QTBUG-117765
+if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.20" AND NOT MACOS)
+ qt_internal_add_test(tst_qcomparehelpers_cpp23
+ SOURCES
+ tst_qcomparehelpers.cpp
+ DEFINES
+ tst_QCompareHelpers=tst_QCompareHelpersCpp23
+ LIBRARIES
+ Qt::TestPrivate
+ )
+
+ # Try to build this test in C++23 mode to test std::float16_t support.
+ # Use CXX_STANDARD_REQUIRED OFF, so that we just fall back to C++17 if the
+ # compiler does not support C++23.
+ set_target_properties(tst_qcomparehelpers_cpp23
+ PROPERTIES
+ CXX_STANDARD 23
+ CXX_STANDARD_REQUIRED OFF
+ )
+endif()
diff --git a/tests/auto/corelib/global/qcomparehelpers/tst_qcomparehelpers.cpp b/tests/auto/corelib/global/qcomparehelpers/tst_qcomparehelpers.cpp
index e3810f8605..995418780a 100644
--- a/tests/auto/corelib/global/qcomparehelpers/tst_qcomparehelpers.cpp
+++ b/tests/auto/corelib/global/qcomparehelpers/tst_qcomparehelpers.cpp
@@ -5,6 +5,10 @@
#include <QtTest/qtest.h>
#include <QtTest/private/qcomparisontesthelper_p.h>
+#if defined(__STDCPP_FLOAT16_T__) && __has_include(<stdfloat>)
+#include <stdfloat>
+#endif
+
class IntWrapper
{
public:
@@ -21,13 +25,7 @@ private:
friend Qt::strong_ordering
compareThreeWay(const IntWrapper &lhs, const IntWrapper &rhs) noexcept
{
- // ### Qt::compareThreeWay
- if (lhs.m_val < rhs.m_val)
- return Qt::strong_ordering::less;
- else if (lhs.m_val > rhs.m_val)
- return Qt::strong_ordering::greater;
- else
- return Qt::strong_ordering::equal;
+ return Qt::compareThreeWay(lhs.m_val, rhs.m_val);
}
friend bool comparesEqual(const IntWrapper &lhs, int rhs) noexcept
{ return lhs.m_val == rhs; }
@@ -52,16 +50,7 @@ private:
friend Qt::partial_ordering
compareThreeWay(const DoubleWrapper &lhs, const DoubleWrapper &rhs) noexcept
{
- // ### Qt::compareThreeWay
- if (qIsNaN(lhs.m_val) || qIsNaN(rhs.m_val))
- return Qt::partial_ordering::unordered;
-
- if (lhs.m_val < rhs.m_val)
- return Qt::partial_ordering::less;
- else if (lhs.m_val > rhs.m_val)
- return Qt::partial_ordering::greater;
- else
- return Qt::partial_ordering::equivalent;
+ return Qt::compareThreeWay(lhs.m_val, rhs.m_val);
}
friend bool comparesEqual(const DoubleWrapper &lhs, const IntWrapper &rhs) noexcept
{ return comparesEqual(lhs, DoubleWrapper(rhs.value())); }
@@ -72,16 +61,7 @@ private:
{ return lhs.m_val == rhs; }
friend Qt::partial_ordering compareThreeWay(const DoubleWrapper &lhs, double rhs) noexcept
{
- // ### Qt::compareThreeWay
- if (qIsNaN(lhs.m_val) || qIsNaN(rhs))
- return Qt::partial_ordering::unordered;
-
- if (lhs.m_val < rhs)
- return Qt::partial_ordering::less;
- else if (lhs.m_val > rhs)
- return Qt::partial_ordering::greater;
- else
- return Qt::partial_ordering::equivalent;
+ return Qt::compareThreeWay(lhs.m_val, rhs);
}
Q_DECLARE_PARTIALLY_ORDERED(DoubleWrapper)
@@ -210,6 +190,8 @@ private slots:
{ compareImpl<StringWrapper<QString>, QAnyStringView, Qt::weak_ordering>(); }
void generatedClasses();
+
+ void builtinOrder();
};
template<typename LeftType, typename RightType, typename OrderingType>
@@ -487,5 +469,239 @@ void tst_QCompareHelpers::generatedClasses()
QTestPrivate::testEqualityOperatorsCompile<DummyNone, int>();
}
+template <typename LeftType, typename RightType,
+ Qt::if_integral<LeftType, RightType> = true>
+void testOrderForTypes()
+{
+ LeftType l0{0};
+ LeftType l1{1};
+ RightType r0{0};
+ RightType r1{1};
+ QCOMPARE_EQ(Qt::compareThreeWay(l0, r1), Qt::strong_ordering::less);
+ QCOMPARE_EQ(Qt::compareThreeWay(l1, r0), Qt::strong_ordering::greater);
+ QCOMPARE_EQ(Qt::compareThreeWay(l1, r1), Qt::strong_ordering::equivalent);
+ // also swap types
+ QCOMPARE_EQ(Qt::compareThreeWay(r1, l0), Qt::strong_ordering::greater);
+ QCOMPARE_EQ(Qt::compareThreeWay(r0, l1), Qt::strong_ordering::less);
+ QCOMPARE_EQ(Qt::compareThreeWay(r1, l1), Qt::strong_ordering::equivalent);
+
+#ifdef __cpp_lib_three_way_comparison
+ QCOMPARE_EQ(Qt::compareThreeWay(l0, r1), std::strong_ordering::less);
+ QCOMPARE_EQ(Qt::compareThreeWay(l1, r0), std::strong_ordering::greater);
+ QCOMPARE_EQ(Qt::compareThreeWay(l1, r1), std::strong_ordering::equivalent);
+
+ QCOMPARE_EQ(Qt::compareThreeWay(r1, l0), std::strong_ordering::greater);
+ QCOMPARE_EQ(Qt::compareThreeWay(r0, l1), std::strong_ordering::less);
+ QCOMPARE_EQ(Qt::compareThreeWay(r1, l1), std::strong_ordering::equivalent);
+#endif // __cpp_lib_three_way_comparison
+
+ if constexpr (std::is_signed_v<LeftType>) {
+ LeftType lm1{-1};
+ QCOMPARE_EQ(Qt::compareThreeWay(lm1, r1), Qt::strong_ordering::less);
+ QCOMPARE_EQ(Qt::compareThreeWay(r1, lm1), Qt::strong_ordering::greater);
+#ifdef __cpp_lib_three_way_comparison
+ QCOMPARE_EQ(Qt::compareThreeWay(lm1, r1), std::strong_ordering::less);
+ QCOMPARE_EQ(Qt::compareThreeWay(r1, lm1), std::strong_ordering::greater);
+#endif // __cpp_lib_three_way_comparison
+ }
+ if constexpr (std::is_signed_v<RightType>) {
+ RightType rm1{-1};
+ QCOMPARE_EQ(Qt::compareThreeWay(rm1, l1), Qt::strong_ordering::less);
+ QCOMPARE_EQ(Qt::compareThreeWay(l1, rm1), Qt::strong_ordering::greater);
+#ifdef __cpp_lib_three_way_comparison
+ QCOMPARE_EQ(Qt::compareThreeWay(rm1, l1), std::strong_ordering::less);
+ QCOMPARE_EQ(Qt::compareThreeWay(l1, rm1), std::strong_ordering::greater);
+#endif // __cpp_lib_three_way_comparison
+ }
+}
+
+template <typename LeftType, typename RightType,
+ Qt::if_floating_point<LeftType, RightType> = true>
+void testOrderForTypes()
+{
+ LeftType lNeg{-1.0};
+ LeftType lPos{1.0};
+
+ RightType rNeg{-1.0};
+ RightType rPos{1.0};
+
+ QCOMPARE_EQ(Qt::compareThreeWay(lNeg, rPos), Qt::partial_ordering::less);
+ QCOMPARE_EQ(Qt::compareThreeWay(lPos, rNeg), Qt::partial_ordering::greater);
+ QCOMPARE_EQ(Qt::compareThreeWay(rNeg, lPos), Qt::partial_ordering::less);
+ QCOMPARE_EQ(Qt::compareThreeWay(rPos, lNeg), Qt::partial_ordering::greater);
+ QCOMPARE_EQ(Qt::compareThreeWay(lNeg, rNeg), Qt::partial_ordering::equivalent);
+ QCOMPARE_EQ(Qt::compareThreeWay(rNeg, lNeg), Qt::partial_ordering::equivalent);
+
+ LeftType lNaN{std::numeric_limits<LeftType>::quiet_NaN()};
+ LeftType lInf{std::numeric_limits<LeftType>::infinity()};
+
+ RightType rNaN{std::numeric_limits<RightType>::quiet_NaN()};
+ RightType rInf{std::numeric_limits<RightType>::infinity()};
+
+ QCOMPARE_EQ(Qt::compareThreeWay(lNaN, rPos), Qt::partial_ordering::unordered);
+ QCOMPARE_EQ(Qt::compareThreeWay(rNeg, lNaN), Qt::partial_ordering::unordered);
+ QCOMPARE_EQ(Qt::compareThreeWay(lNeg, rNaN), Qt::partial_ordering::unordered);
+ QCOMPARE_EQ(Qt::compareThreeWay(rNaN, lPos), Qt::partial_ordering::unordered);
+ QCOMPARE_EQ(Qt::compareThreeWay(rNaN, lNaN), Qt::partial_ordering::unordered);
+ QCOMPARE_EQ(Qt::compareThreeWay(lNaN, rNaN), Qt::partial_ordering::unordered);
+ QCOMPARE_EQ(Qt::compareThreeWay(lNaN, rInf), Qt::partial_ordering::unordered);
+ QCOMPARE_EQ(Qt::compareThreeWay(rNaN, -lInf), Qt::partial_ordering::unordered);
+
+ QCOMPARE_EQ(Qt::compareThreeWay(lInf, rPos), Qt::partial_ordering::greater);
+ QCOMPARE_EQ(Qt::compareThreeWay(rPos, lInf), Qt::partial_ordering::less);
+ QCOMPARE_EQ(Qt::compareThreeWay(rInf, lNeg), Qt::partial_ordering::greater);
+ QCOMPARE_EQ(Qt::compareThreeWay(lNeg, rInf), Qt::partial_ordering::less);
+ QCOMPARE_EQ(Qt::compareThreeWay(lInf, -rInf), Qt::partial_ordering::greater);
+ QCOMPARE_EQ(Qt::compareThreeWay(-lInf, rInf), Qt::partial_ordering::less);
+ QCOMPARE_EQ(Qt::compareThreeWay(-rInf, lInf), Qt::partial_ordering::less);
+ QCOMPARE_EQ(Qt::compareThreeWay(rInf, -lInf), Qt::partial_ordering::greater);
+
+#ifdef __cpp_lib_three_way_comparison
+ QCOMPARE_EQ(Qt::compareThreeWay(lNeg, rPos), std::partial_ordering::less);
+ QCOMPARE_EQ(Qt::compareThreeWay(lPos, rNeg), std::partial_ordering::greater);
+ QCOMPARE_EQ(Qt::compareThreeWay(rNeg, lPos), std::partial_ordering::less);
+ QCOMPARE_EQ(Qt::compareThreeWay(rPos, lNeg), std::partial_ordering::greater);
+ QCOMPARE_EQ(Qt::compareThreeWay(lNeg, rNeg), std::partial_ordering::equivalent);
+ QCOMPARE_EQ(Qt::compareThreeWay(rNeg, lNeg), std::partial_ordering::equivalent);
+
+ QCOMPARE_EQ(Qt::compareThreeWay(lNaN, rPos), std::partial_ordering::unordered);
+ QCOMPARE_EQ(Qt::compareThreeWay(rNeg, lNaN), std::partial_ordering::unordered);
+ QCOMPARE_EQ(Qt::compareThreeWay(lNeg, rNaN), std::partial_ordering::unordered);
+ QCOMPARE_EQ(Qt::compareThreeWay(rNaN, lPos), std::partial_ordering::unordered);
+ QCOMPARE_EQ(Qt::compareThreeWay(rNaN, lNaN), std::partial_ordering::unordered);
+ QCOMPARE_EQ(Qt::compareThreeWay(lNaN, rNaN), std::partial_ordering::unordered);
+ QCOMPARE_EQ(Qt::compareThreeWay(lNaN, rInf), std::partial_ordering::unordered);
+ QCOMPARE_EQ(Qt::compareThreeWay(rNaN, -lInf), std::partial_ordering::unordered);
+
+ QCOMPARE_EQ(Qt::compareThreeWay(lInf, rPos), std::partial_ordering::greater);
+ QCOMPARE_EQ(Qt::compareThreeWay(rPos, lInf), std::partial_ordering::less);
+ QCOMPARE_EQ(Qt::compareThreeWay(rInf, lNeg), std::partial_ordering::greater);
+ QCOMPARE_EQ(Qt::compareThreeWay(lNeg, rInf), std::partial_ordering::less);
+ QCOMPARE_EQ(Qt::compareThreeWay(lInf, -rInf), std::partial_ordering::greater);
+ QCOMPARE_EQ(Qt::compareThreeWay(-lInf, rInf), std::partial_ordering::less);
+ QCOMPARE_EQ(Qt::compareThreeWay(-rInf, lInf), std::partial_ordering::less);
+ QCOMPARE_EQ(Qt::compareThreeWay(rInf, -lInf), std::partial_ordering::greater);
+#endif // __cpp_lib_three_way_comparison
+}
+
+template <typename IntType, typename FloatType,
+ Qt::if_integral_and_floating_point<IntType, FloatType> = true>
+void testOrderForTypes()
+{
+ IntType l0{0};
+ IntType l1{1};
+
+ FloatType r0{0.0};
+ FloatType r1{1.0};
+ FloatType rNaN{std::numeric_limits<FloatType>::quiet_NaN()};
+
+ QCOMPARE_EQ(Qt::compareThreeWay(l0, r1), Qt::partial_ordering::less);
+ QCOMPARE_EQ(Qt::compareThreeWay(l1, r0), Qt::partial_ordering::greater);
+ QCOMPARE_EQ(Qt::compareThreeWay(r1, l0), Qt::partial_ordering::greater);
+ QCOMPARE_EQ(Qt::compareThreeWay(r0, l1), Qt::partial_ordering::less);
+ QCOMPARE_EQ(Qt::compareThreeWay(l0, r0), Qt::partial_ordering::equivalent);
+ QCOMPARE_EQ(Qt::compareThreeWay(r0, l0), Qt::partial_ordering::equivalent);
+ QCOMPARE_EQ(Qt::compareThreeWay(l0, rNaN), Qt::partial_ordering::unordered);
+ QCOMPARE_EQ(Qt::compareThreeWay(rNaN, l1), Qt::partial_ordering::unordered);
+#ifdef __cpp_lib_three_way_comparison
+ QCOMPARE_EQ(Qt::compareThreeWay(l0, r1), std::partial_ordering::less);
+ QCOMPARE_EQ(Qt::compareThreeWay(l1, r0), std::partial_ordering::greater);
+ QCOMPARE_EQ(Qt::compareThreeWay(r1, l0), std::partial_ordering::greater);
+ QCOMPARE_EQ(Qt::compareThreeWay(r0, l1), std::partial_ordering::less);
+ QCOMPARE_EQ(Qt::compareThreeWay(l0, r0), std::partial_ordering::equivalent);
+ QCOMPARE_EQ(Qt::compareThreeWay(r0, l0), std::partial_ordering::equivalent);
+ QCOMPARE_EQ(Qt::compareThreeWay(l0, rNaN), std::partial_ordering::unordered);
+ QCOMPARE_EQ(Qt::compareThreeWay(rNaN, l1), std::partial_ordering::unordered);
+#endif // __cpp_lib_three_way_comparison
+}
+
+enum class TestEnum : quint8 {
+ Smaller,
+ Bigger
+};
+
+void tst_QCompareHelpers::builtinOrder()
+{
+#define TEST_BUILTIN(Left, Right) \
+ testOrderForTypes<Left, Right>(); \
+ if (QTest::currentTestFailed()) { \
+ qDebug("Failed Qt::compareThreeWay() test for builtin types " #Left " and " #Right); \
+ return; \
+ }
+
+ // some combinations
+ TEST_BUILTIN(char, char)
+#if CHAR_MIN < 0
+ TEST_BUILTIN(char, short)
+ TEST_BUILTIN(qint8, char)
+#else
+ TEST_BUILTIN(char, ushort)
+ TEST_BUILTIN(quint8, char)
+#endif
+ TEST_BUILTIN(qint8, qint8)
+ TEST_BUILTIN(qint8, int)
+ TEST_BUILTIN(ulong, quint8)
+ TEST_BUILTIN(ushort, uchar)
+ TEST_BUILTIN(int, int)
+ TEST_BUILTIN(uint, ulong)
+ TEST_BUILTIN(long, int)
+ TEST_BUILTIN(uint, quint64)
+ TEST_BUILTIN(qint64, short)
+ TEST_BUILTIN(wchar_t, wchar_t)
+ TEST_BUILTIN(uint, char16_t)
+ TEST_BUILTIN(char32_t, char32_t)
+ TEST_BUILTIN(char32_t, ushort)
+#ifdef __cpp_char8_t
+ TEST_BUILTIN(char8_t, char8_t)
+ TEST_BUILTIN(char8_t, ushort)
+ TEST_BUILTIN(char8_t, uint)
+ TEST_BUILTIN(char8_t, quint64)
+#endif // __cpp_char8_t
+#ifdef QT_SUPPORTS_INT128
+ TEST_BUILTIN(qint128, qint128)
+ TEST_BUILTIN(quint128, quint128)
+ TEST_BUILTIN(qint128, int)
+ TEST_BUILTIN(ushort, quint128)
+#endif
+ TEST_BUILTIN(float, double)
+ TEST_BUILTIN(double, float)
+ TEST_BUILTIN(quint64, float)
+ TEST_BUILTIN(qint64, double)
+#ifdef __STDCPP_FLOAT16_T__
+ TEST_BUILTIN(std::float16_t, std::float16_t)
+ TEST_BUILTIN(std::float16_t, double)
+ TEST_BUILTIN(qint64, std::float16_t)
+ TEST_BUILTIN(uint, std::float16_t)
+#endif
+
+ TEST_BUILTIN(long double, long double)
+ TEST_BUILTIN(float, long double)
+ TEST_BUILTIN(double, long double)
+ TEST_BUILTIN(quint64, long double)
+ TEST_BUILTIN(ushort, long double)
+
+ QCOMPARE_EQ(Qt::compareThreeWay(TestEnum::Smaller, TestEnum::Bigger),
+ Qt::strong_ordering::less);
+ QCOMPARE_EQ(Qt::compareThreeWay(TestEnum::Bigger, TestEnum::Smaller),
+ Qt::strong_ordering::greater);
+ QCOMPARE_EQ(Qt::compareThreeWay(TestEnum::Smaller, TestEnum::Smaller),
+ Qt::strong_ordering::equivalent);
+
+ std::array<int, 2> arr{1, 0};
+ QCOMPARE_EQ(Qt::compareThreeWay(&arr[0], &arr[1]), Qt::strong_ordering::less);
+ QCOMPARE_EQ(Qt::compareThreeWay(arr.data(), &arr[0]), Qt::strong_ordering::equivalent);
+
+ class Base {};
+ class Derived : public Base {};
+
+ auto b = std::make_unique<Base>();
+ auto d = std::make_unique<Derived>();
+ QCOMPARE_NE(Qt::compareThreeWay(b.get(), d.get()), Qt::strong_ordering::equivalent);
+ QCOMPARE_EQ(Qt::compareThreeWay(b.get(), nullptr), Qt::strong_ordering::greater);
+ QCOMPARE_EQ(Qt::compareThreeWay(nullptr, d.get()), Qt::strong_ordering::less);
+
+#undef TEST_BUILTIN
+}
+
QTEST_MAIN(tst_QCompareHelpers)
#include "tst_qcomparehelpers.moc"