QSequentialIterable: Treat sets as appendable

QSet and std::(unordered_)set were so far not treated as appendable, as
they lack a push_back method. We do however need support for this in
declarative to enable converting back from QJSValue arrays to sets.
We achieve this by testing for and using the insert method. While vector
has also such a method, it doesn't take a single value, but rather a
position or iterator + value, so the template specialization is not
ambiguous.

Task-number: QTBUG-82743
Change-Id: I74fc7b1b856d9bcd38100b274ba2b69578ea8bbb
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
This commit is contained in:
Fabian Kosmale 2020-03-12 16:25:31 +01:00
parent 943cb0999d
commit 6db55e3451
2 changed files with 40 additions and 8 deletions

View File

@ -1014,6 +1014,24 @@ struct ContainerCapabilitiesImpl<Container, decltype(std::declval<Container>().p
{ static_cast<Container *>(const_cast<void *>(container))->push_back(*static_cast<const typename Container::value_type *>(value)); }
};
namespace QtPrivate {
namespace ContainerCapabilitiesMetaProgrammingHelper {
template<typename... Ts>
using void_t = void;
}
}
template<typename Container>
struct ContainerCapabilitiesImpl<Container, QtPrivate::ContainerCapabilitiesMetaProgrammingHelper::void_t<decltype(std::declval<Container>().insert(std::declval<typename Container::value_type>())), decltype(std::declval<typename Container::value_type>() == std::declval<typename Container::value_type>())>>
{
enum {ContainerCapabilities = ContainerIsAppendable};
// The code below invokes undefined behavior if and only if the pointer passed into QSequentialIterableImpl
// pointed to a const object to begin with
static void appendImpl(const void *container, const void *value)
{ static_cast<Container *>(const_cast<void *>(container))->insert(*static_cast<const typename Container::value_type *>(value)); }
};
template<typename T, typename Category = typename std::iterator_traits<typename T::const_iterator>::iterator_category>
struct CapabilitiesImpl;

View File

@ -5161,14 +5161,28 @@ void tst_QVariant::sequentialIterableEndianessSanityCheck()
void tst_QVariant::sequentialIterableAppend()
{
QVector<int> container {1, 2};
auto variant = QVariant::fromValue(container);
QVERIFY(variant.canConvert<QtMetaTypePrivate::QSequentialIterableImpl>());
auto asIterable = variant.value<QtMetaTypePrivate::QSequentialIterableImpl>();
const int i = 3, j = 4;
asIterable.append(&i);
asIterable.append(&j);
QCOMPARE(variant.value<QVector<int>>(), QVector<int> ({1, 2, 3, 4}));
{
QVector<int> container {1, 2};
auto variant = QVariant::fromValue(container);
QVERIFY(variant.canConvert<QtMetaTypePrivate::QSequentialIterableImpl>());
auto asIterable = variant.value<QtMetaTypePrivate::QSequentialIterableImpl>();
const int i = 3, j = 4;
asIterable.append(&i);
asIterable.append(&j);
QCOMPARE(variant.value<QVector<int>>(), QVector<int> ({1, 2, 3, 4}));
}
{
QSet<QByteArray> container { QByteArray{"hello"}, QByteArray{"world"} };
auto variant = QVariant::fromValue(std::move(container));
QVERIFY(variant.canConvert<QtMetaTypePrivate::QSequentialIterableImpl>());
auto asIterable = variant.value<QtMetaTypePrivate::QSequentialIterableImpl>();
QByteArray qba1 {"goodbye"};
QByteArray qba2 { "moon" };
asIterable.append( &qba1 );
asIterable.append( &qba2);
QSet<QByteArray> reference { "hello", "world", "goodbye", "moon" };
QCOMPARE(variant.value<QSet<QByteArray>>(), reference);
}
}
void tst_QVariant::preferDirectConversionOverInterfaces()