From 96d67da420697cee10bdc537a1a592f6f22e2b8f Mon Sep 17 00:00:00 2001 From: Giuseppe D'Angelo Date: Sun, 21 May 2023 13:38:09 +0200 Subject: [PATCH] String-like containers: add implicit conversions towards std:: string views In C++20 std::basic_string_view has gained a range constructor (like QStringView always had), but that range constructor has been made explicit. This means we can't just pass a QString(View) to a function taking a u16string_view. The consensus seems to be that that types that should implictly convert towards stdlib's string views should do that via implicit conversion operators. This patch adds them for * QByteArrayView => std::string_view * QString(View) => std::u16string_view * QUtf8StringView => std::string_view or std::u8string_view, depending on the storage_type QLatin1StringView doesn't have a matching std:: view so I'm not enabling its conversion. QByteArray poses a challenge, in that it already defines a conversion towards const char *. (One can disable that conversion with a macro.) That conversion makes it impossible to support: QByteArray ba; std::string_view sv1(ba); // 1 std::string_view sv2 = ba; // 2 because: * if only operator const char *() is defined, then (2) doesn't work (situation right now); * if both conversions to const char * and string_view are defined, then (1) is ambiguous on certain compilers (MSVC, QCC). Interestingly enough, not on GCC/Clang, but only in C++17 and later modes. I can't kill the conversion towards const char * (API break, and we use it *everywhere* in Qt), hence, QByteArray does not get the implicit conversion, at least not in this patch. [ChangeLog][QtCore][QByteArrayView] Added an implicit conversion operator towards std::string_view. [ChangeLog][QtCore][QString] Added an implicit conversion operator towards std::u16string_view. [ChangeLog][QtCore][QStringView] Added an implicit conversion operator towards std::u16string_view. [ChangeLog][QtCore][QUtf8StringView] Added an implicit conversion operator towards std::string_view (QUtf8StringView is using char as its storage type in Qt 6). Note that QUtf8StringView is planned to use char8_t in Qt 7, therefore it is expected that the conversion will change towards std::u8string_view in Qt 7. Change-Id: I6d3b64d211a386241ae157765cd1b03f531f909a Reviewed-by: Thiago Macieira --- src/corelib/text/qbytearrayview.h | 3 +++ src/corelib/text/qbytearrayview.qdoc | 9 +++++++ src/corelib/text/qstring.cpp | 6 +++++ src/corelib/text/qstring.h | 7 +++++ src/corelib/text/qstringview.cpp | 9 +++++++ src/corelib/text/qstringview.h | 3 +++ src/corelib/text/qutf8stringview.h | 3 +++ src/corelib/text/qutf8stringview.qdoc | 10 +++++++ .../qbytearrayview/tst_qbytearrayview.cpp | 26 ++++++++++++++++++ .../auto/corelib/text/qstring/tst_qstring.cpp | 27 +++++++++++++++++++ .../text/qstringview/tst_qstringview.cpp | 27 +++++++++++++++++++ 11 files changed, 130 insertions(+) diff --git a/src/corelib/text/qbytearrayview.h b/src/corelib/text/qbytearrayview.h index f822a2ca202..a056c7836e5 100644 --- a/src/corelib/text/qbytearrayview.h +++ b/src/corelib/text/qbytearrayview.h @@ -301,6 +301,9 @@ public: [[nodiscard]] constexpr char front() const { Q_ASSERT(!empty()); return m_data[0]; } [[nodiscard]] constexpr char back() const { Q_ASSERT(!empty()); return m_data[m_size - 1]; } + [[nodiscard]] Q_IMPLICIT operator std::string_view() const noexcept + { return std::string_view(m_data, size_t(m_size)); } + // // Qt compatibility API: // diff --git a/src/corelib/text/qbytearrayview.qdoc b/src/corelib/text/qbytearrayview.qdoc index 48013f86619..60c68024b44 100644 --- a/src/corelib/text/qbytearrayview.qdoc +++ b/src/corelib/text/qbytearrayview.qdoc @@ -1032,3 +1032,12 @@ \sa QByteArray::isNull(), QByteArrayView */ + +/*! + \fn QByteArrayView::operator std::string_view() const + \since 6.7 + + Converts this QByteArrayView object to a \c{std::string_view} object. + The returned view will have the same data pointer and length of + this view. +*/ diff --git a/src/corelib/text/qstring.cpp b/src/corelib/text/qstring.cpp index 26dad6b7792..5a0a9102b32 100644 --- a/src/corelib/text/qstring.cpp +++ b/src/corelib/text/qstring.cpp @@ -2614,6 +2614,12 @@ QString::QString(QChar ch) \internal */ +/*! \fn QString::operator std::u16string_view() const + \since 6.7 + + Converts this QString object to a \c{std::u16string_view} object. +*/ + static bool needsReallocate(const QString &str, qsizetype newSize) { const auto capacityAtEnd = str.capacity() - str.data_ptr().freeSpaceAtBegin(); diff --git a/src/corelib/text/qstring.h b/src/corelib/text/qstring.h index e050ec356e4..602bbb3e972 100644 --- a/src/corelib/text/qstring.h +++ b/src/corelib/text/qstring.h @@ -883,6 +883,8 @@ public: static inline QString fromStdU32String(const std::u32string &s); inline std::u32string toStdU32String() const; + Q_IMPLICIT inline operator std::u16string_view() const noexcept; + #if defined(Q_OS_DARWIN) || defined(Q_QDOC) static QString fromCFString(CFStringRef string); CFStringRef toCFString() const Q_DECL_CF_RETURNS_RETAINED; @@ -1351,6 +1353,11 @@ inline std::u32string QString::toStdU32String() const return u32str; } +QString::operator std::u16string_view() const noexcept +{ + return std::u16string_view(d.data(), size_t(d.size)); +} + #if !defined(QT_NO_DATASTREAM) || defined(QT_BOOTSTRAPPED) Q_CORE_EXPORT QDataStream &operator<<(QDataStream &, const QString &); Q_CORE_EXPORT QDataStream &operator>>(QDataStream &, QString &); diff --git a/src/corelib/text/qstringview.cpp b/src/corelib/text/qstringview.cpp index 302637eebf8..226ab4da9cd 100644 --- a/src/corelib/text/qstringview.cpp +++ b/src/corelib/text/qstringview.cpp @@ -1411,4 +1411,13 @@ or the character \a ch \sa QStringTokenizer, qTokenize() */ +/*! + \fn QStringView::operator std::u16string_view() const + \since 6.7 + + Converts this QStringView object to a \c{std::u16string_view} object. + The returned view will have the same data pointer and length of + this view. +*/ + QT_END_NAMESPACE diff --git a/src/corelib/text/qstringview.h b/src/corelib/text/qstringview.h index a6f217d2a50..69694db0c88 100644 --- a/src/corelib/text/qstringview.h +++ b/src/corelib/text/qstringview.h @@ -400,6 +400,9 @@ public: [[nodiscard]] constexpr QChar front() const { return Q_ASSERT(!empty()), QChar(m_data[0]); } [[nodiscard]] constexpr QChar back() const { return Q_ASSERT(!empty()), QChar(m_data[m_size - 1]); } + [[nodiscard]] Q_IMPLICIT operator std::u16string_view() const noexcept + { return std::u16string_view(m_data, size_t(m_size)); } + // // Qt compatibility API: // diff --git a/src/corelib/text/qutf8stringview.h b/src/corelib/text/qutf8stringview.h index 9d7f01b7084..5efb45abe98 100644 --- a/src/corelib/text/qutf8stringview.h +++ b/src/corelib/text/qutf8stringview.h @@ -274,6 +274,9 @@ public: [[nodiscard]] constexpr storage_type front() const { return Q_ASSERT(!empty()), m_data[0]; } [[nodiscard]] constexpr storage_type back() const { return Q_ASSERT(!empty()), m_data[m_size - 1]; } + [[nodiscard]] Q_IMPLICIT operator std::basic_string_view() const noexcept + { return std::basic_string_view(data(), size_t(size())); } + // // Qt compatibility API: // diff --git a/src/corelib/text/qutf8stringview.qdoc b/src/corelib/text/qutf8stringview.qdoc index e7c1daaca7b..60d0d7cd1df 100644 --- a/src/corelib/text/qutf8stringview.qdoc +++ b/src/corelib/text/qutf8stringview.qdoc @@ -695,3 +695,13 @@ \sa QByteArray::isNull(), QUtf8StringView */ + + +/*! \fn QUtf8StringView::operator std::basic_string_view() const + \since 6.7 + + Converts this QUtf8StringView object to a + \c{std::basic_string_view} object. The returned view will have the + same data pointer and length of this view. The character type of + the returned view will be \c{storage_type}. +*/ diff --git a/tests/auto/corelib/text/qbytearrayview/tst_qbytearrayview.cpp b/tests/auto/corelib/text/qbytearrayview/tst_qbytearrayview.cpp index 1a777502460..e2842e08d6d 100644 --- a/tests/auto/corelib/text/qbytearrayview/tst_qbytearrayview.cpp +++ b/tests/auto/corelib/text/qbytearrayview/tst_qbytearrayview.cpp @@ -171,6 +171,7 @@ private slots: void comparison() const; void compare() const; + void std_stringview_conversion(); private: template @@ -632,5 +633,30 @@ void tst_QByteArrayView::compare() const QVERIFY(alpha.compare(beta, Qt::CaseSensitive) > 0); } +void tst_QByteArrayView::std_stringview_conversion() +{ + static_assert(std::is_convertible_v); + + QByteArrayView bav; + std::string_view sv(bav); + QCOMPARE(sv, std::string_view()); + + bav = ""; + sv = bav; + QCOMPARE(bav.size(), 0); + QCOMPARE(sv.size(), size_t(0)); + QCOMPARE(sv, std::string_view()); + + bav = "Hello"; + sv = bav; + QCOMPARE(sv, std::string_view("Hello")); + + bav = QByteArrayView::fromArray("Hello\0world"); + sv = bav; + QCOMPARE(bav.size(), 12); + QCOMPARE(sv.size(), size_t(12)); + QCOMPARE(sv, std::string_view("Hello\0world", 12)); +} + QTEST_APPLESS_MAIN(tst_QByteArrayView) #include "tst_qbytearrayview.moc" diff --git a/tests/auto/corelib/text/qstring/tst_qstring.cpp b/tests/auto/corelib/text/qstring/tst_qstring.cpp index eaf35c969ea..010216e7874 100644 --- a/tests/auto/corelib/text/qstring/tst_qstring.cpp +++ b/tests/auto/corelib/text/qstring/tst_qstring.cpp @@ -696,6 +696,8 @@ private slots: void sliced(); void chopped(); void removeIf(); + + void std_stringview_conversion(); }; Q_DECLARE_OPERATORS_FOR_FLAGS(tst_QString::DataOptions) @@ -8762,6 +8764,31 @@ void tst_QString::removeIf() QCOMPARE(a.removeIf(removeA), u"BcbC"); } +void tst_QString::std_stringview_conversion() +{ + static_assert(std::is_convertible_v); + + QString s; + std::u16string_view sv(s); + QCOMPARE(sv, std::u16string_view()); + + s = u""_s; + sv = s; + QCOMPARE(s.size(), 0); + QCOMPARE(sv.size(), size_t(0)); + QCOMPARE(sv, std::u16string_view()); + + s = u"Hello"_s; + sv = s; + QCOMPARE(sv, std::u16string_view(u"Hello")); + + s = u"Hello\0world"_s; + sv = s; + QCOMPARE(s.size(), 11); + QCOMPARE(sv.size(), size_t(11)); + QCOMPARE(sv, std::u16string_view(u"Hello\0world", 11)); +} + // QString's collation order is only supported during the lifetime as QCoreApplication QTEST_GUILESS_MAIN(tst_QString) diff --git a/tests/auto/corelib/text/qstringview/tst_qstringview.cpp b/tests/auto/corelib/text/qstringview/tst_qstringview.cpp index f510e9a8227..e0564ac671a 100644 --- a/tests/auto/corelib/text/qstringview/tst_qstringview.cpp +++ b/tests/auto/corelib/text/qstringview/tst_qstringview.cpp @@ -258,6 +258,8 @@ private Q_SLOTS: void tokenize_data() const; void tokenize() const; + void std_stringview_conversion(); + private: template void conversion_tests(String arg) const; @@ -879,5 +881,30 @@ void tst_QStringView::overloadResolution() } } +void tst_QStringView::std_stringview_conversion() +{ + static_assert(std::is_convertible_v); + + QStringView s; + std::u16string_view sv(s); + QCOMPARE(sv, std::u16string_view()); + + s = u""; + sv = s; + QCOMPARE(s.size(), 0); + QCOMPARE(sv.size(), size_t(0)); + QCOMPARE(sv, std::u16string_view()); + + s = u"Hello"; + sv = s; + QCOMPARE(sv, std::u16string_view(u"Hello")); + + s = QStringView::fromArray(u"Hello\0world"); + sv = s; + QCOMPARE(s.size(), 12); + QCOMPARE(sv.size(), size_t(12)); + QCOMPARE(sv, std::u16string_view(u"Hello\0world\0", 12)); +} + QTEST_APPLESS_MAIN(tst_QStringView) #include "tst_qstringview.moc"