Add container access functionality for associative containers in QVariant.
Change-Id: I4763a4c157e52918a0a68cba63624c0649aca235 Reviewed-by: Jędrzej Nowacki <jedrzej.nowacki@digia.com>bb10
parent
65f953407a
commit
87e27eb870
|
|
@ -162,3 +162,33 @@ if (variant.canConvert<QVariantList>()) {
|
|||
}
|
||||
|
||||
//! [9]
|
||||
|
||||
//! [10]
|
||||
|
||||
QHash<int, QString> mapping;
|
||||
mapping.insert(7, "Seven");
|
||||
mapping.insert(11, "Eleven");
|
||||
mapping.insert(42, "Forty-two");
|
||||
|
||||
QVariant variant = QVariant::fromValue(mapping);
|
||||
if (variant.canConvert<QVariantHash>()) {
|
||||
QAssociativeIterable iterable = variant.value<QAssociativeIterable>();
|
||||
// Can use foreach over the values:
|
||||
foreach (const QVariant &v, iterable) {
|
||||
qDebug() << v;
|
||||
}
|
||||
// Can use C++11 range-for over the values:
|
||||
for (const QVariant &v : iterable) {
|
||||
qDebug() << v;
|
||||
}
|
||||
// Can use iterators:
|
||||
QAssociativeIterable::const_iterator it = iterable.begin();
|
||||
const QAssociativeIterable::const_iterator end = iterable.end();
|
||||
for ( ; it != end; ++it) {
|
||||
qDebug() << *it; // The current value
|
||||
qDebug() << it.key();
|
||||
qDebug() << it.value();
|
||||
}
|
||||
}
|
||||
|
||||
//! [10]
|
||||
|
|
|
|||
|
|
@ -1990,6 +1990,21 @@ const QMetaObject *QMetaType::metaObjectForType(int type)
|
|||
\sa QVariant::canConvert()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\fn bool qRegisterAssociativeConverter()
|
||||
\relates QMetaType
|
||||
\since 5.2
|
||||
|
||||
Registers an associative container so that it can be converted to
|
||||
a QVariantHash or QVariantMap. If the key_type and mapped_type of the container
|
||||
was not declared with Q_DECLARE_METATYPE(), compilation will fail.
|
||||
|
||||
Note that it is not necessary to call this method for Qt containers (QHash,
|
||||
QMap etc) or for std::map. Such containers are automatically registered by Qt.
|
||||
|
||||
\sa QVariant::canConvert()
|
||||
*/
|
||||
|
||||
namespace {
|
||||
class TypeInfo {
|
||||
template<typename T, bool IsAcceptedType = DefinedTypesFilter::Acceptor<T>::IsAccepted>
|
||||
|
|
|
|||
|
|
@ -296,6 +296,8 @@ struct ConverterFunctor : public AbstractConverterFunction
|
|||
|
||||
template<typename T, bool>
|
||||
struct ValueTypeIsMetaType;
|
||||
template<typename T, bool>
|
||||
struct AssociativeValueTypeIsMetaType;
|
||||
}
|
||||
|
||||
class Q_CORE_EXPORT QMetaType {
|
||||
|
|
@ -531,6 +533,9 @@ private:
|
|||
template<typename, typename> friend struct QtPrivate::ConverterMemberFunction;
|
||||
template<typename, typename> friend struct QtPrivate::ConverterMemberFunctionOk;
|
||||
template<typename, typename, typename> friend struct QtPrivate::ConverterFunctor;
|
||||
template<typename T>
|
||||
friend bool qRegisterAssociativeConverter();
|
||||
template<typename, bool> friend struct QtPrivate::AssociativeValueTypeIsMetaType;
|
||||
#endif
|
||||
#else
|
||||
public:
|
||||
|
|
@ -876,6 +881,173 @@ struct QSequentialIterableConvertFunctor
|
|||
};
|
||||
}
|
||||
|
||||
namespace QtMetaTypePrivate {
|
||||
template<typename T, bool = QtPrivate::is_same<typename T::const_iterator::value_type, typename T::mapped_type>::value>
|
||||
struct AssociativeContainerAccessor
|
||||
{
|
||||
static const typename T::key_type& getKey(const typename T::const_iterator &it)
|
||||
{
|
||||
return it.key();
|
||||
}
|
||||
|
||||
static const typename T::mapped_type& getValue(const typename T::const_iterator &it)
|
||||
{
|
||||
return it.value();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, bool = QtPrivate::is_same<typename T::const_iterator::value_type, std::pair<const typename T::key_type, typename T::mapped_type> >::value>
|
||||
struct StlStyleAssociativeContainerAccessor;
|
||||
|
||||
template<typename T>
|
||||
struct StlStyleAssociativeContainerAccessor<T, true>
|
||||
{
|
||||
static const typename T::key_type& getKey(const typename T::const_iterator &it)
|
||||
{
|
||||
return it->first;
|
||||
}
|
||||
|
||||
static const typename T::mapped_type& getValue(const typename T::const_iterator &it)
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct AssociativeContainerAccessor<T, false> : public StlStyleAssociativeContainerAccessor<T>
|
||||
{
|
||||
};
|
||||
|
||||
class QAssociativeIterableImpl
|
||||
{
|
||||
public:
|
||||
const void *_iterable;
|
||||
void *_iterator;
|
||||
int _metaType_id_key;
|
||||
uint _metaType_flags_key;
|
||||
int _metaType_id_value;
|
||||
uint _metaType_flags_value;
|
||||
typedef int(*sizeFunc)(const void *p);
|
||||
typedef void (*findFunc)(const void *container, const void *p, void **iterator);
|
||||
typedef void (*beginFunc)(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;
|
||||
findFunc _find;
|
||||
beginFunc _begin;
|
||||
beginFunc _end;
|
||||
advanceFunc _advance;
|
||||
getFunc _getKey;
|
||||
getFunc _getValue;
|
||||
destroyIterFunc _destroyIter;
|
||||
equalIterFunc _equalIter;
|
||||
|
||||
template<class T>
|
||||
static int sizeImpl(const void *p)
|
||||
{ return std::distance(static_cast<const T*>(p)->begin(),
|
||||
static_cast<const T*>(p)->end()); }
|
||||
|
||||
template<class T>
|
||||
static void findImpl(const void *container, const void *p, void **iterator)
|
||||
{ IteratorOwner<typename T::const_iterator>::assign(iterator,
|
||||
static_cast<const T*>(container)->find(*static_cast<const typename T::key_type*>(p))); }
|
||||
|
||||
template<class T>
|
||||
static void advanceImpl(void **p, int step)
|
||||
{ std::advance(*static_cast<typename T::const_iterator*>(*p), step); }
|
||||
|
||||
template<class T>
|
||||
static void beginImpl(const void *container, void **iterator)
|
||||
{ IteratorOwner<typename T::const_iterator>::assign(iterator, static_cast<const T*>(container)->begin()); }
|
||||
|
||||
template<class T>
|
||||
static void endImpl(const void *container, void **iterator)
|
||||
{ IteratorOwner<typename T::const_iterator>::assign(iterator, static_cast<const T*>(container)->end()); }
|
||||
|
||||
template<class T>
|
||||
static VariantData getKeyImpl(void * const *iterator, int metaTypeId, uint flags)
|
||||
{ return VariantData(metaTypeId, &AssociativeContainerAccessor<T>::getKey(*static_cast<typename T::const_iterator*>(*iterator)), flags); }
|
||||
|
||||
template<class T>
|
||||
static VariantData getValueImpl(void * const *iterator, int metaTypeId, uint flags)
|
||||
{ return VariantData(metaTypeId, &AssociativeContainerAccessor<T>::getValue(*static_cast<typename T::const_iterator*>(*iterator)), flags); }
|
||||
|
||||
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); }
|
||||
|
||||
public:
|
||||
template<class T> QAssociativeIterableImpl(const T*p)
|
||||
: _iterable(p)
|
||||
, _metaType_id_key(qMetaTypeId<typename T::key_type>())
|
||||
, _metaType_flags_key(QTypeInfo<typename T::key_type>::isPointer)
|
||||
, _metaType_id_value(qMetaTypeId<typename T::mapped_type>())
|
||||
, _metaType_flags_value(QTypeInfo<typename T::mapped_type>::isPointer)
|
||||
, _size(sizeImpl<T>)
|
||||
, _find(findImpl<T>)
|
||||
, _begin(beginImpl<T>)
|
||||
, _end(endImpl<T>)
|
||||
, _advance(advanceImpl<T>)
|
||||
, _getKey(getKeyImpl<T>)
|
||||
, _getValue(getValueImpl<T>)
|
||||
, _destroyIter(destroyIterImpl<T>)
|
||||
, _equalIter(equalIterImpl<T>)
|
||||
{
|
||||
}
|
||||
|
||||
QAssociativeIterableImpl()
|
||||
: _iterable(0)
|
||||
, _metaType_id_key(QMetaType::UnknownType)
|
||||
, _metaType_flags_key(0)
|
||||
, _metaType_id_value(QMetaType::UnknownType)
|
||||
, _metaType_flags_value(0)
|
||||
, _size(0)
|
||||
, _find(0)
|
||||
, _begin(0)
|
||||
, _end(0)
|
||||
, _advance(0)
|
||||
, _getKey(0)
|
||||
, _getValue(0)
|
||||
, _destroyIter(0)
|
||||
, _equalIter(0)
|
||||
{
|
||||
}
|
||||
|
||||
inline void begin() { _begin(_iterable, &_iterator); }
|
||||
inline void end() { _end(_iterable, &_iterator); }
|
||||
inline bool equal(const QAssociativeIterableImpl&other) const { return _equalIter(&_iterator, &other._iterator); }
|
||||
inline QAssociativeIterableImpl &advance(int i) { _advance(&_iterator, i); return *this; }
|
||||
|
||||
inline void destroyIter() { _destroyIter(&_iterator); }
|
||||
|
||||
inline VariantData getCurrentKey() const { return _getKey(&_iterator, _metaType_id_key, _metaType_flags_value); }
|
||||
inline VariantData getCurrentValue() const { return _getValue(&_iterator, _metaType_id_value, _metaType_flags_value); }
|
||||
|
||||
inline void find(const VariantData &key)
|
||||
{ _find(_iterable, key.data, &_iterator); }
|
||||
|
||||
int size() const { Q_ASSERT(_iterable); return _size(_iterable); }
|
||||
};
|
||||
|
||||
template<typename From>
|
||||
struct QAssociativeIterableConvertFunctor
|
||||
{
|
||||
QAssociativeIterableConvertFunctor() {}
|
||||
|
||||
QAssociativeIterableImpl operator()(const From& f) const
|
||||
{
|
||||
return QAssociativeIterableImpl(&f);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
class QObject;
|
||||
class QWidget;
|
||||
template <class T> class QSharedPointer;
|
||||
|
|
@ -978,6 +1150,23 @@ namespace QtPrivate
|
|||
QT_DEFINE_SEQUENTIAL_CONTAINER_TYPE(std::vector)
|
||||
QT_DEFINE_SEQUENTIAL_CONTAINER_TYPE(std::list)
|
||||
|
||||
template<typename T>
|
||||
struct IsAssociativeContainer
|
||||
{
|
||||
enum { Value = false };
|
||||
};
|
||||
|
||||
#define QT_DEFINE_ASSOCIATIVE_CONTAINER_TYPE(CONTAINER) \
|
||||
template<typename T, typename U> \
|
||||
struct IsAssociativeContainer<CONTAINER<T, U> > \
|
||||
{ \
|
||||
enum { Value = true }; \
|
||||
};
|
||||
QT_DEFINE_ASSOCIATIVE_CONTAINER_TYPE(QHash)
|
||||
QT_DEFINE_ASSOCIATIVE_CONTAINER_TYPE(QMap)
|
||||
QT_DEFINE_ASSOCIATIVE_CONTAINER_TYPE(std::map)
|
||||
|
||||
|
||||
template<typename T, bool = QtPrivate::IsSequentialContainer<T>::Value>
|
||||
struct SequentialContainerConverterHelper
|
||||
{
|
||||
|
|
@ -1018,6 +1207,60 @@ namespace QtPrivate
|
|||
{
|
||||
};
|
||||
|
||||
template<typename T, bool = QtPrivate::IsAssociativeContainer<T>::Value>
|
||||
struct AssociativeContainerConverterHelper
|
||||
{
|
||||
static bool registerConverter(int)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, bool = QMetaTypeId2<typename T::mapped_type>::Defined>
|
||||
struct AssociativeValueTypeIsMetaType
|
||||
{
|
||||
static bool registerConverter(int)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct AssociativeValueTypeIsMetaType<T, true>
|
||||
{
|
||||
static bool registerConverter(int id)
|
||||
{
|
||||
const int toId = qMetaTypeId<QtMetaTypePrivate::QAssociativeIterableImpl>();
|
||||
if (!QMetaType::hasRegisteredConverterFunction(id, toId)) {
|
||||
static const QtMetaTypePrivate::QAssociativeIterableConvertFunctor<T> o;
|
||||
static const QtPrivate::ConverterFunctor<T,
|
||||
QtMetaTypePrivate::QAssociativeIterableImpl,
|
||||
QtMetaTypePrivate::QAssociativeIterableConvertFunctor<T> > f(o);
|
||||
return QMetaType::registerConverterFunction(&f, id, toId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, bool = QMetaTypeId2<typename T::key_type>::Defined>
|
||||
struct KeyAndValueTypeIsMetaType
|
||||
{
|
||||
static bool registerConverter(int)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct KeyAndValueTypeIsMetaType<T, true> : AssociativeValueTypeIsMetaType<T>
|
||||
{
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct AssociativeContainerConverterHelper<T, true> : KeyAndValueTypeIsMetaType<T>
|
||||
{
|
||||
};
|
||||
|
||||
Q_CORE_EXPORT bool isBuiltinType(const QByteArray &type);
|
||||
} // namespace QtPrivate
|
||||
|
||||
|
|
@ -1118,6 +1361,7 @@ int qRegisterNormalizedMetaType(const QT_PREPEND_NAMESPACE(QByteArray) &normaliz
|
|||
|
||||
if (id > 0) {
|
||||
QtPrivate::SequentialContainerConverterHelper<T>::registerConverter(id);
|
||||
QtPrivate::AssociativeContainerConverterHelper<T>::registerConverter(id);
|
||||
}
|
||||
|
||||
return id;
|
||||
|
|
@ -1501,6 +1745,7 @@ QT_END_NAMESPACE
|
|||
QT_FOR_EACH_STATIC_TYPE(Q_DECLARE_BUILTIN_METATYPE)
|
||||
|
||||
Q_DECLARE_METATYPE(QtMetaTypePrivate::QSequentialIterableImpl)
|
||||
Q_DECLARE_METATYPE(QtMetaTypePrivate::QAssociativeIterableImpl)
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
|
|
@ -1520,9 +1765,26 @@ bool qRegisterSequentialConverter()
|
|||
static const QtPrivate::ConverterFunctor<T,
|
||||
QtMetaTypePrivate::QSequentialIterableImpl,
|
||||
QtMetaTypePrivate::QSequentialIterableConvertFunctor<T> > f(o);
|
||||
return QMetaType::registerConverterFunction(
|
||||
&f,
|
||||
id, toId);
|
||||
return QMetaType::registerConverterFunction(&f, id, toId);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool qRegisterAssociativeConverter()
|
||||
{
|
||||
Q_STATIC_ASSERT_X(QMetaTypeId2<typename T::key_type>::Defined
|
||||
&& QMetaTypeId2<typename T::mapped_type>::Defined,
|
||||
"The key_type and mapped_type of an associative container must themselves be metatypes.");
|
||||
|
||||
const int id = qMetaTypeId<T>();
|
||||
const int toId = qMetaTypeId<QtMetaTypePrivate::QAssociativeIterableImpl>();
|
||||
if (QMetaType::hasRegisteredConverterFunction(id, toId))
|
||||
return true;
|
||||
static const QtMetaTypePrivate::QAssociativeIterableConvertFunctor<T> o;
|
||||
static const QtPrivate::ConverterFunctor<T,
|
||||
QtMetaTypePrivate::QAssociativeIterableImpl,
|
||||
QtMetaTypePrivate::QAssociativeIterableConvertFunctor<T> > f(o);
|
||||
|
||||
return QMetaType::registerConverterFunction(&f, id, toId);
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
|
|
|||
|
|
@ -2755,7 +2755,14 @@ static bool canConvertMetaObject(int fromId, int toId, QObject *fromObject)
|
|||
|
||||
This requires that the value_type of the container is itself a metatype.
|
||||
|
||||
\sa convert(), QSequentialIterable, qRegisterSequentialConverter()
|
||||
Similarly, a QVariant containing a sequential container will also return true for this
|
||||
function the \a targetTypeId is QVariantHash or QVariantMap. It is possible to iterate over
|
||||
the contents of the container without extracting it as a (copied) QVariantHash or QVariantMap:
|
||||
|
||||
\snippet code/src_corelib_kernel_qvariant.cpp 10
|
||||
|
||||
\sa convert(), QSequentialIterable, qRegisterSequentialConverter(), QAssociativeIterable,
|
||||
qRegisterAssociativeConverter()
|
||||
*/
|
||||
bool QVariant::canConvert(int targetTypeId) const
|
||||
{
|
||||
|
|
@ -2767,6 +2774,14 @@ bool QVariant::canConvert(int targetTypeId) const
|
|||
return true;
|
||||
}
|
||||
|
||||
if ((targetTypeId == QMetaType::QVariantHash || targetTypeId == QMetaType::QVariantMap)
|
||||
&& (d.type == QMetaType::QVariantMap
|
||||
|| d.type == QMetaType::QVariantHash
|
||||
|| QMetaType::hasRegisteredConverterFunction(d.type,
|
||||
qMetaTypeId<QtMetaTypePrivate::QAssociativeIterableImpl>()))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((d.type >= QMetaType::User || targetTypeId >= QMetaType::User)
|
||||
&& QMetaType::hasRegisteredConverterFunction(d.type, targetTypeId)) {
|
||||
return true;
|
||||
|
|
@ -3423,4 +3438,176 @@ QDebug operator<<(QDebug dbg, const QVariant::Type p)
|
|||
\sa operator+(), operator-=(), canReverseIterate()
|
||||
*/
|
||||
|
||||
/*!
|
||||
\class QAssociativeIterable
|
||||
|
||||
\inmodule QtCore
|
||||
\brief The QAssociativeIterable class is an iterable interface for an associative container in a QVariant.
|
||||
|
||||
This class allows several methods of accessing the elements of an associative container held within
|
||||
a QVariant. An instance of QAssociativeIterable can be extracted from a QVariant if it can
|
||||
be converted to a QVariantHash or QVariantMap.
|
||||
|
||||
\snippet code/src_corelib_kernel_qvariant.cpp 10
|
||||
|
||||
The container itself is not copied before iterating over it.
|
||||
|
||||
\sa QVariant
|
||||
*/
|
||||
|
||||
/*! \fn QAssociativeIterable::QAssociativeIterable(QtMetaTypePrivate::QAssociativeIterableImpl)
|
||||
|
||||
\internal
|
||||
*/
|
||||
|
||||
/*! \fn QAssociativeIterable::const_iterator QAssociativeIterable::begin() const
|
||||
|
||||
Returns a QAssociativeIterable::const_iterator for the beginning of the container. This
|
||||
can be used in stl-style iteration.
|
||||
|
||||
\sa end()
|
||||
*/
|
||||
|
||||
/*! \fn QAssociativeIterable::const_iterator QAssociativeIterable::end() const
|
||||
|
||||
Returns a QAssociativeIterable::const_iterator for the end of the container. This
|
||||
can be used in stl-style iteration.
|
||||
|
||||
\sa begin()
|
||||
*/
|
||||
|
||||
/*! \fn QVariant QAssociativeIterable::value(const QVariant &key) const
|
||||
|
||||
Returns the value for the given \a key in the container, if the types are convertible.
|
||||
*/
|
||||
|
||||
/*! \fn int QAssociativeIterable::size() const
|
||||
|
||||
Returns the number of elements in the container.
|
||||
*/
|
||||
|
||||
/*!
|
||||
\class QAssociativeIterable::const_iterator
|
||||
|
||||
\inmodule QtCore
|
||||
\brief The QAssociativeIterable::const_iterator allows iteration over a container in a QVariant.
|
||||
|
||||
A QAssociativeIterable::const_iterator can only be created by a QAssociativeIterable instance,
|
||||
and can be used in a way similar to other stl-style iterators.
|
||||
|
||||
\snippet code/src_corelib_kernel_qvariant.cpp 10
|
||||
|
||||
\sa QAssociativeIterable
|
||||
*/
|
||||
|
||||
|
||||
/*! \fn QAssociativeIterable::const_iterator::~const_iterator()
|
||||
|
||||
Destroys the QAssociativeIterable::const_iterator.
|
||||
*/
|
||||
|
||||
/*! \fn QAssociativeIterable::const_iterator::const_iterator(const const_iterator &other)
|
||||
|
||||
Creates a copy of \a other.
|
||||
*/
|
||||
|
||||
/*! \fn QVariant QAssociativeIterable::const_iterator::operator*() const
|
||||
|
||||
Returns the current value, converted to a QVariant.
|
||||
*/
|
||||
|
||||
/*! \fn QVariant QAssociativeIterable::const_iterator::key() const
|
||||
|
||||
Returns the current key, converted to a QVariant.
|
||||
*/
|
||||
|
||||
/*! \fn QVariant QAssociativeIterable::const_iterator::value() const
|
||||
|
||||
Returns the current value, converted to a QVariant.
|
||||
*/
|
||||
|
||||
/*! \fn bool QAssociativeIterable::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 QAssociativeIterable::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 QAssociativeIterable::const_iterator &QAssociativeIterable::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 QAssociativeIterable::end() leads to undefined results.
|
||||
|
||||
\sa operator--()
|
||||
*/
|
||||
|
||||
/*! \fn QAssociativeIterable::const_iterator QAssociativeIterable::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 QAssociativeIterable::const_iterator &QAssociativeIterable::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 QAssociativeIterable::begin() leads to undefined results.
|
||||
|
||||
\sa operator++()
|
||||
*/
|
||||
|
||||
/*! \fn QAssociativeIterable::const_iterator QAssociativeIterable::const_iterator::operator--(int)
|
||||
|
||||
\overload
|
||||
|
||||
The postfix -- operator (\c{it--}) makes the preceding item
|
||||
current and returns an iterator to the previously current item.
|
||||
*/
|
||||
|
||||
/*! \fn QAssociativeIterable::const_iterator &QAssociativeIterable::const_iterator::operator+=(int j)
|
||||
|
||||
Advances the iterator by \a j items.
|
||||
|
||||
\sa operator-=(), operator+()
|
||||
*/
|
||||
|
||||
/*! \fn QAssociativeIterable::const_iterator &QAssociativeIterable::const_iterator::operator-=(int j)
|
||||
|
||||
Makes the iterator go back by \a j items.
|
||||
|
||||
\sa operator+=(), operator-()
|
||||
*/
|
||||
|
||||
/*! \fn QAssociativeIterable::const_iterator QAssociativeIterable::const_iterator::operator+(int j) const
|
||||
|
||||
Returns an iterator to the item at \a j positions forward from
|
||||
this iterator.
|
||||
|
||||
\sa operator-(), operator+=()
|
||||
*/
|
||||
|
||||
/*! \fn QAssociativeIterable::const_iterator QAssociativeIterable::const_iterator::operator-(int j) const
|
||||
|
||||
Returns an iterator to the item at \a j positions backward from
|
||||
this iterator.
|
||||
|
||||
\sa operator+(), operator-=()
|
||||
*/
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
|
|
|||
|
|
@ -632,6 +632,103 @@ public:
|
|||
{ return m_impl._iteratorCapabilities & QtMetaTypePrivate::BiDirectionalCapability; }
|
||||
};
|
||||
|
||||
class QAssociativeIterable
|
||||
{
|
||||
QtMetaTypePrivate::QAssociativeIterableImpl m_impl;
|
||||
public:
|
||||
struct const_iterator
|
||||
{
|
||||
private:
|
||||
QtMetaTypePrivate::QAssociativeIterableImpl m_impl;
|
||||
QAtomicInt *ref;
|
||||
friend class QAssociativeIterable;
|
||||
inline explicit const_iterator(const QAssociativeIterable &iter, QAtomicInt *ref_)
|
||||
: m_impl(iter.m_impl), ref(ref_) { ref->ref(); }
|
||||
|
||||
inline explicit const_iterator(const QtMetaTypePrivate::QAssociativeIterableImpl &impl, QAtomicInt *ref_)
|
||||
: m_impl(impl), ref(ref_) { ref->ref(); }
|
||||
|
||||
inline void begin() { m_impl.begin(); }
|
||||
inline void end() { m_impl.end(); }
|
||||
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 key() const {
|
||||
const QtMetaTypePrivate::VariantData d = m_impl.getCurrentKey();
|
||||
QVariant v(d.metaTypeId, d.data, d.flags);
|
||||
if (d.metaTypeId == qMetaTypeId<QVariant>())
|
||||
return *reinterpret_cast<const QVariant*>(d.data);
|
||||
return v;
|
||||
}
|
||||
|
||||
inline const QVariant value() const {
|
||||
const QtMetaTypePrivate::VariantData d = m_impl.getCurrentValue();
|
||||
QVariant v(d.metaTypeId, d.data, d.flags);
|
||||
if (d.metaTypeId == qMetaTypeId<QVariant>())
|
||||
return *reinterpret_cast<const QVariant*>(d.data);
|
||||
return v;
|
||||
}
|
||||
|
||||
inline const QVariant operator*() const {
|
||||
const QtMetaTypePrivate::VariantData d = m_impl.getCurrentValue();
|
||||
QVariant v(d.metaTypeId, d.data, d.flags);
|
||||
if (d.metaTypeId == qMetaTypeId<QVariant>())
|
||||
return *reinterpret_cast<const QVariant*>(d.data);
|
||||
return v;
|
||||
}
|
||||
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::QAssociativeIterableImpl 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::QAssociativeIterableImpl 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::QAssociativeIterableImpl impl = m_impl; impl.advance(j); return const_iterator(impl, this->ref); }
|
||||
inline const_iterator operator-(int j) const { QtMetaTypePrivate::QAssociativeIterableImpl impl = m_impl; impl.advance(-j); return const_iterator(impl, this->ref); }
|
||||
};
|
||||
|
||||
friend struct const_iterator;
|
||||
|
||||
explicit QAssociativeIterable(QtMetaTypePrivate::QAssociativeIterableImpl 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 value(const QVariant &key) const
|
||||
{
|
||||
QVariant key_ = key;
|
||||
if (!key_.canConvert(m_impl._metaType_id_key))
|
||||
return QVariant();
|
||||
if (!key_.convert(m_impl._metaType_id_key))
|
||||
return QVariant();
|
||||
const QtMetaTypePrivate::VariantData dkey(key_.userType(), key_.constData(), 0 /*key.flags()*/);
|
||||
QtMetaTypePrivate::QAssociativeIterableImpl impl = m_impl;
|
||||
impl.find(dkey);
|
||||
QtMetaTypePrivate::QAssociativeIterableImpl endIt = m_impl;
|
||||
endIt.end();
|
||||
if (impl.equal(endIt))
|
||||
return QVariant();
|
||||
const QtMetaTypePrivate::VariantData d = impl.getCurrentValue();
|
||||
QVariant v(d.metaTypeId, d.data, d.flags);
|
||||
if (d.metaTypeId == qMetaTypeId<QVariant>())
|
||||
return *reinterpret_cast<const QVariant*>(d.data);
|
||||
return v;
|
||||
}
|
||||
|
||||
int size() const { return m_impl.size(); }
|
||||
};
|
||||
|
||||
#ifndef QT_MOC
|
||||
namespace QtPrivate {
|
||||
template<typename T>
|
||||
|
|
@ -675,6 +772,20 @@ namespace QtPrivate {
|
|||
}
|
||||
};
|
||||
template<>
|
||||
struct QVariantValueHelperInterface<QAssociativeIterable>
|
||||
{
|
||||
static QAssociativeIterable invoke(const QVariant &v)
|
||||
{
|
||||
if (v.userType() == qMetaTypeId<QVariantMap>()) {
|
||||
return QAssociativeIterable(QtMetaTypePrivate::QAssociativeIterableImpl(reinterpret_cast<const QVariantMap*>(v.constData())));
|
||||
}
|
||||
if (v.userType() == qMetaTypeId<QVariantHash>()) {
|
||||
return QAssociativeIterable(QtMetaTypePrivate::QAssociativeIterableImpl(reinterpret_cast<const QVariantHash*>(v.constData())));
|
||||
}
|
||||
return QAssociativeIterable(v.value<QtMetaTypePrivate::QAssociativeIterableImpl>());
|
||||
}
|
||||
};
|
||||
template<>
|
||||
struct QVariantValueHelperInterface<QVariantList>
|
||||
{
|
||||
static QVariantList invoke(const QVariant &v)
|
||||
|
|
@ -690,6 +801,37 @@ namespace QtPrivate {
|
|||
return QVariantValueHelper<QVariantList>::invoke(v);
|
||||
}
|
||||
};
|
||||
template<>
|
||||
struct QVariantValueHelperInterface<QVariantHash>
|
||||
{
|
||||
static QVariantHash invoke(const QVariant &v)
|
||||
{
|
||||
if (QMetaType::hasRegisteredConverterFunction(v.userType(), qMetaTypeId<QtMetaTypePrivate::QAssociativeIterableImpl>())) {
|
||||
QAssociativeIterable iter = QVariantValueHelperInterface<QAssociativeIterable>::invoke(v);
|
||||
QVariantHash l;
|
||||
l.reserve(iter.size());
|
||||
for (QAssociativeIterable::const_iterator it = iter.begin(), end = iter.end(); it != end; ++it)
|
||||
l.insert(it.key().toString(), it.value());
|
||||
return l;
|
||||
}
|
||||
return QVariantValueHelper<QVariantHash>::invoke(v);
|
||||
}
|
||||
};
|
||||
template<>
|
||||
struct QVariantValueHelperInterface<QVariantMap>
|
||||
{
|
||||
static QVariantMap invoke(const QVariant &v)
|
||||
{
|
||||
if (QMetaType::hasRegisteredConverterFunction(v.userType(), qMetaTypeId<QtMetaTypePrivate::QAssociativeIterableImpl>())) {
|
||||
QAssociativeIterable iter = QVariantValueHelperInterface<QAssociativeIterable>::invoke(v);
|
||||
QVariantMap l;
|
||||
for (QAssociativeIterable::const_iterator it = iter.begin(), end = iter.end(); it != end; ++it)
|
||||
l.insert(it.key().toString(), it.value());
|
||||
return l;
|
||||
}
|
||||
return QVariantValueHelper<QVariantMap>::invoke(v);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template<typename T> inline T qvariant_cast(const QVariant &v)
|
||||
|
|
|
|||
|
|
@ -3460,6 +3460,55 @@ struct ContainerAPI<std::forward_list<QString> >
|
|||
};
|
||||
#endif
|
||||
|
||||
template<typename Container>
|
||||
struct KeyGetter
|
||||
{
|
||||
static const typename Container::key_type & get(const typename Container::const_iterator &it)
|
||||
{
|
||||
return it.key();
|
||||
}
|
||||
static const typename Container::mapped_type & value(const typename Container::const_iterator &it)
|
||||
{
|
||||
return it.value();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T, typename U>
|
||||
struct KeyGetter<std::map<T, U> >
|
||||
{
|
||||
static const T & get(const typename std::map<T, U>::const_iterator &it)
|
||||
{
|
||||
return it->first;
|
||||
}
|
||||
static const U & value(const typename std::map<T, U>::const_iterator &it)
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// We have no built-in defines to check the stdlib features.
|
||||
// #define TEST_UNORDERED_MAP
|
||||
|
||||
#ifdef TEST_UNORDERED_MAP
|
||||
#include <unordered_map>
|
||||
typedef std::unordered_map<int, bool> StdUnorderedMap_int_bool;
|
||||
Q_DECLARE_METATYPE(StdUnorderedMap_int_bool)
|
||||
|
||||
template<typename T, typename U>
|
||||
struct KeyGetter<std::unordered_map<T, U> >
|
||||
{
|
||||
static const T & get(const typename std::unordered_map<T, U>::const_iterator &it)
|
||||
{
|
||||
return it->first;
|
||||
}
|
||||
static const U & value(const typename std::unordered_map<T, U>::const_iterator &it)
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
void tst_QVariant::iterateContainerElements()
|
||||
{
|
||||
#ifdef Q_COMPILER_RANGE_FOR
|
||||
|
|
@ -3545,6 +3594,42 @@ void tst_QVariant::iterateContainerElements()
|
|||
TEST_SEQUENTIAL_ITERATION(std::forward_list, QVariant)
|
||||
TEST_SEQUENTIAL_ITERATION(std::forward_list, QString)
|
||||
#endif
|
||||
|
||||
#define TEST_ASSOCIATIVE_ITERATION(CONTAINER, KEY_TYPE, MAPPED_TYPE) \
|
||||
{ \
|
||||
int numSeen = 0; \
|
||||
CONTAINER<KEY_TYPE, MAPPED_TYPE> mapping; \
|
||||
mapping[5] = true; \
|
||||
mapping[15] = false; \
|
||||
\
|
||||
QVariant mappingVariant = QVariant::fromValue(mapping); \
|
||||
QVariantMap varMap = mappingVariant.value<QVariantMap>(); \
|
||||
QVariantMap varHash = mappingVariant.value<QVariantMap>(); \
|
||||
QAssociativeIterable mappingIter = mappingVariant.value<QAssociativeIterable>(); \
|
||||
\
|
||||
CONTAINER<KEY_TYPE, MAPPED_TYPE>::const_iterator containerIter = mapping.begin(); \
|
||||
const CONTAINER<KEY_TYPE, MAPPED_TYPE>::const_iterator containerEnd = mapping.end(); \
|
||||
for ( ; containerIter != containerEnd; ++containerIter, ++numSeen) \
|
||||
{ \
|
||||
MAPPED_TYPE expected = KeyGetter<CONTAINER<KEY_TYPE, MAPPED_TYPE> >::value(containerIter); \
|
||||
KEY_TYPE key = KeyGetter<CONTAINER<KEY_TYPE, MAPPED_TYPE> >::get(containerIter); \
|
||||
MAPPED_TYPE actual = mappingIter.value(key).value<MAPPED_TYPE >(); \
|
||||
QCOMPARE(varMap.value(QString::number(key)).value<MAPPED_TYPE>(), expected); \
|
||||
QCOMPARE(varHash.value(QString::number(key)).value<MAPPED_TYPE>(), expected); \
|
||||
QCOMPARE(actual, expected); \
|
||||
} \
|
||||
QCOMPARE(numSeen, (int)std::distance(mapping.begin(), mapping.end())); \
|
||||
QCOMPARE(containerIter, containerEnd); \
|
||||
\
|
||||
}
|
||||
|
||||
TEST_ASSOCIATIVE_ITERATION(QHash, int, bool)
|
||||
TEST_ASSOCIATIVE_ITERATION(QMap, int, bool)
|
||||
TEST_ASSOCIATIVE_ITERATION(std::map, int, bool)
|
||||
#ifdef TEST_UNORDERED_MAP
|
||||
qRegisterAssociativeConverter<StdUnorderedMap_int_bool>();
|
||||
TEST_ASSOCIATIVE_ITERATION(std::unordered_map, int, bool)
|
||||
#endif
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QVariant)
|
||||
|
|
|
|||
Loading…
Reference in New Issue