Add container access functionality for associative containers in QVariant.

Change-Id: I4763a4c157e52918a0a68cba63624c0649aca235
Reviewed-by: Jędrzej Nowacki <jedrzej.nowacki@digia.com>
bb10
Stephen Kelly 2013-04-05 13:23:38 +02:00 committed by The Qt Project
parent 65f953407a
commit 87e27eb870
6 changed files with 725 additions and 4 deletions

View File

@ -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]

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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)