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 Change-Id: Icb44244cb08af391161c4309467d4e0d2d3d3d62 Reviewed-by: Ivan Solovev <ivan.solovev@qt.io> Reviewed-by: Dennis Oberst <dennis.oberst@qt.io> (cherry picked from commit f5ed163c19c4a165a61e6fbfdaf5ee39b5587a0c) Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
e5d222867c
commit
a08ca88b24
@ -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()) {
|
||||
|
@ -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); }
|
||||
|
@ -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"
|
||||
@ -3422,11 +3424,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);
|
||||
@ -3454,6 +3497,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()
|
||||
@ -3482,6 +3562,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()
|
||||
|
@ -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>
|
||||
|
Loading…
x
Reference in New Issue
Block a user