summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarc Mutz <marc.mutz@qt.io>2023-06-02 16:22:37 +0200
committerMarc Mutz <marc.mutz@qt.io>2023-06-07 23:18:03 +0000
commitf5ed163c19c4a165a61e6fbfdaf5ee39b5587a0c (patch)
treea8ab795999c4e6ec4bdc91ca26167564f6433cb6
parent245c2b621f5942861b7f827bfc8a859b9efb9b72 (diff)
QString: add STL-style assign() [2/4]: (it,it) overload for QChar-convertible *it
Restrict the permissible value_types to those QStringView can take, plus QLatin1Char. All of these implicitly convert to QChar and give the correct result, even when converted char-by-char. Task-number: QTBUG-106198 Pick-to: 6.6 Change-Id: Icb44244cb08af391161c4309467d4e0d2d3d3d62 Reviewed-by: Ivan Solovev <ivan.solovev@qt.io> Reviewed-by: Dennis Oberst <dennis.oberst@qt.io>
-rw-r--r--src/corelib/text/qstring.cpp28
-rw-r--r--src/corelib/text/qstring.h48
-rw-r--r--tests/auto/corelib/text/qstring/tst_qstring.cpp99
-rw-r--r--tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp2
4 files changed, 177 insertions, 0 deletions
diff --git a/src/corelib/text/qstring.cpp b/src/corelib/text/qstring.cpp
index de1409960a..ae6e6f67eb 100644
--- a/src/corelib/text/qstring.cpp
+++ b/src/corelib/text/qstring.cpp
@@ -3339,6 +3339,34 @@ QString &QString::append(QChar ch)
\sa fill()
*/
+/*!
+ \fn template <typename InputIterator, if_compatible_iterator<InputIterator>> QString &QString::assign(InputIterator first, InputIterator last)
+ \since 6.6
+
+ Replaces the contents of this string with a copy of the elements in the
+ iterator range [\a first, \a last) and returns a reference to this string.
+
+ The size of this string will be equal to the number of elements in the
+ range [\a first, \a last).
+
+ This function will only allocate memory if the number of elements in the
+ range exceeds the capacity of this string or this string is shared.
+
+ \note This function overload only participates in overload resolution if
+ \c InputIterator meets the requirements of a
+ \l {https://en.cppreference.com/w/cpp/named_req/InputIterator} {LegacyInputIterator}
+ and the \c{value_type} of \c InputIterator is one of the following character types:
+ \list
+ \li QChar
+ \li QLatin1Char
+ \li \c char16_t
+ \li (on platforms, such as Windows, where it is a 16-bit type) \c wchar_t
+ \endlist
+
+ \note The behavior is undefined if either argument is an iterator into *this or
+ [\a first, \a last) is not a valid range.
+*/
+
QString &QString::assign(QAnyStringView s)
{
if (s.size() <= capacity() && isDetached()) {
diff --git a/src/corelib/text/qstring.h b/src/corelib/text/qstring.h
index 71b712c3e9..76833147d4 100644
--- a/src/corelib/text/qstring.h
+++ b/src/corelib/text/qstring.h
@@ -24,6 +24,7 @@
#include <string>
#include <iterator>
+#include <QtCore/q20memory.h>
#include <stdarg.h>
@@ -120,6 +121,33 @@ class Q_CORE_EXPORT QString
typedef QTypedArrayData<char16_t> Data;
friend class ::tst_QString;
+
+ template <typename Iterator>
+ static constexpr bool is_contiguous_iterator_v =
+ // Can't use contiguous_iterator_tag here, as STL impls can't agree on feature macro.
+ // To avoid differences in C++20 and C++17 builds, treat only pointers as contiguous
+ // for now:
+ // std::contiguous_iterator<Iterator>;
+ std::is_pointer_v<Iterator>;
+
+ template <typename Char>
+ using is_compatible_char_helper = std::disjunction<
+ QtPrivate::IsCompatibleCharType<Char>,
+ std::is_same<Char, QLatin1Char> // special case
+ >;
+
+ template <typename Iterator>
+ static constexpr bool is_compatible_iterator_v = std::conjunction_v<
+ std::is_convertible<
+ typename std::iterator_traits<Iterator>::iterator_category,
+ std::input_iterator_tag
+ >,
+ is_compatible_char_helper<typename std::iterator_traits<Iterator>::value_type>
+ >;
+
+ template <typename Iterator>
+ using if_compatible_iterator = std::enable_if_t<is_compatible_iterator_v<Iterator>, bool>;
+
public:
typedef QStringPrivate DataPointer;
@@ -385,6 +413,26 @@ public:
Q_ASSERT(n >= 0);
return fill(c, n);
}
+ template <typename InputIterator, if_compatible_iterator<InputIterator> = true>
+ QString &assign(InputIterator first, InputIterator last)
+ {
+ using V = typename std::iterator_traits<InputIterator>::value_type;
+ constexpr bool IsL1C = std::is_same_v<std::remove_cv_t<V>, QLatin1Char>;
+
+ if constexpr (is_contiguous_iterator_v<InputIterator>) {
+ const auto p = q20::to_address(first);
+ const auto len = qsizetype(last - first);
+ if constexpr (IsL1C)
+ return assign(QLatin1StringView(reinterpret_cast<const char*>(p), len));
+ else
+ return assign(QAnyStringView(p, len));
+ } else { // non-contiguous iterator, need to feed data piecemeal
+ d.assign(first, last, [](QChar ch) -> char16_t { return ch.unicode(); });
+ d.data()[d.size] = u'\0';
+ return *this;
+ }
+ }
+
inline QString &operator+=(QChar c) { return append(c); }
inline QString &operator+=(const QString &s) { return append(s); }
diff --git a/tests/auto/corelib/text/qstring/tst_qstring.cpp b/tests/auto/corelib/text/qstring/tst_qstring.cpp
index a6362d6a23..f3d2594e34 100644
--- a/tests/auto/corelib/text/qstring/tst_qstring.cpp
+++ b/tests/auto/corelib/text/qstring/tst_qstring.cpp
@@ -27,9 +27,11 @@
#include <qhash.h>
#include <private/qtools_p.h>
+#include <forward_list>
#include <string>
#include <algorithm>
#include <limits>
+#include <sstream>
#include "../shared/test_number_shared.h"
#include "../../../../shared/localechange.h"
@@ -3424,11 +3426,52 @@ void tst_QString::assign()
QCOMPARE(str.assign(3, u'x'), u"xxx");
QCOMPARE(str.size(), 3);
}
+ // QString &assign(InputIterator, InputIterator)
+ {
+ // Forward iterator versions
+ QString str;
+ const QString tstr = QString::fromUtf8(u8"(ノಠ益ಠ)\0ノ彡┻━┻");
+ QCOMPARE(str.assign(tstr.begin(), tstr.end()), u"(ノಠ益ಠ)\0ノ彡┻━┻");
+ QCOMPARE(str.size(), 6);
+
+ const char16_t c16[] = u"٩(⁎❛ᴗ❛⁎)۶ 🤷";
+ str.assign(std::begin(c16), std::end(c16) - 1);
+ QCOMPARE(str, c16);
+
+ std::u16string c16str(c16);
+ str.assign(c16str.begin(), c16str.end());
+ QCOMPARE(str, c16);
+
+ QVarLengthArray<QLatin1Char, 5> l1ch = {'F'_L1, 'G'_L1, 'H'_L1, 'I'_L1, 'J'_L1};
+ str.assign(l1ch.begin(), l1ch.end());
+ QCOMPARE(str, u"FGHIJ");
+ std::forward_list<QChar> qch = {u'G', u'H', u'I', u'J', u'K'};
+ str.assign(qch.begin(), qch.end());
+ QCOMPARE(str, u"GHIJK");
+ const QList<char16_t> qch16 = {u'X', u'H', u'I', u'J', u'K'}; // QList<T>::iterator need not be T*
+ str.assign(qch16.begin(), qch16.end());
+ QCOMPARE(str, u"XHIJK");
+#if defined(Q_OS_WIN)
+ QVarLengthArray<wchar_t> wch = {L'A', L'B', L'C', L'D', L'E'};
+ str.assign(wch.begin(), wch.end());
+ QCOMPARE(str, u"ABCDE");
+#endif
+ // Input iterator versions
+ std::stringstream ss("50 51 52 53 54");
+ str.assign(std::istream_iterator<ushort>{ss}, std::istream_iterator<ushort>{});
+ QCOMPARE(str, u"23456");
+ }
// Test chaining
{
QString str;
+ QString tstr = u"TEST DATA"_s;
+ str.assign(tstr.begin(), tstr.end()).assign({"Hello World!"}).assign(5, u'T');
+ QCOMPARE(str, u"TTTTT");
+ QCOMPARE(str.size(), 5);
QCOMPARE(str.assign(300, u'T').assign({"[̲̅$̲̅(̲̅5̲̅)̲̅$̲̅]"}), u"[̲̅$̲̅(̲̅5̲̅)̲̅$̲̅]");
QCOMPARE(str.size(), 19);
+ QCOMPARE(str.assign(10, u'c').assign(str.begin(), str.end()), str);
+ QCOMPARE(str.size(), 10);
QCOMPARE(str.assign("data").assign(QByteArrayView::fromArray(
{std::byte('T'), std::byte('T'), std::byte('T')})), u"TTT");
QCOMPARE(str.size(), 3);
@@ -3456,6 +3499,43 @@ void tst_QString::assign_shared()
QCOMPARE(str, u"DDDD");
QCOMPARE(strCopy, u"DATA");
}
+ {
+ QString str = "DATA"_L1;
+ QVERIFY(str.isDetached());
+ auto copyForwardIt = str;
+ QVERIFY(!str.isDetached());
+ QVERIFY(!copyForwardIt.isDetached());
+ QVERIFY(str.isSharedWith(copyForwardIt));
+ QVERIFY(copyForwardIt.isSharedWith(str));
+
+ QString tstr = u"DDDD"_s;
+ str.assign(tstr.begin(), tstr.end());
+ QVERIFY(str.isDetached());
+ QVERIFY(copyForwardIt.isDetached());
+ QVERIFY(!str.isSharedWith(copyForwardIt));
+ QVERIFY(!copyForwardIt.isSharedWith(str));
+ QCOMPARE(str, u"DDDD");
+ QCOMPARE(copyForwardIt, u"DATA");
+ }
+ {
+ QString str = "DATA"_L1;
+ QVERIFY(str.isDetached());
+ auto copyInputIt = str;
+ QVERIFY(!str.isDetached());
+ QVERIFY(!copyInputIt.isDetached());
+ QVERIFY(str.isSharedWith(copyInputIt));
+ QVERIFY(copyInputIt.isSharedWith(str));
+
+ std::stringstream ss("49 50 51 52 53 54 ");
+ str.assign(std::istream_iterator<ushort>{ss}, std::istream_iterator<ushort>{});
+ QVERIFY(str.isDetached());
+ QVERIFY(copyInputIt.isDetached());
+ QVERIFY(!str.isSharedWith(copyInputIt));
+ QVERIFY(!copyInputIt.isSharedWith(str));
+
+ QCOMPARE(str, u"123456");
+ QCOMPARE(copyInputIt, u"DATA");
+ }
}
void tst_QString::assign_uses_prepend_buffer()
@@ -3484,6 +3564,25 @@ void tst_QString::assign_uses_prepend_buffer()
QCOMPARE_EQ(capEnd(withFreeSpaceAtBegin), oldCapEnd);
QCOMPARE(withFreeSpaceAtBegin, test);
}
+ // QString &assign(InputIterator, InputIterator)
+ {
+ QString withFreeSpaceAtBegin;
+ for (int i = 0; i < 100 && withFreeSpaceAtBegin.d.freeSpaceAtBegin() < 2; ++i)
+ withFreeSpaceAtBegin.prepend(u'd');
+ QCOMPARE_GT(withFreeSpaceAtBegin.d.freeSpaceAtBegin(), 1);
+
+ const auto oldCapBegin = capBegin(withFreeSpaceAtBegin);
+ const auto oldCapEnd = capEnd(withFreeSpaceAtBegin);
+
+ std::stringstream ss;
+ for (qsizetype i = 0; i < withFreeSpaceAtBegin.d.freeSpaceAtBegin(); ++i)
+ ss << "d ";
+
+ withFreeSpaceAtBegin.assign(std::istream_iterator<ushort>{ss}, std::istream_iterator<ushort>{});
+ QCOMPARE_EQ(withFreeSpaceAtBegin.d.freeSpaceAtBegin(), 0); // we used the prepend buffer
+ QCOMPARE_EQ(capBegin(withFreeSpaceAtBegin), oldCapBegin);
+ QCOMPARE_EQ(capEnd(withFreeSpaceAtBegin), oldCapEnd);
+ }
}
void tst_QString::operator_pluseq_special_cases()
diff --git a/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp b/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp
index 023a03f4a4..d683950445 100644
--- a/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp
+++ b/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp
@@ -337,9 +337,11 @@ private:
private Q_SLOTS:
void assign_std_vector() { assign_impl<std::vector<int>>(); };
+ void assign_std_string() { assign_impl<std::string>(); }
void assign_QVarLengthArray() { assign_impl<QVarLengthArray<int, 4>>(); };
void assign_QList() { assign_impl<QList<int>>(); }
void assign_QByteArray() { assign_impl<QByteArray>(); }
+ void assign_QString() { assign_impl<QString>(); }
private:
template <typename Container>