diff options
-rw-r--r-- | src/corelib/global/qcompare.cpp | 59 | ||||
-rw-r--r-- | src/corelib/global/qcompare.h | 77 | ||||
-rw-r--r-- | tests/auto/corelib/global/qcompare/tst_qcompare.cpp | 113 |
3 files changed, 247 insertions, 2 deletions
diff --git a/src/corelib/global/qcompare.cpp b/src/corelib/global/qcompare.cpp index 4f3a240068..91124c3d7d 100644 --- a/src/corelib/global/qcompare.cpp +++ b/src/corelib/global/qcompare.cpp @@ -134,6 +134,16 @@ CHECK(strong, equivalent); This header introduces the \l Qt::partial_ordering, \l Qt::weak_ordering, and \l Qt::strong_ordering types, which are Qt's C++17 backports of \c {std::*_ordering} types. + + This header also contains functions for implementing three-way comparison + in C++17. + + The \c {Qt::compareThreeWay()} function overloads provide three-way + comparison for built-in C++ types. + + The \l qCompareThreeWay() template serves as a generic three-way comparison + implementation. It relies on \c {Qt::compareThreeWay()} and free + \c {compareThreeWay()} functions in its implementation. */ /*! @@ -1250,4 +1260,53 @@ CHECK(strong, equivalent); between \a lhs and \a rhs. */ +/*! + \fn template <typename LeftType, typename RightType> qCompareThreeWay(const LeftType &lhs, const RightType &rhs) + \since 6.7 + \relates <QtCompare> + + Performs the three-way comparison on \a lhs and \a rhs and returns one of + the Qt ordering types as a result. This function is available for both + C++17 and C++20. + + The actual returned type depends on \c LeftType and \c RightType. + + \note This function template is only available when \c {compareThreeWay()} + is implemented for the \c {(LeftType, RightType)} pair or the reversed + \c {(RightType, LeftType)} pair. + + This method is equivalent to + + \code + using Qt::compareThreeWay; + return compareThreeWay(lhs, rhs); + \endcode + + where \c {Qt::compareThreeWay} is the Qt implementation of three-way + comparison for built-in types. + + The free \c {compareThreeWay} functions should provide three-way comparison + for custom types. The functions should return one of the Qt ordering types. + + Qt provides \c {compareThreeWay} implementation for some of its types. + + \note \b {Do not} re-implement \c {compareThreeWay()} for Qt types, as more + Qt types will get support for it in future Qt releases. + + Use this function primarly in generic code, when you know nothing about + \c LeftType and \c RightType. + + If you know the types, use + + \list + \li \c {Qt::compareThreeWay} for built-in types + \li \c {compareThreeWay} for custom types + \endlist + + Use \c {operator<=>()} directly in code that will only be compiled with + C++20 or later. + + \sa Qt::partial_ordering, Qt::weak_ordering, Qt::strong_ordering +*/ + QT_END_NAMESPACE diff --git a/src/corelib/global/qcompare.h b/src/corelib/global/qcompare.h index a9227c71d3..8f3a99953c 100644 --- a/src/corelib/global/qcompare.h +++ b/src/corelib/global/qcompare.h @@ -682,9 +682,82 @@ inline constexpr strong_ordering strong_ordering::greater(QtPrivate::Ordering::G } // namespace Qt -QT_END_NAMESPACE +QT_BEGIN_INCLUDE_NAMESPACE -// This is intentionally included in the end of qcompare.h +// This is intentionally included after Qt::*_ordering types and before +// qCompareThreeWay. Do not change! #include <QtCore/qcomparehelpers.h> +QT_END_INCLUDE_NAMESPACE + +namespace QtPrivate { + +namespace CompareThreeWayTester { + + using Qt::compareThreeWay; + + // Check if compareThreeWay is implemented for the (LT, RT) argument + // pair. + template <typename LT, typename RT, typename = void> + constexpr bool hasCompareThreeWay = false; + + template <typename LT, typename RT> + constexpr bool hasCompareThreeWay< + LT, RT, std::void_t<decltype(compareThreeWay(std::declval<LT>(), std::declval<RT>()))> + > = true; + + // Check if the operation is noexcept. We have two different overloads, + // depending on the available compareThreeWay() implementation. + // Both are declared, but not implemented. To be used only in unevaluated + // context. + + template <typename LT, typename RT, + std::enable_if_t<hasCompareThreeWay<LT, RT>, bool> = true> + constexpr bool compareThreeWayNoexcept() noexcept + { return noexcept(compareThreeWay(std::declval<LT>(), std::declval<RT>())); } + + template <typename LT, typename RT, + std::enable_if_t<!hasCompareThreeWay<LT, RT> && hasCompareThreeWay<RT, LT>, + bool> = true> + constexpr bool compareThreeWayNoexcept() noexcept + { return noexcept(compareThreeWay(std::declval<RT>(), std::declval<LT>())); } + +} // namespace CompareThreeWayTester + +} // namespace QtPrivate + +#if defined(Q_QDOC) + +template <typename LeftType, typename RightType> +auto qCompareThreeWay(const LeftType &lhs, const RightType &rhs); + +#else + +template <typename LT, typename RT, + std::enable_if_t<QtPrivate::CompareThreeWayTester::hasCompareThreeWay<LT, RT> + || QtPrivate::CompareThreeWayTester::hasCompareThreeWay<RT, LT>, + bool> = true> +auto qCompareThreeWay(const LT &lhs, const RT &rhs) + noexcept(QtPrivate::CompareThreeWayTester::compareThreeWayNoexcept<LT, RT>()) +{ + using Qt::compareThreeWay; + if constexpr (QtPrivate::CompareThreeWayTester::hasCompareThreeWay<LT, RT>) { + return compareThreeWay(lhs, rhs); + } else { + const auto retval = compareThreeWay(rhs, lhs); + // We can compare any ordering type with Qt::partial_ordering, but we + // always need to return the right type. Use Qt::strong_ordering for + // casting, as it can be cast to any ordering type. + if (retval == Qt::partial_ordering::less) + return static_cast<decltype(retval)>(Qt::strong_ordering::greater); + else if (retval == Qt::partial_ordering::greater) + return static_cast<decltype(retval)>(Qt::strong_ordering::less); + return retval; + } +} + +#endif // defined(Q_QDOC) + +QT_END_NAMESPACE + #endif // QCOMPARE_H diff --git a/tests/auto/corelib/global/qcompare/tst_qcompare.cpp b/tests/auto/corelib/global/qcompare/tst_qcompare.cpp index ba382d57fb..f07b2b8910 100644 --- a/tests/auto/corelib/global/qcompare/tst_qcompare.cpp +++ b/tests/auto/corelib/global/qcompare/tst_qcompare.cpp @@ -20,6 +20,7 @@ private slots: void strongOrdering(); void conversions(); void is_eq_overloads(); + void compareThreeWay(); }; void tst_QCompare::legacyPartialOrdering() @@ -647,5 +648,117 @@ void tst_QCompare::is_eq_overloads() #endif // __cpp_lib_three_way_comparison } +class StringWrapper +{ +public: + explicit StringWrapper() {} + explicit StringWrapper(const QString &val) : m_val(val) {} + QString value() const { return m_val; } + +private: + static Qt::weak_ordering compareHelper(const QString &lhs, const QString &rhs) noexcept + { + const int res = QString::compare(lhs, rhs, Qt::CaseInsensitive); + if (res < 0) + return Qt::weak_ordering::less; + else if (res > 0) + return Qt::weak_ordering::greater; + else + return Qt::weak_ordering::equivalent; + } + + friend bool comparesEqual(const StringWrapper &lhs, const StringWrapper &rhs) noexcept + { return QString::compare(lhs.m_val, rhs.m_val, Qt::CaseInsensitive) == 0; } + friend Qt::weak_ordering + compareThreeWay(const StringWrapper &lhs, const StringWrapper &rhs) noexcept + { return compareHelper(lhs.m_val, rhs.m_val); } + Q_DECLARE_WEAKLY_ORDERED(StringWrapper) + + // these helper functions are intentionally non-noexcept + friend bool comparesEqual(const StringWrapper &lhs, int rhs) + { return comparesEqual(lhs, StringWrapper(QString::number(rhs))); } + friend Qt::weak_ordering compareThreeWay(const StringWrapper &lhs, int rhs) + { return compareHelper(lhs.m_val, QString::number(rhs)); } + Q_DECLARE_WEAKLY_ORDERED(StringWrapper, int) + + QString m_val; +}; + +void tst_QCompare::compareThreeWay() +{ + // test noexcept + + // for custom types + static_assert(noexcept(qCompareThreeWay(std::declval<StringWrapper>(), + std::declval<StringWrapper>()))); + static_assert(!noexcept(qCompareThreeWay(std::declval<StringWrapper>(), + std::declval<int>()))); + static_assert(!noexcept(qCompareThreeWay(std::declval<int>(), + std::declval<StringWrapper>()))); + // for built-in types + static_assert(noexcept(qCompareThreeWay(std::declval<int>(), std::declval<int>()))); + static_assert(noexcept(qCompareThreeWay(std::declval<float>(), std::declval<int>()))); + static_assert(noexcept(qCompareThreeWay(std::declval<double>(), std::declval<float>()))); + static_assert(noexcept(qCompareThreeWay(std::declval<int>(), std::declval<int>()))); + + // enums + enum TestEnum : int { + Smaller, + Bigger + }; + static_assert(noexcept(qCompareThreeWay(std::declval<TestEnum>(), std::declval<TestEnum>()))); + + // pointers + static_assert(noexcept(qCompareThreeWay(std::declval<StringWrapper *>(), + std::declval<StringWrapper *>()))); + static_assert(noexcept(qCompareThreeWay(std::declval<StringWrapper *>(), nullptr))); + + // Test some actual comparison results + + // for custom types + QCOMPARE_EQ(qCompareThreeWay(StringWrapper("ABC"), StringWrapper("abc")), + Qt::weak_ordering::equivalent); + QVERIFY(StringWrapper("ABC") == StringWrapper("abc")); + QCOMPARE_EQ(qCompareThreeWay(StringWrapper("ABC"), StringWrapper("qwe")), + Qt::weak_ordering::less); + QVERIFY(StringWrapper("ABC") != StringWrapper("qwe")); + QCOMPARE_EQ(qCompareThreeWay(StringWrapper("qwe"), StringWrapper("ABC")), + Qt::weak_ordering::greater); + QVERIFY(StringWrapper("qwe") != StringWrapper("ABC")); + QCOMPARE_EQ(qCompareThreeWay(StringWrapper("10"), 10), Qt::weak_ordering::equivalent); + QVERIFY(StringWrapper("10") == 10); + QCOMPARE_EQ(qCompareThreeWay(StringWrapper("10"), 12), Qt::weak_ordering::less); + QVERIFY(StringWrapper("10") != 12); + QCOMPARE_EQ(qCompareThreeWay(StringWrapper("12"), 10), Qt::weak_ordering::greater); + QVERIFY(StringWrapper("12") != 10); + + // reversed compareThreeWay() + auto result = qCompareThreeWay(10, StringWrapper("12")); + QCOMPARE_EQ(result, Qt::weak_ordering::less); + static_assert(std::is_same_v<decltype(result), Qt::weak_ordering>); + QVERIFY(10 != StringWrapper("12")); + result = qCompareThreeWay(12, StringWrapper("10")); + QCOMPARE_EQ(result, Qt::weak_ordering::greater); + static_assert(std::is_same_v<decltype(result), Qt::weak_ordering>); + QVERIFY(12 != StringWrapper("10")); + result = qCompareThreeWay(10, StringWrapper("10")); + QCOMPARE_EQ(result, Qt::weak_ordering::equivalent); + static_assert(std::is_same_v<decltype(result), Qt::weak_ordering>); + QVERIFY(10 == StringWrapper("10")); + + // built-in types + QCOMPARE_EQ(qCompareThreeWay(1, 1.0), Qt::partial_ordering::equivalent); + QCOMPARE_EQ(qCompareThreeWay(1, 2), Qt::strong_ordering::less); + QCOMPARE_EQ(qCompareThreeWay(2.0f, 1.0), Qt::partial_ordering::greater); + + // enums + QCOMPARE_EQ(qCompareThreeWay(Smaller, Bigger), Qt::strong_ordering::less); + + // pointers + std::array<int, 2> arr{1, 0}; + QCOMPARE_EQ(qCompareThreeWay(&arr[1], &arr[0]), Qt::strong_ordering::greater); + QCOMPARE_EQ(qCompareThreeWay(arr.data(), &arr[0]), Qt::strong_ordering::equivalent); +} + QTEST_MAIN(tst_QCompare) #include "tst_qcompare.moc" |