From b2b58624791cde5c2b24b9397f02ccad5b394ea6 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Wed, 28 Jun 2023 15:43:02 +0200 Subject: [PATCH] QAnyStringView: add QDebug stream operator When QDebug::quoted(), indicates the encoding using the u/u8 prefixes or the _L1 suffix. This is information that might come in handy, and we plan to make it off-switchable (QTBUG-114936). The default should be true, though, for QAnyStringView, because we should confront users with this feature so they learn it exists. For concrete view types, changing the default behavior is probably not a good idea. [ChangeLog][QtCore][QAnyStringView/QDebug] Can now stream QAnyStringView into QDebug. Fixes: QTBUG-114935 Change-Id: Icd5bf700c8b7958e942468b54248487998f262d5 Reviewed-by: Thiago Macieira --- src/corelib/CMakeLists.txt | 2 +- ...qanystringview.qdoc => qanystringview.cpp} | 50 +++++++++++++ src/corelib/text/qanystringview.h | 4 + .../qanystringview/tst_qanystringview.cpp | 73 +++++++++++++++++++ 4 files changed, 128 insertions(+), 1 deletion(-) rename src/corelib/text/{qanystringview.qdoc => qanystringview.cpp} (93%) 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