summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/global/qcompare.cpp59
-rw-r--r--src/corelib/global/qcompare.h77
-rw-r--r--tests/auto/corelib/global/qcompare/tst_qcompare.cpp113
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"