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:
parent
f7b313e6d8
commit
01fb843af8
@ -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]
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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"
|
||||
|
Loading…
Reference in New Issue
Block a user