Add QVariant container iteration API.

A new set of classes is introduced for iterating over the contents
of a container within a QVariant without knowing the exact type of
the container, but with the guarantee that the element type within
the container is a metatype.

The implementation of the iterable interface uses
the stl-compatible container API so that we can also iterate over stl
containers, or any other container which also conforms to stl norms.

This enables the functionality in the bug report.

Task-number: QTBUG-23566

Change-Id: I92a2f3458516de201b8f0e470982c4d030e8ac8b
Reviewed-by: Stephen Kelly <stephen.kelly@kdab.com>
This commit is contained in:
Stephen Kelly 2013-03-18 21:53:55 +01:00 committed by The Qt Project
parent f7b313e6d8
commit 01fb843af8
6 changed files with 775 additions and 3 deletions

View File

@ -134,3 +134,33 @@ return QVariant::fromValue(s);
QObject *object = getObjectFromSomewhere();
QVariant data = QVariant::fromValue(object);
//! [8]
//! [9]
qRegisterSequentialConverter<QList<int> >();
QList<int> intList;
intList.push_back(7);
intList.push_back(11);
intList.push_back(42);
QVariant variant = QVariant::fromValue(intList);
if (variant.canConvert<QVariantList>()) {
QSequentialIterable iterable = variant.value<QSequentialIterable>();
// Can use foreach:
foreach (const QVariant &v, iterable) {
qDebug() << v;
}
// Can use C++11 range-for:
for (const QVariant &v : iterable) {
qDebug() << v;
}
// Can use iterators:
QSequentialIterable::const_iterator it = iterable.begin();
const QSequentialIterable::const_iterator end = iterable.end();
for ( ; it != end; ++it) {
qDebug() << *it;
}
}
//! [9]

View File

