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()
|
\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)
|
QString &QString::assign(QAnyStringView s)
|
||||||
{
|
{
|
||||||
if (s.size() <= capacity() && isDetached()) {
|
if (s.size() <= capacity() && isDetached()) {
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <iterator>
|
#include <iterator>
|
||||||
|
#include <QtCore/q20memory.h>
|
||||||
|
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
@ -120,6 +121,33 @@ class Q_CORE_EXPORT QString
|
|||||||
typedef QTypedArrayData<char16_t> Data;
|
typedef QTypedArrayData<char16_t> Data;
|
||||||
|
|
||||||
friend class ::tst_QString;
|
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:
|
public:
|
||||||
typedef QStringPrivate DataPointer;
|
typedef QStringPrivate DataPointer;
|
||||||
|
|
||||||
@ -385,6 +413,26 @@ public:
|
|||||||
Q_ASSERT(n >= 0);
|
Q_ASSERT(n >= 0);
|
||||||
return fill(c, n);
|
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+=(QChar c) { return append(c); }
|
||||||
|
|
||||||
inline QString &operator+=(const QString &s) { return append(s); }
|
inline QString &operator+=(const QString &s) { return append(s); }
|
||||||
|
@ -27,9 +27,11 @@
|
|||||||
#include <qhash.h>
|
#include <qhash.h>
|
||||||
#include <private/qtools_p.h>
|
#include <private/qtools_p.h>
|
||||||
|
|
||||||
|
#include <forward_list>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
#include "../shared/test_number_shared.h"
|
#include "../shared/test_number_shared.h"
|
||||||
#include "../../../../shared/localechange.h"
|
#include "../../../../shared/localechange.h"
|
||||||
@ -3422,11 +3424,52 @@ void tst_QString::assign()
|
|||||||
QCOMPARE(str.assign(3, u'x'), u"xxx");
|
QCOMPARE(str.assign(3, u'x'), u"xxx");
|
||||||
QCOMPARE(str.size(), 3);
|
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
|
// Test chaining
|
||||||
{
|
{
|
||||||
QString str;
|
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.assign(300, u'T').assign({"[̲̅$̲̅(̲̅5̲̅)̲̅$̲̅]"}), u"[̲̅$̲̅(̲̅5̲̅)̲̅$̲̅]");
|
||||||
QCOMPARE(str.size(), 19);
|
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(
|
QCOMPARE(str.assign("data").assign(QByteArrayView::fromArray(
|
||||||
{std::byte('T'), std::byte('T'), std::byte('T')})), u"TTT");
|
{std::byte('T'), std::byte('T'), std::byte('T')})), u"TTT");
|
||||||
QCOMPARE(str.size(), 3);
|
QCOMPARE(str.size(), 3);
|
||||||
@ -3454,6 +3497,43 @@ void tst_QString::assign_shared()
|
|||||||
QCOMPARE(str, u"DDDD");
|
QCOMPARE(str, u"DDDD");
|
||||||
QCOMPARE(strCopy, u"DATA");
|
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()
|
void tst_QString::assign_uses_prepend_buffer()
|
||||||
@ -3482,6 +3562,25 @@ void tst_QString::assign_uses_prepend_buffer()
|
|||||||
QCOMPARE_EQ(capEnd(withFreeSpaceAtBegin), oldCapEnd);
|
QCOMPARE_EQ(capEnd(withFreeSpaceAtBegin), oldCapEnd);
|
||||||
QCOMPARE(withFreeSpaceAtBegin, test);
|
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()
|
void tst_QString::operator_pluseq_special_cases()
|
||||||
|
@ -337,9 +337,11 @@ private:
|
|||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void assign_std_vector() { assign_impl<std::vector<int>>(); };
|
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_QVarLengthArray() { assign_impl<QVarLengthArray<int, 4>>(); };
|
||||||
void assign_QList() { assign_impl<QList<int>>(); }
|
void assign_QList() { assign_impl<QList<int>>(); }
|
||||||
void assign_QByteArray() { assign_impl<QByteArray>(); }
|
void assign_QByteArray() { assign_impl<QByteArray>(); }
|
||||||
|
void assign_QString() { assign_impl<QString>(); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template <typename Container>
|
template <typename Container>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user