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 <thiago.macieira@intel.com>
bb10
Marc Mutz 2023-06-28 15:43:02 +02:00
parent 4443d392e4
commit b2b5862479
4 changed files with 128 additions and 1 deletions

View File

@ -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

View File

@ -1,6 +1,12 @@
// Copyright (C) 2020 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com>
// 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<View, QLatin1StringView>) {
return S{"", "_L1"};
} else if constexpr (std::is_same_v<View, QUtf8StringView>) {
return S{"u8", ""};
} else if constexpr (std::is_same_v<View, QStringView>) {
return S{"u", ""};
} else {
static_assert(QtPrivate::type_dependent_false<View>());
}
});
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

View File

@ -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; }

View File

@ -3,6 +3,7 @@
#include <QAnyStringView>
#include <QChar>
#include <QDebug>
#include <QList>
#include <QString>
#include <QStringBuilder>
@ -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<QString>(); }
@ -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