From bd225ef95e948f375d54af517097039d2c59a5fa Mon Sep 17 00:00:00 2001 From: Karsten Heimrich Date: Mon, 21 Sep 2020 12:40:59 +0200 Subject: [PATCH] Make QAnyStringView comparison operators hidden friends Also add the very same operators to the QBasicUtf8StringView class to overcome the compiler issues seen on gcc. Fixes: QTBUG-86481 Change-Id: I12484455ebd3b7b38d4ad67c38977d76f9b3ddfa Reviewed-by: Andrei Golubev Reviewed-by: Lars Knoll --- src/corelib/text/qanystringview.h | 44 ++++++----- src/corelib/text/qstring.h | 7 +- src/corelib/text/qutf8stringview.h | 23 ++++++ .../tst_qstringapisymmetry.cpp | 78 +++++++++++-------- 4 files changed, 98 insertions(+), 54 deletions(-) diff --git a/src/corelib/text/qanystringview.h b/src/corelib/text/qanystringview.h index 242efc6e5c7..647f43f2653 100644 --- a/src/corelib/text/qanystringview.h +++ b/src/corelib/text/qanystringview.h @@ -42,6 +42,10 @@ #include #include +#ifdef __cpp_impl_three_way_comparison +#include +#endif + QT_BEGIN_NAMESPACE template class QStringBuilder; @@ -217,7 +221,27 @@ public: constexpr int length() const /* not nothrow! */ { return Q_ASSERT(int(size()) == size()), int(size()); } #endif + private: + [[nodiscard]] friend inline bool operator==(QAnyStringView lhs, QAnyStringView rhs) noexcept + { return QAnyStringView::compare(lhs, rhs) == 0; } + [[nodiscard]] friend inline bool operator!=(QAnyStringView lhs, QAnyStringView rhs) noexcept + { return !operator==(lhs, rhs); } + +#ifdef __cpp_impl_three_way_comparison + [[nodiscard]] friend inline auto operator<=>(QAnyStringView lhs, QAnyStringView rhs) noexcept + { return QAnyStringView::compare(lhs, rhs) <=> 0; } +#else + [[nodiscard]] friend inline bool operator<=(QAnyStringView lhs, QAnyStringView rhs) noexcept + { return QAnyStringView::compare(lhs, rhs) <= 0; } + [[nodiscard]] friend inline bool operator>=(QAnyStringView lhs, QAnyStringView rhs) noexcept + { return QAnyStringView::compare(lhs, rhs) >= 0; } + [[nodiscard]] friend inline bool operator<(QAnyStringView lhs, QAnyStringView rhs) noexcept + { return QAnyStringView::compare(lhs, rhs) < 0; } + [[nodiscard]] friend inline bool operator>(QAnyStringView lhs, QAnyStringView rhs) noexcept + { return QAnyStringView::compare(lhs, rhs) > 0; } +#endif + // TODO: Optimize by inverting and storing the flags in the low bits and // the size in the high. static_assert(std::is_same_v); @@ -273,26 +297,6 @@ template ) -#else -Q_ANY_SV_MAKE_RELOP(<=) -Q_ANY_SV_MAKE_RELOP(>=) -Q_ANY_SV_MAKE_RELOP(<) -Q_ANY_SV_MAKE_RELOP(>) -#endif - -#undef Q_ANY_SV_MAKE_RELOP - QT_END_NAMESPACE #endif /* QANYSTRINGVIEW_H */ diff --git a/src/corelib/text/qstring.h b/src/corelib/text/qstring.h index d12c96632e1..5c809592e8d 100644 --- a/src/corelib/text/qstring.h +++ b/src/corelib/text/qstring.h @@ -1042,6 +1042,11 @@ QString QBasicUtf8StringView::toString() const return QString::fromUtf8(data(), int(size())); } +template +inline int QBasicUtf8StringView::compare(QBasicUtf8StringView lhs, QBasicUtf8StringView rhs, + Qt::CaseSensitivity cs) noexcept +{ return QAnyStringView::compare(lhs, rhs, cs); } + // // QAnyStringView inline members that require QString: // @@ -1438,7 +1443,6 @@ inline bool operator> (QLatin1String lhs, QChar rhs) noexcept { return rhs < inline bool operator<=(QLatin1String lhs, QChar rhs) noexcept { return !(rhs < lhs); } inline bool operator>=(QLatin1String lhs, QChar rhs) noexcept { return !(rhs > lhs); } -#if 0 // QStringView <> QStringView inline bool operator==(QStringView lhs, QStringView rhs) noexcept { return lhs.size() == rhs.size() && QtPrivate::compareStrings(lhs, rhs) == 0; } inline bool operator!=(QStringView lhs, QStringView rhs) noexcept { return !(lhs == rhs); } @@ -1446,7 +1450,6 @@ inline bool operator< (QStringView lhs, QStringView rhs) noexcept { return QtPri inline bool operator<=(QStringView lhs, QStringView rhs) noexcept { return QtPrivate::compareStrings(lhs, rhs) <= 0; } inline bool operator> (QStringView lhs, QStringView rhs) noexcept { return QtPrivate::compareStrings(lhs, rhs) > 0; } inline bool operator>=(QStringView lhs, QStringView rhs) noexcept { return QtPrivate::compareStrings(lhs, rhs) >= 0; } -#endif // QStringView <> QChar inline bool operator==(QStringView lhs, QChar rhs) noexcept { return lhs == QStringView(&rhs, 1); } diff --git a/src/corelib/text/qutf8stringview.h b/src/corelib/text/qutf8stringview.h index ac42a3d73f9..98357ae5a9e 100644 --- a/src/corelib/text/qutf8stringview.h +++ b/src/corelib/text/qutf8stringview.h @@ -304,7 +304,30 @@ public: constexpr int length() const /* not nothrow! */ { return Q_ASSERT(int(size()) == size()), int(size()); } #endif + private: + [[nodiscard]] static inline int compare(QBasicUtf8StringView lhs, QBasicUtf8StringView rhs, + Qt::CaseSensitivity cs = Qt::CaseSensitive) noexcept; + + [[nodiscard]] friend inline bool operator==(QBasicUtf8StringView lhs, QBasicUtf8StringView rhs) noexcept + { return QBasicUtf8StringView::compare(lhs, rhs) == 0; } + [[nodiscard]] friend inline bool operator!=(QBasicUtf8StringView lhs, QBasicUtf8StringView rhs) noexcept + { return !operator==(lhs, rhs); } + +#ifdef __cpp_impl_three_way_comparison + [[nodiscard]] friend inline auto operator<=>(QBasicUtf8StringView lhs, QBasicUtf8StringView rhs) noexcept + { return QBasicUtf8StringView::compare(lhs, rhs) <=> 0; } +#else + [[nodiscard]] friend inline bool operator<=(QBasicUtf8StringView lhs, QBasicUtf8StringView rhs) noexcept + { return QBasicUtf8StringView::compare(lhs, rhs) <= 0; } + [[nodiscard]] friend inline bool operator>=(QBasicUtf8StringView lhs, QBasicUtf8StringView rhs) noexcept + { return QBasicUtf8StringView::compare(lhs, rhs) >= 0; } + [[nodiscard]] friend inline bool operator<(QBasicUtf8StringView lhs, QBasicUtf8StringView rhs) noexcept + { return QBasicUtf8StringView::compare(lhs, rhs) < 0; } + [[nodiscard]] friend inline bool operator>(QBasicUtf8StringView lhs, QBasicUtf8StringView rhs) noexcept + { return QBasicUtf8StringView::compare(lhs, rhs) > 0; } +#endif + Q_ALWAYS_INLINE constexpr void verify(qsizetype pos, qsizetype n = 0) const { Q_ASSERT(pos >= 0); diff --git a/tests/auto/corelib/text/qstringapisymmetry/tst_qstringapisymmetry.cpp b/tests/auto/corelib/text/qstringapisymmetry/tst_qstringapisymmetry.cpp index 79eab245c95..9f94de7af21 100644 --- a/tests/auto/corelib/text/qstringapisymmetry/tst_qstringapisymmetry.cpp +++ b/tests/auto/corelib/text/qstringapisymmetry/tst_qstringapisymmetry.cpp @@ -49,6 +49,7 @@ struct QAnyStringViewUsingU16 : QAnyStringView {}; // QAnyStringView with Utf-1 template QString toQString(const T &t) { return QString(t); } QString toQString(QStringView view) { return view.toString(); } +QString toQString(QUtf8StringView view) { return view.toString(); } template QStringList toQStringList(const Iterable &i) { @@ -83,6 +84,19 @@ MAKE_ALL(char16_t, QByteArray) MAKE_ALL(const char*, QChar) +MAKE_ALL(QChar, QByteArray) +MAKE_ALL(QChar, const char*) +MAKE_ALL(QChar, QUtf8StringView) + +MAKE_ALL(QString, QUtf8StringView) +MAKE_ALL(QByteArray, QUtf8StringView) +MAKE_ALL(const char*, QUtf8StringView) + +MAKE_ALL(QUtf8StringView, QChar) +MAKE_ALL(QUtf8StringView, char16_t) +MAKE_ALL(QUtf8StringView, QStringView) +MAKE_ALL(QUtf8StringView, QLatin1String) + #undef MAKE_ALL #undef MAKE_RELOP // END FIXME @@ -1383,7 +1397,7 @@ void tst_QStringApiSymmetry::mid_data() // mid() has a wider contract compared to sliced(), so test those cases here: #define ROW(base, p, n, r1, r2) \ - QTest::addRow("%s %d %d", #base, p, n) << QStringView(base) << QLatin1String(#base) << p << n << QStringView(r1) << QStringView(r2) + QTest::addRow("%s %d %d", #base, p, n) << QStringView(base) << QLatin1String(#base) << p << n << QAnyStringView(r1) << QAnyStringView(r2) ROW(a, -1, 0, a, null); ROW(a, -1, 2, a, a); @@ -1420,8 +1434,8 @@ void tst_QStringApiSymmetry::mid_impl() QFETCH(const QLatin1String, latin1); QFETCH(const int, pos); QFETCH(const int, n); - QFETCH(const QStringView, result); - QFETCH(const QStringView, result2); + QFETCH(const QAnyStringView, result); + QFETCH(const QAnyStringView, result2); const auto utf8 = unicode.toUtf8(); @@ -1459,7 +1473,7 @@ void tst_QStringApiSymmetry::left_data() // specific data testing out of bounds cases #define ROW(base, n, res) \ - QTest::addRow("%s%d", #base, n) << QStringView(base) << QLatin1String(#base) << n << QStringView(res); + QTest::addRow("%s%d", #base, n) << QStringView(base) << QLatin1String(#base) << n << QAnyStringView(res); ROW(a, -1, a); ROW(a, 2, a); @@ -1477,7 +1491,7 @@ void tst_QStringApiSymmetry::left_QByteArray_data() // specific data testing out of bounds cases #define ROW(base, n, res) \ - QTest::addRow("%s%d", #base, n) << QStringView(base) << QLatin1String(#base) << n << QStringView(res); + QTest::addRow("%s%d", #base, n) << QStringView(base) << QLatin1String(#base) << n << QAnyStringView(res); ROW(a, -1, empty); ROW(a, 2, a); @@ -1493,7 +1507,7 @@ void tst_QStringApiSymmetry::left_impl() QFETCH(const QStringView, unicode); QFETCH(const QLatin1String, latin1); QFETCH(const int, n); - QFETCH(const QStringView, result); + QFETCH(const QAnyStringView, result); const auto utf8 = unicode.toUtf8(); @@ -1521,7 +1535,7 @@ void tst_QStringApiSymmetry::right_data() // specific data testing out of bounds cases #define ROW(base, n, res) \ - QTest::addRow("%s%d", #base, n) << QStringView(base) << QLatin1String(#base) << n << QStringView(res); + QTest::addRow("%s%d", #base, n) << QStringView(base) << QLatin1String(#base) << n << QAnyStringView(res); ROW(a, -1, a); ROW(a, 2, a); @@ -1539,7 +1553,7 @@ void tst_QStringApiSymmetry::right_QByteArray_data() // specific data testing out of bounds cases #define ROW(base, n, res) \ - QTest::addRow("%s%d", #base, n) << QStringView(base) << QLatin1String(#base) << n << QStringView(res); + QTest::addRow("%s%d", #base, n) << QStringView(base) << QLatin1String(#base) << n << QAnyStringView(res); ROW(a, -1, empty); ROW(a, 2, a); @@ -1555,7 +1569,7 @@ void tst_QStringApiSymmetry::right_impl() QFETCH(const QStringView, unicode); QFETCH(const QLatin1String, latin1); QFETCH(const int, n); - QFETCH(const QStringView, result); + QFETCH(const QAnyStringView, result); const auto utf8 = unicode.toUtf8(); @@ -1583,14 +1597,14 @@ void tst_QStringApiSymmetry::sliced_data() QTest::addColumn("latin1"); QTest::addColumn("pos"); QTest::addColumn("n"); - QTest::addColumn("result"); - QTest::addColumn("result2"); + QTest::addColumn("result"); + QTest::addColumn("result2"); // QTest::addRow("null") << QStringView() << QLatin1String() << 0 << 0 << QStringView() << QStringView(); - QTest::addRow("empty") << QStringView(empty) << QLatin1String("") << 0 << 0 << QStringView(empty) << QStringView(empty); + QTest::addRow("empty") << QStringView(empty) << QLatin1String("") << 0 << 0 << QAnyStringView(empty) << QAnyStringView(empty); #define ROW(base, p, n, r1, r2) \ - QTest::addRow("%s%d%d", #base, p, n) << QStringView(base) << QLatin1String(#base) << p << n << QStringView(r1) << QStringView(r2) + QTest::addRow("%s%d%d", #base, p, n) << QStringView(base) << QLatin1String(#base) << p << n << QAnyStringView(r1) << QAnyStringView(r2) ROW(a, 0, 0, a, empty); ROW(a, 0, 1, a, a); @@ -1623,8 +1637,8 @@ void tst_QStringApiSymmetry::sliced_impl() QFETCH(const QLatin1String, latin1); QFETCH(const int, pos); QFETCH(const int, n); - QFETCH(const QStringView, result); - QFETCH(const QStringView, result2); + QFETCH(const QAnyStringView, result); + QFETCH(const QAnyStringView, result2); const auto utf8 = unicode.toUtf8(); @@ -1665,15 +1679,15 @@ void tst_QStringApiSymmetry::first_data() QTest::addColumn("unicode"); QTest::addColumn("latin1"); QTest::addColumn("n"); - QTest::addColumn("result"); + QTest::addColumn("result"); // QTest::addRow("null") << QStringView() << QLatin1String() << 0 << QStringView(); - QTest::addRow("empty") << QStringView(empty) << QLatin1String("") << 0 << QStringView(empty); + QTest::addRow("empty") << QStringView(empty) << QLatin1String("") << 0 << QAnyStringView(empty); // Some classes' left() implementations have a wide contract, others a narrow one // so only test valid arguments here: #define ROW(base, n, res) \ - QTest::addRow("%s%d", #base, n) << QStringView(base) << QLatin1String(#base) << n << QStringView(res); + QTest::addRow("%s%d", #base, n) << QStringView(base) << QLatin1String(#base) << n << QAnyStringView(res); ROW(a, 0, empty); ROW(a, 1, a); @@ -1695,7 +1709,7 @@ void tst_QStringApiSymmetry::first_impl() QFETCH(const QStringView, unicode); QFETCH(const QLatin1String, latin1); QFETCH(const int, n); - QFETCH(const QStringView, result); + QFETCH(const QAnyStringView, result); const auto utf8 = unicode.toUtf8(); @@ -1730,15 +1744,15 @@ void tst_QStringApiSymmetry::last_data() QTest::addColumn("unicode"); QTest::addColumn("latin1"); QTest::addColumn("n"); - QTest::addColumn("result"); + QTest::addColumn("result"); // QTest::addRow("null") << QStringView() << QLatin1String() << 0 << QStringView(); - QTest::addRow("empty") << QStringView(empty) << QLatin1String("") << 0 << QStringView(empty); + QTest::addRow("empty") << QStringView(empty) << QLatin1String("") << 0 << QAnyStringView(empty); // Some classes' last() implementations have a wide contract, others a narrow one - // so only test valid arguents here: + // so only test valid arguments here: #define ROW(base, n, res) \ - QTest::addRow("%s%d", #base, n) << QStringView(base) << QLatin1String(#base) << n << QStringView(res); + QTest::addRow("%s%d", #base, n) << QStringView(base) << QLatin1String(#base) << n << QAnyStringView(res); ROW(a, 0, empty); ROW(a, 1, a); @@ -1760,7 +1774,7 @@ void tst_QStringApiSymmetry::last_impl() QFETCH(const QStringView, unicode); QFETCH(const QLatin1String, latin1); QFETCH(const int, n); - QFETCH(const QStringView, result); + QFETCH(const QAnyStringView, result); const auto utf8 = unicode.toUtf8(); @@ -1787,15 +1801,15 @@ void tst_QStringApiSymmetry::chop_data() QTest::addColumn("unicode"); QTest::addColumn("latin1"); QTest::addColumn("n"); - QTest::addColumn("result"); + QTest::addColumn("result"); // QTest::addRow("null") << QStringView() << QLatin1String() << 0 << QStringView(); - QTest::addRow("empty") << QStringView(empty) << QLatin1String("") << 0 << QStringView(empty); + QTest::addRow("empty") << QStringView(empty) << QLatin1String("") << 0 << QAnyStringView(empty); // Some classes' truncate() implementations have a wide contract, others a narrow one // so only test valid arguents here: #define ROW(base, n, res) \ - QTest::addRow("%s%d", #base, n) << QStringView(base) << QLatin1String(#base) << n << QStringView(res); + QTest::addRow("%s%d", #base, n) << QStringView(base) << QLatin1String(#base) << n << QAnyStringView(res); ROW(a, 0, a); ROW(a, 1, empty); @@ -1817,7 +1831,7 @@ void tst_QStringApiSymmetry::chop_impl() QFETCH(const QStringView, unicode); QFETCH(const QLatin1String, latin1); QFETCH(const int, n); - QFETCH(const QStringView, result); + QFETCH(const QAnyStringView, result); const auto utf8 = unicode.toUtf8(); @@ -1850,11 +1864,11 @@ void tst_QStringApiSymmetry::chop_impl() void tst_QStringApiSymmetry::trimmed_data() { QTest::addColumn("unicode"); - QTest::addColumn("result"); + QTest::addColumn("result"); const auto latin1Whitespace = QLatin1String(" \r\n\t\f\v"); - QTest::addRow("null") << QString() << QStringView(); + QTest::addRow("null") << QString() << QAnyStringView(); auto add = [latin1Whitespace](const QString &str) { // run through all substrings of latin1Whitespace @@ -1862,7 +1876,7 @@ void tst_QStringApiSymmetry::trimmed_data() for (int pos = 0; pos < latin1Whitespace.size() - len; ++pos) { const QString unicode = latin1Whitespace.mid(pos, len) + str + latin1Whitespace.mid(pos, len); const QScopedArrayPointer escaped(QTest::toString(unicode)); - QTest::addRow("%s", escaped.data()) << unicode << QStringView(str); + QTest::addRow("%s", escaped.data()) << unicode << QAnyStringView(str); } } }; @@ -1876,7 +1890,7 @@ template void tst_QStringApiSymmetry::trimmed_impl() { QFETCH(const QString, unicode); - QFETCH(const QStringView, result); + QFETCH(const QAnyStringView, result); const auto utf8 = unicode.toUtf8(); const auto l1s = unicode.toLatin1();