Make QString and QByteArray sequentially iterable

As lists of QStrings and QByteArrays are sequentially iterable the base
types should really also be.

The only problem is that they don't have methods to remove items from
the back or the front, but that is well within what we can support with
QSequentialIterable.

Change-Id: I2ab551e7b11a092aba363fb4012d131bbc4b11b4
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
bb10
Ulf Hermann 2020-12-18 15:46:11 +01:00
parent 05146a77fc
commit c9a1102269
2 changed files with 94 additions and 34 deletions

View File

@ -1951,27 +1951,33 @@ static bool convertIterableToVariantPair(QMetaType fromType, const void *from, v
static bool convertToSequentialIterable(QMetaType fromType, const void *from, void *to)
{
using namespace QtMetaTypePrivate;
int fromTypeId = fromType.id();
const int fromTypeId = fromType.id();
QSequentialIterable &i = *static_cast<QSequentialIterable *>(to);
if (fromTypeId == QMetaType::QVariantList) {
switch (fromTypeId) {
case QMetaType::QVariantList:
i = QSequentialIterable(reinterpret_cast<const QVariantList *>(from));
return true;
}
if (fromTypeId == QMetaType::QStringList) {
case QMetaType::QStringList:
i = QSequentialIterable(reinterpret_cast<const QStringList *>(from));
return true;
}
else if (fromTypeId == QMetaType::QByteArrayList) {
case QMetaType::QByteArrayList:
i = QSequentialIterable(reinterpret_cast<const QByteArrayList *>(from));
return true;
}
QSequentialIterable impl;
if (QMetaType::convert(
fromType, from, QMetaType::fromType<QIterable<QMetaSequence>>(), &impl)) {
i = std::move(impl);
case QMetaType::QString:
i = QSequentialIterable(reinterpret_cast<const QString *>(from));
return true;
case QMetaType::QByteArray:
i = QSequentialIterable(reinterpret_cast<const QByteArray *>(from));
return true;
default: {
QSequentialIterable impl;
if (QMetaType::convert(
fromType, from, QMetaType::fromType<QIterable<QMetaSequence>>(), &impl)) {
i = std::move(impl);
return true;
}
}
}
return false;
@ -1983,6 +1989,8 @@ static bool canConvertToSequentialIterable(QMetaType fromType)
case QMetaType::QVariantList:
case QMetaType::QStringList:
case QMetaType::QByteArrayList:
case QMetaType::QString:
case QMetaType::QByteArray:
return true;
default:
return QMetaType::canConvert(fromType, QMetaType::fromType<QIterable<QMetaSequence>>());
@ -1995,6 +2003,8 @@ static bool canImplicitlyViewAsSequentialIterable(QMetaType fromType)
case QMetaType::QVariantList:
case QMetaType::QStringList:
case QMetaType::QByteArrayList:
case QMetaType::QString:
case QMetaType::QByteArray:
return true;
default:
return QMetaType::canView(
@ -2005,27 +2015,33 @@ static bool canImplicitlyViewAsSequentialIterable(QMetaType fromType)
static bool viewAsSequentialIterable(QMetaType fromType, void *from, void *to)
{
using namespace QtMetaTypePrivate;
int fromTypeId = fromType.id();
const int fromTypeId = fromType.id();
QSequentialIterable &i = *static_cast<QSequentialIterable *>(to);
if (fromTypeId == QMetaType::QVariantList) {
switch (fromTypeId) {
case QMetaType::QVariantList:
i = QSequentialIterable(reinterpret_cast<QVariantList *>(from));
return true;
}
if (fromTypeId == QMetaType::QStringList) {
case QMetaType::QStringList:
i = QSequentialIterable(reinterpret_cast<QStringList *>(from));
return true;
}
else if (fromTypeId == QMetaType::QByteArrayList) {
case QMetaType::QByteArrayList:
i = QSequentialIterable(reinterpret_cast<QByteArrayList *>(from));
return true;
}
QIterable<QMetaSequence> j(QMetaSequence(), nullptr);
if (QMetaType::view(
fromType, from, QMetaType::fromType<QIterable<QMetaSequence>>(), &j)) {
i = std::move(j);
case QMetaType::QString:
i = QSequentialIterable(reinterpret_cast<QString *>(from));
return true;
case QMetaType::QByteArray:
i = QSequentialIterable(reinterpret_cast<QByteArray *>(from));
return true;
default: {
QIterable<QMetaSequence> j(QMetaSequence(), nullptr);
if (QMetaType::view(
fromType, from, QMetaType::fromType<QIterable<QMetaSequence>>(), &j)) {
i = std::move(j);
return true;
}
}
}
return false;

View File

@ -474,7 +474,7 @@ void tst_QVariant::canConvert_data()
<< var << Y << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N;
var = QVariant(QByteArray());
QTest::newRow("ByteArray")
<< var << N << N << Y << N << Y << Y << N << N << N << Y << N << N << Y << N << N << N << Y << N << N << N << N << N << N << N << N << N << Y << N << N << Y << Y;
<< var << N << N << Y << N << Y << Y << N << N << N << Y << N << N << Y << N << N << Y << Y << N << N << N << N << N << N << N << N << N << Y << N << N << Y << Y;
var = QVariant(QDate());
QTest::newRow("Date")
<< var << N << N << N << N << N << N << N << Y << Y << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << Y << N << N << N << N;
@ -513,7 +513,7 @@ void tst_QVariant::canConvert_data()
<< var << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << Y << N << N << N << N << N << N;
var = QVariant(QString());
QTest::newRow("String")
<< var << N << N << Y << N << Y << Y << N << Y << Y << Y << Y << N << Y << N << Y << N << Y << N << N << N << N << N << N << N << N << N << Y << Y << Y << Y << Y;
<< var << N << N << Y << N << Y << Y << N << Y << Y << Y << Y << N << Y << N << Y << Y << Y << N << N << N << N << N << N << N << N << N << Y << Y << Y << Y << Y;
var = QVariant(QStringList("entry"));
QTest::newRow("StringList")
<< var << N << N << N << N << N << N << N << N << N << N << N << N << N << N << N << Y << N << N << N << N << N << N << N << N << N << N << Y << Y << N << N << N;
@ -4074,6 +4074,42 @@ struct ContainerAPI<Container, QByteArray>
}
};
template<typename Container>
struct ContainerAPI<Container, QChar>
{
static void insert(Container &container, int value)
{
container.push_back(QChar::fromLatin1(char(value) + '0'));
}
static bool compare(const QVariant &variant, QChar value)
{
return variant.value<QChar>() == value;
}
static bool compare(QVariant variant, const QVariant &value)
{
return variant == value;
}
};
template<typename Container>
struct ContainerAPI<Container, char>
{
static void insert(Container &container, int value)
{
container.push_back(char(value) + '0');
}
static bool compare(const QVariant &variant, char value)
{
return variant.value<char>() == value;
}
static bool compare(QVariant variant, const QVariant &value)
{
return variant == value;
}
};
#ifdef __has_include
# if __has_include(<forward_list>)
# define TEST_FORWARD_LIST
@ -4287,14 +4323,20 @@ void testSequentialIteration()
QCOMPARE(listIter.at(4), third);
QCOMPARE(listIter.at(5), third);
listIter.removeValue();
compareLists();
QCOMPARE(listIter.size(), 5);
QCOMPARE(listIter.at(0), first);
QCOMPARE(listIter.at(1), first);
QCOMPARE(listIter.at(2), second);
QCOMPARE(listIter.at(3), second);
QCOMPARE(listIter.at(4), third);
if (listIter.metaContainer().canRemoveValue()) {
listIter.removeValue();
compareLists();
QCOMPARE(listIter.size(), 5);
QCOMPARE(listIter.at(0), first);
QCOMPARE(listIter.at(1), first);
QCOMPARE(listIter.at(2), second);
QCOMPARE(listIter.at(3), second);
QCOMPARE(listIter.at(4), third);
} else {
// QString and QByteArray have no pop_back or pop_front and it's unclear what other
// method we should use to remove an item.
QVERIFY((std::is_same_v<Container, QString> || std::is_same_v<Container, QByteArray>));
}
auto i = listIter.mutableBegin();
QVERIFY(i != listIter.mutableEnd());
@ -4404,6 +4446,8 @@ void tst_QVariant::iterateContainerElements()
testSequentialIteration<std::list<QString>>();
testSequentialIteration<QStringList>();
testSequentialIteration<QByteArrayList>();
testSequentialIteration<QString>();
testSequentialIteration<QByteArray>();
#ifdef TEST_FORWARD_LIST
testSequentialIteration<std::forward_list<int>>();