diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt index 5b331bd7d0..5ce46ba086 100644 --- a/src/corelib/CMakeLists.txt +++ b/src/corelib/CMakeLists.txt @@ -206,7 +206,7 @@ qt_internal_add_module(Core serialization/qjsonwriter.cpp serialization/qjsonwriter_p.h serialization/qtextstream.cpp serialization/qtextstream.h serialization/qtextstream_p.h serialization/qxmlutils.cpp serialization/qxmlutils_p.h - text/qanystringview.h + text/qanystringview.cpp text/qanystringview.h text/qbytearray.cpp text/qbytearray.h text/qbytearray_p.h text/qbytearrayalgorithms.h text/qbytearraylist.cpp text/qbytearraylist.h diff --git a/src/corelib/text/qanystringview.qdoc b/src/corelib/text/qanystringview.cpp similarity index 93% rename from src/corelib/text/qanystringview.qdoc rename to src/corelib/text/qanystringview.cpp index 0fb68f182a..73c01d9718 100644 --- a/src/corelib/text/qanystringview.qdoc +++ b/src/corelib/text/qanystringview.cpp @@ -1,6 +1,12 @@ // Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only +#include "qanystringview.h" +#include "qdebug.h" +#include "qttypetraits.h" + +QT_BEGIN_NAMESPACE + /*! \class QAnyStringView \inmodule QtCore @@ -597,3 +603,47 @@ \sa QString::isNull(), QAnyStringView */ +/*! + \fn QAnyStringView::operator<<(QDebug d, QAnyStringView s) + \since 6.7 + \relates QAnyStringView + \relatesalso QDebug + + Outputs \a s to debug stream \a d. + + If \c{d.quotedString()} is \c true, indicates which encoding the string is + in. If you just want the string data, use visit() like this: + + \code + s.visit([&d) (auto s) { d << s; }); + \endcode + + \sa QAnyStringView::visit() +*/ +QDebug operator<<(QDebug d, QAnyStringView s) +{ + struct S { const char *prefix, *suffix; }; + const auto affixes = s.visit([](auto s) { + using View = decltype(s); + if constexpr (std::is_same_v) { + return S{"", "_L1"}; + } else if constexpr (std::is_same_v) { + return S{"u8", ""}; + } else if constexpr (std::is_same_v) { + return S{"u", ""}; + } else { + static_assert(QtPrivate::type_dependent_false()); + } + }); + const QDebugStateSaver saver(d); + d.nospace(); + if (d.quoteStrings()) + d << affixes.prefix; + s.visit([&d](auto s) { d << s; }); + if (d.quoteStrings()) + d << affixes.suffix; + return d; +} + + +QT_END_NAMESPACE diff --git a/src/corelib/text/qanystringview.h b/src/corelib/text/qanystringview.h index 2be79c2b52..ba5a179caf 100644 --- a/src/corelib/text/qanystringview.h +++ b/src/corelib/text/qanystringview.h @@ -312,6 +312,10 @@ private: { return QAnyStringView::compare(lhs, rhs) > 0; } #endif +#ifndef QT_NO_DEBUG_STREAM + Q_CORE_EXPORT friend QDebug operator<<(QDebug d, QAnyStringView s); +#endif + [[nodiscard]] constexpr Tag tag() const noexcept { return Tag{m_size & TypeMask}; } [[nodiscard]] constexpr bool isUtf16() const noexcept { return tag() == Tag::Utf16; } [[nodiscard]] constexpr bool isUtf8() const noexcept { return tag() == Tag::Utf8; } diff --git a/tests/auto/corelib/text/qanystringview/tst_qanystringview.cpp b/tests/auto/corelib/text/qanystringview/tst_qanystringview.cpp index 52d1042769..95beec4321 100644 --- a/tests/auto/corelib/text/qanystringview/tst_qanystringview.cpp +++ b/tests/auto/corelib/text/qanystringview/tst_qanystringview.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -304,6 +305,7 @@ class tst_QAnyStringView : public QObject private Q_SLOTS: void constExpr() const; void basics() const; + void debug() const; void asciiLiteralIsLatin1() const; void fromQString() const { fromQStringOrByteArray(); } @@ -476,6 +478,77 @@ void tst_QAnyStringView::basics() const QVERIFY(!(sv2 != sv1)); } +void tst_QAnyStringView::debug() const +{ + #ifdef QT_SUPPORTS_IS_CONSTANT_EVALUATED + # define MAYBE_L1(str) str "_L1" + # define VERIFY_L1(s) QVERIFY(s.isLatin1()) + #else + # define MAYBE_L1(str) "u8" str + # define VERIFY_L1(s) QVERIFY(s.isUtf8()) + #endif + #define CHECK1(s, mod, expected) do { \ + QString result; \ + QDebug(&result) mod << "X"_L1 << s << "Y"_L1; \ + /* QDebug appends an eager ' ', so trim before comparison */ \ + /* We use X and Y affixes so we can still check spacing */ \ + /* around the QAnyStringView itself. */ \ + QCOMPARE(result.trimmed(), expected); \ + } while (false) + #define CHECK(init, esq, eq, es, e) do { \ + QAnyStringView s = init; \ + CHECK1(s, , esq); \ + CHECK1(s, .nospace(), eq); \ + CHECK1(s, .noquote(), es); \ + CHECK1(s, .nospace().noquote(), e); \ + } while (false) + + CHECK(nullptr, + R"("X" u8"" "Y")", + R"("X"u8"""Y")", + R"(X Y)", + R"(XY)"); + CHECK(QLatin1StringView(nullptr), + R"("X" ""_L1 "Y")", + R"("X"""_L1"Y")", + R"(X Y)", + R"(XY)"); + CHECK(QUtf8StringView(nullptr), + R"("X" u8"" "Y")", + R"("X"u8"""Y")", + R"(X Y)", + R"(XY)"); + CHECK(QStringView(nullptr), + R"("X" u"" "Y")", + R"("X"u"""Y")", + R"(X Y)", + R"(XY)"); + { + constexpr QAnyStringView asv = "hello"; + VERIFY_L1(asv); // ### fails when asv isn't constexpr + CHECK(asv, + R"("X" )" MAYBE_L1(R"("hello")") R"( "Y")", + R"("X")" MAYBE_L1(R"("hello")") R"("Y")", + R"(X hello Y)", + R"(XhelloY)"); + } + CHECK(u8"hällo", + R"("X" u8"h\xC3\xA4llo" "Y")", + R"("X"u8"h\xC3\xA4llo""Y")", + R"(X hällo Y)", + R"(XhälloY)"); + CHECK(u"hällo", + R"("X" u"hällo" "Y")", + R"("X"u"hällo""Y")", + R"(X hällo Y)", + R"(XhälloY)"); + + #undef CHECK + #undef CHECK1 + #undef VERIFY_L1 + #undef MAYBE_L1 +} + void tst_QAnyStringView::asciiLiteralIsLatin1() const { #ifdef QT_SUPPORTS_IS_CONSTANT_EVALUATED