QDebug: fix std::string/view stream operators; add u8 versions

Make them templates, for two reasons:

- so they can accept std::pmr types and, in general, any basic_string
  with custom allocators and char_traits

- to break overload ambiguities with the Qt string view types

Also, add the missing C++20 char8_t overloads.

Also, avoid creation of a QString in the sizeof(wchar_t) == 2 case
(Windows). Add a comment to optimize for the sizeof(wchar_t) != 2 case
later.

Found in API review.

Pick-to: 6.5
Change-Id: I30139520f582a38863a0936f8eca4b1ed33e37c8
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
Marc Mutz 2023-03-01 14:42:32 +01:00
parent a84b2e5973
commit 52c7f699ac
3 changed files with 141 additions and 81 deletions

View File

@ -765,75 +765,20 @@ QDebug &QDebug::resetFormat()
/*!
\since 6.5
\fn QDebug &QDebug::operator<<(const std::string &s)
\fn template <typename Char, typename...Args> QDebug &QDebug::operator<<(const std::basic_string<Char, Args...> &s)
\fn template <typename Char, typename...Args> QDebug &QDebug::operator<<(std::basic_string_view<Char, Args...> s)
Converts \a s to a QUtf8StringView,
writes the result to the stream and returns
a reference to the stream.
*/
Writes the string or string-view \a s to the stream and returns a reference
to the stream.
/*!
\since 6.5
\fn QDebug &QDebug::operator<<(std::string_view s)
Converts \a s to a QUtf8StringView,
writes the result to the stream and returns
a reference to the stream.
*/
/*!
\since 6.5
\fn QDebug &QDebug::operator<<(const std::wstring &s)
Converts \a s to a QString via QString::fromStdWString(),
writes the result to the stream and returns
a reference to the stream.
*/
/*!
\since 6.5
\fn QDebug &QDebug::operator<<(std::wstring_view s)
Converts \a s to a QString via QString::fromWCharArray(),
writes the result to the stream and returns
a reference to the stream.
*/
/*!
\since 6.5
\fn QDebug &QDebug::operator<<(const std::u16string &s)
Converts \a s to a QStringView,
writes the result to the stream and returns
a reference to the stream.
*/
/*!
\since 6.5
\fn QDebug &QDebug::operator<<(std::u16string_view s)
Converts \a s to a QStringView,
writes the result to the stream and returns
a reference to the stream.
*/
/*!
\since 6.5
\fn QDebug &QDebug::operator<<(const std::u32string &s)
Converts \a s to a QString via QString::fromUcs4(),
writes the result to the stream and returns
a reference to the stream.
*/
/*!
\since 6.5
\fn QDebug &QDebug::operator<<(std::u32string_view s)
Converts \a s to a QString via QString::fromUcs4(),
writes the result to the stream and returns
a reference to the stream.
These operators only participate in overload resolution if \c Char is one of
\list
\li char
\li char8_t (C++20 only)
\li char16_t
\li char32_t
\li wchar_t
\endlist
*/
/*!

View File

@ -129,29 +129,65 @@ public:
inline QDebug &operator<<(QTextStreamManipulator m)
{ stream->ts << m; return *this; }
inline QDebug &operator<<(const std::string &s)
#ifdef Q_QDOC
template <typename Char, typename...Args>
QDebug &operator<<(const std::basic_string<Char, Args...> &s);
template <typename Char, typename...Args>
QDebug &operator<<(std::basic_string_view<Char, Args...> s);
#else
template <typename...Args>
QDebug &operator<<(const std::basic_string<char, Args...> &s)
{ return *this << QUtf8StringView(s); }
inline QDebug &operator<<(std::string_view s)
template <typename...Args>
QDebug &operator<<(std::basic_string_view<char, Args...> s)
{ return *this << QUtf8StringView(s); }
inline QDebug &operator<<(const std::wstring &s)
{ return *this << QString::fromStdWString(s); }
#ifdef __cpp_char8_t
template <typename...Args>
QDebug &operator<<(const std::basic_string<char8_t, Args...> &s)
{ return *this << QUtf8StringView(s); }
inline QDebug &operator<<(std::wstring_view s)
{ return *this << QString::fromWCharArray(s.data(), s.size()); }
template <typename...Args>
QDebug &operator<<(std::basic_string_view<char8_t, Args...> s)
{ return *this << QUtf8StringView(s); }
#endif // __cpp_char8_t
inline QDebug &operator<<(const std::u16string &s)
template <typename...Args>
QDebug &operator<<(const std::basic_string<char16_t, Args...> &s)
{ return *this << QStringView(s); }
inline QDebug &operator<<(std::u16string_view s)
template <typename...Args>
QDebug &operator<<(std::basic_string_view<char16_t, Args...> s)
{ return *this << QStringView(s); }
inline QDebug &operator<<(const std::u32string &s)
template <typename...Args>
QDebug &operator<<(const std::basic_string<wchar_t, Args...> &s)
{
if constexpr (sizeof(wchar_t) == 2)
return *this << QStringView(s);
else
return *this << QString::fromWCharArray(s.data(), s.size()); // ### optimize
}
template <typename...Args>
QDebug &operator<<(std::basic_string_view<wchar_t, Args...> s)
{
if constexpr (sizeof(wchar_t) == 2)
return *this << QStringView(s);
else
return *this << QString::fromWCharArray(s.data(), s.size()); // ### optimize
}
template <typename...Args>
QDebug &operator<<(const std::basic_string<char32_t, Args...> &s)
{ return *this << QString::fromUcs4(s.data(), s.size()); }
inline QDebug &operator<<(std::u32string_view s)
template <typename...Args>
QDebug &operator<<(std::basic_string_view<char32_t, Args...> s)
{ return *this << QString::fromUcs4(s.data(), s.size()); }
#endif // !Q_QDOC
template <typename T>
static QString toString(T &&object)

View File

@ -16,6 +16,13 @@
#include <QMimeDatabase>
#include <QMetaType>
#ifdef __cpp_lib_memory_resource
# include <memory_resource>
namespace pmr = std::pmr;
#else
namespace pmr = std;
#endif
using namespace Qt::StringLiterals;
static_assert(QTypeTraits::has_ostream_operator_v<QDebug, int>);
@ -67,6 +74,8 @@ private slots:
void qDebugStdStringView() const;
void qDebugStdWString() const;
void qDebugStdWStringView() const;
void qDebugStdU8String() const;
void qDebugStdU8StringView() const;
void qDebugStdU16String() const;
void qDebugStdU16StringView() const;
void qDebugStdU32String() const;
@ -663,7 +672,7 @@ void tst_QDebug::qDebugStdString() const
MessageHandlerSetter mhs(myMessageHandler);
{
QDebug d = qDebug();
d << std::string("foo") << std::string("") << std::string("barbaz", 3);
d << pmr::string("foo") << std::string("") << std::string("barbaz", 3);
d.nospace().noquote() << std::string("baz");
}
#ifndef QT_NO_MESSAGELOGCONTEXT
@ -725,7 +734,7 @@ void tst_QDebug::qDebugStdWString() const
MessageHandlerSetter mhs(myMessageHandler);
{
QDebug d = qDebug();
d << std::wstring(L"foo") << std::wstring(L"") << std::wstring(L"barbaz", 3);
d << pmr::wstring(L"foo") << std::wstring(L"") << std::wstring(L"barbaz", 3);
d.nospace().noquote() << std::wstring(L"baz");
}
#ifndef QT_NO_MESSAGELOGCONTEXT
@ -780,6 +789,76 @@ void tst_QDebug::qDebugStdWStringView() const
QCOMPARE(s_msg, " " + QString::fromStdWString(std::wstring(string)));
}
void tst_QDebug::qDebugStdU8String() const
{
#ifndef __cpp_lib_char8_t
QSKIP("This test requires C++20 char8_t support enabled in the compiler.");
#else
QString file, function;
int line = 0;
MessageHandlerSetter mhs(myMessageHandler);
{
QDebug d = qDebug();
d << pmr::u8string(u8"foo") << std::u8string(u8"") << std::u8string(u8"barbaz", 3);
d.nospace().noquote() << std::u8string(u8"baz");
}
#ifndef QT_NO_MESSAGELOGCONTEXT
file = __FILE__; line = __LINE__ - 5; function = Q_FUNC_INFO;
#endif
QCOMPARE(s_msgType, QtDebugMsg);
QCOMPARE(s_msg, QString::fromLatin1("\"foo\" \"\" \"bar\" baz"));
QCOMPARE(QString::fromLatin1(s_file), file);
QCOMPARE(s_line, line);
QCOMPARE(QString::fromLatin1(s_function), function);
/* simpler tests from now on */
std::u8string string(u8"\"Hello\"");
qDebug() << string;
QCOMPARE(s_msg, QString("\"\\\"Hello\\\"\""));
qDebug().noquote().nospace() << string;
QCOMPARE(s_msg, QUtf8StringView(string).toString());
qDebug().noquote().nospace() << qSetFieldWidth(8) << string;
QCOMPARE(s_msg, " " + QUtf8StringView(string).toString());
#endif // __cpp_lib_char8_t
}
void tst_QDebug::qDebugStdU8StringView() const
{
#ifndef __cpp_lib_char8_t
QSKIP("This test requires C++20 char8_t support enabled in the compiler.");
#else
QString file, function;
int line = 0;
MessageHandlerSetter mhs(myMessageHandler);
{
QDebug d = qDebug();
d << std::u16string_view(u"foo") << std::u16string_view(u"") << std::u16string_view(u"barbaz", 3);
d.nospace().noquote() << std::u16string_view(u"baz");
}
#ifndef QT_NO_MESSAGELOGCONTEXT
file = __FILE__; line = __LINE__ - 5; function = Q_FUNC_INFO;
#endif
QCOMPARE(s_msgType, QtDebugMsg);
QCOMPARE(s_msg, QString::fromLatin1("\"foo\" \"\" \"bar\" baz"));
QCOMPARE(QString::fromLatin1(s_file), file);
QCOMPARE(s_line, line);
QCOMPARE(QString::fromLatin1(s_function), function);
/* simpler tests from now on */
std::u16string_view string(u"\"Hello\"");
qDebug() << string;
QCOMPARE(s_msg, QString("\"\\\"Hello\\\"\""));
qDebug().noquote().nospace() << string;
QCOMPARE(s_msg, QString::fromStdU16String(std::u16string(string)));
qDebug().noquote().nospace() << qSetFieldWidth(8) << string;
QCOMPARE(s_msg, " " + QString::fromStdU16String(std::u16string(string)));
#endif // __cpp_lib_char8_t
}
void tst_QDebug::qDebugStdU16String() const
{
QString file, function;
@ -787,7 +866,7 @@ void tst_QDebug::qDebugStdU16String() const
MessageHandlerSetter mhs(myMessageHandler);
{
QDebug d = qDebug();
d << std::u16string(u"foo") << std::u16string(u"") << std::u16string(u"barbaz", 3);
d << pmr::u16string(u"foo") << std::u16string(u"") << std::u16string(u"barbaz", 3);
d.nospace().noquote() << std::u16string(u"baz");
}
#ifndef QT_NO_MESSAGELOGCONTEXT
@ -849,7 +928,7 @@ void tst_QDebug::qDebugStdU32String() const
MessageHandlerSetter mhs(myMessageHandler);
{
QDebug d = qDebug();
d << std::u32string(U"foo") << std::u32string(U"") << std::u32string(U"barbaz", 3);
d << pmr::u32string(U"foo") << std::u32string(U"") << std::u32string(U"barbaz", 3);
d.nospace().noquote() << std::u32string(U"baz");
}
#ifndef QT_NO_MESSAGELOGCONTEXT