@ -1962,6 +1962,18 @@ const QMetaObject *QMetaType::metaObjectForType(int type)
\sa Q_DECLARE_METATYPE(), QMetaType::type()
*/
/*!
\fn bool qRegisterSequentialConverter()
\relates QMetaType
\since 5.2
Registers a sequential container so that it can be converted to
a QVariantList. If compilation fails, then you probably forgot to
Q_DECLARE_METATYPE the value type.
\sa QVariant::canConvert()
*/
namespace {
class TypeInfo {
template<typename T, bool IsAcceptedType = DefinedTypesFilter::Acceptor<T>::IsAccepted>

View File

@ -534,7 +534,16 @@ private:
void *constructExtended(void *where, const void *copy = 0) const;
void destructExtended(void *data) const;
#ifndef Q_NO_TEMPLATE_FRIENDS
#ifndef Q_QDOC
template<typename T>
friend bool qRegisterSequentialConverter();
#endif
#else
public:
#endif
static bool registerConverterFunction(QtPrivate::AbstractConverterFunction *f, int from, int to);
private:
Creator m_creator;
Deleter m_deleter;
@ -609,6 +618,246 @@ template <>
struct QMetaTypeFunctionHelper<void, /* Accepted */ true>
: public QMetaTypeFunctionHelper<void, /* Accepted */ false>
{};
struct VariantData
{
VariantData(const int metaTypeId_,
const void *data_,
const uint flags_)
: metaTypeId(metaTypeId_)
, data(data_)
, flags(flags_)
{
}
const int metaTypeId;
const void *data;
const uint flags;
};
template<typename const_iterator>
struct IteratorOwner
{
static void assign(void **ptr, const_iterator iterator)
{
*ptr = new const_iterator(iterator);
}
static void advance(void **iterator, int step)
{
const_iterator &it = *static_cast<const_iterator*>(*iterator);
std::advance(it, step);
}
static void destroy(void **ptr)
{
delete static_cast<const_iterator*>(*ptr);
}
static const void *getData(void * const *iterator)
{
return &**static_cast<const_iterator*>(*iterator);
}
static const void *getData(const_iterator it)
{
return &*it;
}
};
template<typename const_iterator>
struct IteratorOwner<const const_iterator*>
{
static void assign(void **ptr, const const_iterator *iterator )
{
*ptr = const_cast<const_iterator*>(iterator);
}
static void advance(void **iterator, int step)
{
const_iterator *it = static_cast<const_iterator*>(*iterator);
std::advance(it, step);
*iterator = it;
}
static void destroy(void **)
{
}
static const void *getData(void * const *iterator)
{
return *iterator;
}
static const void *getData(const const_iterator *it)
{
return it;
}
};
enum IteratorCapability
{
ForwardCapability = 1,
BiDirectionalCapability = 2,
RandomAccessCapability = 4
};
template<typename T, typename Category = typename std::iterator_traits<typename T::const_iterator>::iterator_category>
struct CapabilitiesImpl;
template<typename T>
struct CapabilitiesImpl<T, std::forward_iterator_tag>
{ enum { IteratorCapabilities = ForwardCapability }; };
template<typename T>
struct CapabilitiesImpl<T, std::bidirectional_iterator_tag>
{ enum { IteratorCapabilities = BiDirectionalCapability | ForwardCapability }; };
template<typename T>
struct CapabilitiesImpl<T, std::random_access_iterator_tag>
{ enum { IteratorCapabilities = RandomAccessCapability | BiDirectionalCapability | ForwardCapability }; };
template<typename T>
struct ContainerAPI : CapabilitiesImpl<T>
{
static int size(const T *t) { return std::distance(t->begin(), t->end()); }
};
template<typename T>
struct ContainerAPI<QList<T> > : CapabilitiesImpl<QList<T> >
{ static int size(const QList<T> *t) { return t->size(); } };
template<typename T>
struct ContainerAPI<QVector<T> > : CapabilitiesImpl<QVector<T> >
{ static int size(const QVector<T> *t) { return t->size(); } };
template<typename T>
struct ContainerAPI<std::vector<T> > : CapabilitiesImpl<std::vector<T> >
{ static int size(const std::vector<T> *t) { return t->size(); } };
template<typename T>
struct ContainerAPI<std::list<T> > : CapabilitiesImpl<std::list<T> >
{ static int size(const std::list<T> *t) { return t->size(); } };
class QSequentialIterableImpl
{
public:
const void * _iterable;
void *_iterator;
int _metaType_id;
uint _metaType_flags;
uint _iteratorCapabilities;
typedef int(*sizeFunc)(const void *p);
typedef const void * (*atFunc)(const void *p, int);
typedef void (*moveIteratorFunc)(const void *p, void **);
typedef void (*advanceFunc)(void **p, int);
typedef VariantData (*getFunc)( void * const *p, int metaTypeId, uint flags);
typedef void (*destroyIterFunc)(void **p);
typedef bool (*equalIterFunc)(void * const *p, void * const *other);
sizeFunc _size;
atFunc _at;
moveIteratorFunc _moveToBegin;
moveIteratorFunc _moveToEnd;
advanceFunc _advance;
getFunc _get;
destroyIterFunc _destroyIter;
equalIterFunc _equalIter;
template<class T>
static int sizeImpl(const void *p)
{ return ContainerAPI<T>::size(static_cast<const T*>(p)); }
template<class T>
static const void* atImpl(const void *p, int idx)
{
typename T::const_iterator i = static_cast<const T*>(p)->begin();
std::advance(i, idx);
return IteratorOwner<typename T::const_iterator>::getData(i);
}
template<class T>
static void advanceImpl(void **p, int step)
{ IteratorOwner<typename T::const_iterator>::advance(p, step); }
template<class T>
static void moveToBeginImpl(const void *container, void **iterator)
{ IteratorOwner<typename T::const_iterator>::assign(iterator, static_cast<const T*>(container)->begin()); }
template<class T>
static void moveToEndImpl(const void *container, void **iterator)
{ IteratorOwner<typename T::const_iterator>::assign(iterator, static_cast<const T*>(container)->end()); }
template<class T>
static void destroyIterImpl(void **iterator)
{ IteratorOwner<typename T::const_iterator>::destroy(iterator); }
template<class T>
static bool equalIterImpl(void * const *iterator, void * const *other)
{ return *static_cast<typename T::const_iterator*>(*iterator) == *static_cast<typename T::const_iterator*>(*other); }
template<class T>
static VariantData getImpl(void * const *iterator, int metaTypeId, uint flags)
{ return VariantData(metaTypeId, IteratorOwner<typename T::const_iterator>::getData(iterator), flags); }
public:
template<class T> QSequentialIterableImpl(const T*p)
: _iterable(p)
, _iterator(0)
, _metaType_id(qMetaTypeId<typename T::value_type>())
, _metaType_flags(QTypeInfo<typename T::value_type>::isPointer)
, _iteratorCapabilities(ContainerAPI<T>::IteratorCapabilities)
, _size(sizeImpl<T>)
, _at(atImpl<T>)
, _moveToBegin(moveToBeginImpl<T>)
, _moveToEnd(moveToEndImpl<T>)
, _advance(advanceImpl<T>)
, _get(getImpl<T>)
, _destroyIter(destroyIterImpl<T>)
, _equalIter(equalIterImpl<T>)
{
}
QSequentialIterableImpl()
: _iterable(0)
, _iterator(0)
, _metaType_id(QMetaType::UnknownType)
, _metaType_flags(0)
, _iteratorCapabilities(0)
, _size(0)
, _at(0)
, _moveToBegin(0)
, _moveToEnd(0)
, _advance(0)
, _get(0)
, _destroyIter(0)
, _equalIter(0)
{
}
inline void moveToBegin() { _moveToBegin(_iterable, &_iterator); }
inline void moveToEnd() { _moveToEnd(_iterable, &_iterator); }
inline bool equal(const QSequentialIterableImpl&other) const { return _equalIter(&_iterator, &other._iterator); }
inline QSequentialIterableImpl &advance(int i) {
Q_ASSERT(i > 0 || _iteratorCapabilities & BiDirectionalCapability);
_advance(&_iterator, i);
return *this;
}
inline VariantData getCurrent() const { return _get(&_iterator, _metaType_id, _metaType_flags); }
VariantData at(int idx) const
{ return VariantData(_metaType_id, _at(_iterable, idx), _metaType_flags); }
int size() const { Q_ASSERT(_iterable); return _size(_iterable); }
inline void destroyIter() { _destroyIter(&_iterator); }
};
template<typename From>
struct QSequentialIterableConvertFunctor
{
QSequentialIterableImpl operator()(const From &f) const
{
return QSequentialIterableImpl(&f);
}
};
}
class QObject;
@ -1173,5 +1422,30 @@ QT_END_NAMESPACE
QT_FOR_EACH_STATIC_TYPE(Q_DECLARE_BUILTIN_METATYPE)
Q_DECLARE_METATYPE(QtMetaTypePrivate::QSequentialIterableImpl)
QT_BEGIN_NAMESPACE
#ifndef Q_QDOC
template<typename T>
#endif
bool qRegisterSequentialConverter()
{
Q_STATIC_ASSERT_X(QMetaTypeId2<typename T::value_type>::Defined,
"The value_type of a sequential container must itself be a metatype.");
const int id = qMetaTypeId<T>();
const int toId = qMetaTypeId<QtMetaTypePrivate::QSequentialIterableImpl>();
if (QMetaType::hasRegisteredConverterFunction(id, toId))
return true;
QtMetaTypePrivate::QSequentialIterableConvertFunctor<T> f;
return QMetaType::registerConverterFunction(
new QtPrivate::ConverterFunctor<T,
QtMetaTypePrivate::QSequentialIterableImpl,
QtMetaTypePrivate::QSequentialIterableConvertFunctor<T> >(f),
id, toId);
}
QT_END_NAMESPACE
#endif // QMETATYPE_H

View File

@ -2747,10 +2747,28 @@ static bool canConvertMetaObject(int fromId, int toId, QObject *fromObject)
function if a qobject_cast to the type described by \a targetTypeId would succeed. Note that
this only works for QObject subclasses which use the Q_OBJECT macro.
\sa convert()
A QVariant containing a sequential container will also return true for this
function if the \a targetTypeId is QVariantList. It is possible to iterate over
the contents of the container without extracting it as a (copied) QVariantList:
\snippet code/src_corelib_kernel_qvariant.cpp 9
This requires that the value_type of the container is itself a metatype. To make it
possible to convert or iterate over a sequential container, the qRegisterSequentialConverter
method must first be called for the container.
\sa convert(), QSequentialIterable
*/
bool QVariant::canConvert(int targetTypeId) const
{
if (targetTypeId == QMetaType::QVariantList
&& (d.type == QMetaType::QVariantList
|| d.type == QMetaType::QStringList
|| QMetaType::hasRegisteredConverterFunction(d.type,
qMetaTypeId<QtMetaTypePrivate::QSequentialIterableImpl>()))) {
return true;
}
if ((d.type >= QMetaType::User || targetTypeId >= QMetaType::User)
&& QMetaType::hasRegisteredConverterFunction(d.type, targetTypeId)) {
return true;
@ -3219,4 +3237,187 @@ QDebug operator<<(QDebug dbg, const QVariant::Type p)
\internal
*/
/*!
\class QSequentialIterable
\inmodule QtCore
\brief The QSequentialIterable class is an iterable interface for a container in a QVariant.
This class allows several methods of accessing the elements of a container held within
a QVariant. An instance of QSequentialIterable can be extracted from a QVariant if it can
be converted to a QVariantList.
\snippet code/src_corelib_kernel_qvariant.cpp 9
The container itself is not copied before iterating over it.
\sa QVariant
*/
/*! \fn QSequentialIterable::QSequentialIterable(QtMetaTypePrivate::QSequentialIterableImpl)
\internal
*/
/*! \fn QSequentialIterable::const_iterator QSequentialIterable::begin() const
Returns a QSequentialIterable::const_iterator for the beginning of the container. This
can be used in stl-style iteration.
\sa end()
*/
/*! \fn QSequentialIterable::const_iterator QSequentialIterable::end() const
Returns a QSequentialIterable::const_iterator for the end of the container. This
can be used in stl-style iteration.
\sa begin()
*/
/*! \fn QVariant QSequentialIterable::at(int idx) const
Returns the element at position \a idx in the container.
*/
/*! \fn int QSequentialIterable::size() const
Returns the number of elements in the container.
*/
/*! \fn bool QSequentialIterable::canReverseIterate() const
Returns whether it is possible to iterate over the container in reverse. This
corresponds to the std::bidirectional_iterator_tag iterator trait of the
const_iterator of the container.
*/
/*!
\class QSequentialIterable::const_iterator
\inmodule QtCore
\brief The QSequentialIterable::const_iterator allows iteration over a container in a QVariant.
A QSequentialIterable::const_iterator can only be created by a QSequentialIterable instance,
and can be used in a way similar to other stl-style iterators.
\snippet code/src_corelib_kernel_qvariant.cpp 9
\sa QSequentialIterable
*/
/*! \fn QSequentialIterable::const_iterator::~const_iterator()
Destroys the QSequentialIterable::const_iterator.
*/
/*! \fn QSequentialIterable::const_iterator::const_iterator(const const_iterator &other)
Creates a copy of \a other.
*/
/*! \fn QVariant QSequentialIterable::const_iterator::operator*() const
Returns the current item, converted to a QVariant.
*/
/*! \fn bool QSequentialIterable::const_iterator::operator==(const const_iterator &other) const
Returns true if \a other points to the same item as this
iterator; otherwise returns false.
\sa operator!=()
*/
/*! \fn bool QSequentialIterable::const_iterator::operator!=(const const_iterator &other) const
Returns true if \a other points to a different item than this
iterator; otherwise returns false.
\sa operator==()
*/
/*! \fn QSequentialIterable::const_iterator &QSequentialIterable::const_iterator::operator++()
The prefix ++ operator (\c{++it}) advances the iterator to the
next item in the container and returns an iterator to the new current
item.
Calling this function on QSequentialIterable::end() leads to undefined results.
\sa operator--()
*/
/*! \fn QSequentialIterable::const_iterator QSequentialIterable::const_iterator::operator++(int)
\overload
The postfix ++ operator (\c{it++}) advances the iterator to the
next item in the container and returns an iterator to the previously
current item.
*/
/*! \fn QSequentialIterable::const_iterator &QSequentialIterable::const_iterator::operator--()
The prefix -- operator (\c{--it}) makes the preceding item
current and returns an iterator to the new current item.
Calling this function on QSequentialIterable::begin() leads to undefined results.
If the container in the QVariant does not support bi-directional iteration, calling this function
leads to undefined results.
\sa operator++(), canReverseIterate()
*/
/*! \fn QSequentialIterable::const_iterator QSequentialIterable::const_iterator::operator--(int)
\overload
The postfix -- operator (\c{it--}) makes the preceding item
current and returns an iterator to the previously current item.
If the container in the QVariant does not support bi-directional iteration, calling this function
leads to undefined results.
\sa canReverseIterate()
*/
/*! \fn QSequentialIterable::const_iterator &QSequentialIterable::const_iterator::operator+=(int j)
Advances the iterator by \a j items.
\sa operator-=(), operator+()
*/
/*! \fn QSequentialIterable::const_iterator &QSequentialIterable::const_iterator::operator-=(int j)
Makes the iterator go back by \a j items.
If the container in the QVariant does not support bi-directional iteration, calling this function
leads to undefined results.
\sa operator+=(), operator-(), canReverseIterate()
*/
/*! \fn QSequentialIterable::const_iterator QSequentialIterable::const_iterator::operator+(int j) const
Returns an iterator to the item at \a j positions forward from
this iterator.
\sa operator-(), operator+=()
*/
/*! \fn QSequentialIterable::const_iterator QSequentialIterable::const_iterator::operator-(int j) const
Returns an iterator to the item at \a j positions backward from
this iterator.
If the container in the QVariant does not support bi-directional iteration, calling this function
leads to undefined results.
\sa operator+(), operator-=(), canReverseIterate()
*/
QT_END_NAMESPACE

View File

@ -49,6 +49,7 @@
#include <QtCore/qmap.h>
#include <QtCore/qhash.h>
#include <QtCore/qstring.h>
#include <QtCore/qstringlist.h>
#include <QtCore/qobject.h>
QT_BEGIN_NAMESPACE
@ -562,6 +563,76 @@ inline bool operator!=(const QVariant &v1, const QVariantComparisonHelper &v2)
}
#endif
class QSequentialIterable
{
QtMetaTypePrivate::QSequentialIterableImpl m_impl;
public:
struct const_iterator
{
private:
QtMetaTypePrivate::QSequentialIterableImpl m_impl;
QAtomicInt *ref;
friend class QSequentialIterable;
inline explicit const_iterator(const QSequentialIterable &iter, QAtomicInt *ref_)
: m_impl(iter.m_impl), ref(ref_) { ref->ref(); }
inline explicit const_iterator(const QtMetaTypePrivate::QSequentialIterableImpl &impl, QAtomicInt *ref_)
: m_impl(impl), ref(ref_) { ref->ref(); }
inline void begin() { m_impl.moveToBegin(); }
inline void end() { m_impl.moveToEnd(); }
public:
inline ~const_iterator() {
if (!ref->deref()) {
m_impl.destroyIter();
}
}
inline const_iterator(const const_iterator &other) : m_impl(other.m_impl), ref(other.ref) {
ref->ref();
}
inline const QVariant operator*() const {
const QtMetaTypePrivate::VariantData d = m_impl.getCurrent();
if (d.metaTypeId == qMetaTypeId<QVariant>())
return *reinterpret_cast<const QVariant*>(d.data);
return QVariant(d.metaTypeId, d.data, d.flags);
}
inline bool operator==(const const_iterator &o) const { return m_impl.equal(o.m_impl); }
inline bool operator!=(const const_iterator &o) const { return !m_impl.equal(o.m_impl); }
inline const_iterator &operator++() { m_impl.advance(1); return *this; }
inline const_iterator operator++(int) { QtMetaTypePrivate::QSequentialIterableImpl impl = m_impl; m_impl.advance(1); return const_iterator(impl, this->ref); }
inline const_iterator &operator--() { m_impl.advance(-1); return *this; }
inline const_iterator operator--(int) { QtMetaTypePrivate::QSequentialIterableImpl impl = m_impl; m_impl.advance(-1); return const_iterator(impl, this->ref); }
inline const_iterator &operator+=(int j) { m_impl.advance(j); return *this; }
inline const_iterator &operator-=(int j) { m_impl.advance(-j); return *this; }
inline const_iterator operator+(int j) const { QtMetaTypePrivate::QSequentialIterableImpl impl = m_impl; impl.advance(j); return const_iterator(impl, this->ref); }
inline const_iterator operator-(int j) const { QtMetaTypePrivate::QSequentialIterableImpl impl = m_impl; impl.advance(-j); return const_iterator(impl, this->ref); }
};
friend struct const_iterator;
explicit QSequentialIterable(QtMetaTypePrivate::QSequentialIterableImpl impl)
: m_impl(impl)
{
}
const_iterator begin() const { const_iterator it(*this, new QAtomicInt(0)); it.begin(); return it; }
const_iterator end() const { const_iterator it(*this, new QAtomicInt(0)); it.end(); return it; }
QVariant at(int idx) const {
const QtMetaTypePrivate::VariantData d = m_impl.at(idx);
if (d.metaTypeId == qMetaTypeId<QVariant>())
return *reinterpret_cast<const QVariant*>(d.data);
return QVariant(d.metaTypeId, d.data, d.flags);
}
int size() const { return m_impl.size(); }
bool canReverseIterate() const
{ return m_impl._iteratorCapabilities & QtMetaTypePrivate::BiDirectionalCapability; }
};
#ifndef QT_MOC
namespace QtPrivate {
template<typename T>
struct QVariantValueHelper : TreatAsQObjectBeforeMetaType<QVariantValueHelper<T>, T, const QVariant &, T>
@ -583,12 +654,31 @@ namespace QtPrivate {
}
#endif
};
template<typename T>
struct QVariantValueHelperInterface : QVariantValueHelper<T>
{
};
template<>
struct QVariantValueHelperInterface<QSequentialIterable>
{
static QSequentialIterable invoke(const QVariant &v)
{
if (v.userType() == qMetaTypeId<QVariantList>()) {
return QSequentialIterable(QtMetaTypePrivate::QSequentialIterableImpl(reinterpret_cast<const QVariantList*>(v.constData())));
}
if (v.userType() == qMetaTypeId<QStringList>()) {
return QSequentialIterable(QtMetaTypePrivate::QSequentialIterableImpl(reinterpret_cast<const QStringList*>(v.constData())));
}
return QSequentialIterable(v.value<QtMetaTypePrivate::QSequentialIterableImpl>());
}
};
}
#ifndef QT_MOC
template<typename T> inline T qvariant_cast(const QVariant &v)
{
return QtPrivate::QVariantValueHelper<T>::invoke(v);
return QtPrivate::QVariantValueHelperInterface<T>::invoke(v);
}
template<> inline QVariant qvariant_cast<QVariant>(const QVariant &v)

View File

@ -239,6 +239,8 @@ private slots:
void saveNewBuiltinWithOldStream();
void implicitConstruction();
void iterateContainerElements();
private:
void dataStream_data(QDataStream::Version version);
void loadQVariantFromDataStream(QDataStream::Version version);
@ -3350,5 +3352,168 @@ void tst_QVariant::saveNewBuiltinWithOldStream()
QCOMPARE(int(data.constData()[3]), 0);
}
template<typename Container, typename Value_Type = typename Container::value_type>
struct ContainerAPI
{
static void insert(Container &container, typename Container::value_type value)
{
container.push_back(value);
}
static bool compare(const QVariant &variant, typename Container::value_type value)
{
return variant.value<typename Container::value_type>() == value;
}
};
template<typename Container>
struct ContainerAPI<Container, QString>
{
static void insert(Container &container, int value)
{
container.push_back(QString::number(value));
}
static bool compare(const QVariant &variant, QString value)
{
return variant.value<QString>() == value;
}
};
// We have no built-in defines to check the stdlib features.
// #define TEST_FORWARD_LIST
#ifdef TEST_FORWARD_LIST
#include <forward_list>
Q_DECLARE_METATYPE(std::forward_list<int>)
Q_DECLARE_METATYPE(std::forward_list<QVariant>)
Q_DECLARE_METATYPE(std::forward_list<QString>)
template<typename Value_Type>
struct ContainerAPI<std::forward_list<Value_Type> >
{
static void insert(std::forward_list<Value_Type> &container, Value_Type value)
{
container.push_front(value);
}
static bool compare(const QVariant &variant, Value_Type value)
{
return variant.value<Value_Type>() == value;
}
};
template<>
struct ContainerAPI<std::forward_list<QString> >
{
static void insert(std::forward_list<QString> &container, int value)
{
container.push_front(QString::number(value));
}
static bool compare(const QVariant &variant, QString value)
{
return variant.value<QString>() == value;
}
};
#endif
void tst_QVariant::iterateContainerElements()
{
#ifdef Q_COMPILER_RANGE_FOR
#define TEST_RANGE_FOR(CONTAINER, VALUE_TYPE) \
numSeen = 0; \
containerIter = intList.begin(); \
for (QVariant v : listIter) { \
QVERIFY(ContainerAPI<CONTAINER<VALUE_TYPE > >::compare(v, *containerIter)); \
++containerIter; \
++numSeen; \
} \
QCOMPARE(numSeen, (int)std::distance(intList.begin(), intList.end()));
#else
#define TEST_RANGE_FOR(CONTAINER, VALUE_TYPE)
#endif
#define TEST_SEQUENTIAL_ITERATION(CONTAINER, VALUE_TYPE) \
{ \
int numSeen = 0; \
CONTAINER<VALUE_TYPE > intList; \
ContainerAPI<CONTAINER<VALUE_TYPE > >::insert(intList, 1); \
ContainerAPI<CONTAINER<VALUE_TYPE > >::insert(intList, 2); \
ContainerAPI<CONTAINER<VALUE_TYPE > >::insert(intList, 3); \
\
QVariant listVariant = QVariant::fromValue(intList); \
QVERIFY(listVariant.canConvert<QVariantList>()); \
QSequentialIterable listIter = listVariant.value<QSequentialIterable>(); \
\
CONTAINER<VALUE_TYPE >::iterator containerIter = intList.begin(); \
const CONTAINER<VALUE_TYPE >::iterator containerEnd = intList.end(); \
for (int i = 0; i < listIter.size(); ++i, ++containerIter, ++numSeen) \
{ \
QVERIFY(ContainerAPI<CONTAINER<VALUE_TYPE > >::compare(listIter.at(i), *containerIter)); \
} \
QCOMPARE(numSeen, (int)std::distance(intList.begin(), intList.end())); \
QCOMPARE(containerIter, containerEnd); \
\
containerIter = intList.begin(); \
numSeen = 0; \
Q_FOREACH (const QVariant &v, listIter) { \
QVERIFY(ContainerAPI<CONTAINER<VALUE_TYPE > >::compare(v, *containerIter)); \
++containerIter; \
++numSeen; \
} \
QCOMPARE(numSeen, (int)std::distance(intList.begin(), intList.end())); \
TEST_RANGE_FOR(CONTAINER, VALUE_TYPE) \
}
qRegisterSequentialConverter<QVector<int> >();
qRegisterSequentialConverter<QVector<QVariant> >();
qRegisterSequentialConverter<QVector<QString> >();
qRegisterSequentialConverter<QQueue<int> >();
qRegisterSequentialConverter<QQueue<QVariant> >();
qRegisterSequentialConverter<QQueue<QString> >();
qRegisterSequentialConverter<QList<int> >();
qRegisterSequentialConverter<QList<QVariant> >();
qRegisterSequentialConverter<QList<QString> >();
qRegisterSequentialConverter<QStack<int> >();
qRegisterSequentialConverter<QStack<QVariant> >();
qRegisterSequentialConverter<QStack<QString> >();
qRegisterSequentialConverter<std::vector<int> >();
qRegisterSequentialConverter<std::vector<QVariant> >();
qRegisterSequentialConverter<std::vector<QString> >();
qRegisterSequentialConverter<std::list<int> >();
qRegisterSequentialConverter<std::list<QVariant> >();
qRegisterSequentialConverter<std::list<QString> >();
TEST_SEQUENTIAL_ITERATION(QVector, int)
TEST_SEQUENTIAL_ITERATION(QVector, QVariant)
TEST_SEQUENTIAL_ITERATION(QVector, QString)
TEST_SEQUENTIAL_ITERATION(QQueue, int)
TEST_SEQUENTIAL_ITERATION(QQueue, QVariant)
TEST_SEQUENTIAL_ITERATION(QQueue, QString)
TEST_SEQUENTIAL_ITERATION(QList, int)
TEST_SEQUENTIAL_ITERATION(QList, QVariant)
TEST_SEQUENTIAL_ITERATION(QList, QString)
TEST_SEQUENTIAL_ITERATION(QStack, int)
TEST_SEQUENTIAL_ITERATION(QStack, QVariant)
TEST_SEQUENTIAL_ITERATION(QStack, QString)
TEST_SEQUENTIAL_ITERATION(std::vector, int)
TEST_SEQUENTIAL_ITERATION(std::vector, QVariant)
TEST_SEQUENTIAL_ITERATION(std::vector, QString)
TEST_SEQUENTIAL_ITERATION(std::list, int)
TEST_SEQUENTIAL_ITERATION(std::list, QVariant)
TEST_SEQUENTIAL_ITERATION(std::list, QString)
#ifdef TEST_FORWARD_LIST
qRegisterSequentialConverter<std::forward_list<int> >();
qRegisterSequentialConverter<std::forward_list<QVariant> >();
qRegisterSequentialConverter<std::forward_list<QString> >();
TEST_SEQUENTIAL_ITERATION(std::forward_list, int)
TEST_SEQUENTIAL_ITERATION(std::forward_list, QVariant)
TEST_SEQUENTIAL_ITERATION(std::forward_list, QString)
#endif
}
QTEST_MAIN(tst_QVariant)
#include "tst_qvariant.moc"