From 87e27eb870d08ee8953cc9b350ed29c5b3e4f785 Mon Sep 17 00:00:00 2001 From: Stephen Kelly Date: Fri, 5 Apr 2013 13:23:38 +0200 Subject: Add container access functionality for associative containers in QVariant. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: I4763a4c157e52918a0a68cba63624c0649aca235 Reviewed-by: Jędrzej Nowacki --- src/corelib/kernel/qmetatype.cpp | 15 +++ src/corelib/kernel/qmetatype.h | 268 ++++++++++++++++++++++++++++++++++++++- src/corelib/kernel/qvariant.cpp | 189 ++++++++++++++++++++++++++- src/corelib/kernel/qvariant.h | 142 +++++++++++++++++++++ 4 files changed, 610 insertions(+), 4 deletions(-) (limited to 'src/corelib/kernel') diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp index 5c3f252395..bfc48774b5 100644 --- a/src/corelib/kernel/qmetatype.cpp +++ b/src/corelib/kernel/qmetatype.cpp @@ -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::IsAccepted> diff --git a/src/corelib/kernel/qmetatype.h b/src/corelib/kernel/qmetatype.h index 302c5da63a..ef741aeff9 100644 --- a/src/corelib/kernel/qmetatype.h +++ b/src/corelib/kernel/qmetatype.h @@ -296,6 +296,8 @@ struct ConverterFunctor : public AbstractConverterFunction template struct ValueTypeIsMetaType; + template + struct AssociativeValueTypeIsMetaType; } class Q_CORE_EXPORT QMetaType { @@ -531,6 +533,9 @@ private: template friend struct QtPrivate::ConverterMemberFunction; template friend struct QtPrivate::ConverterMemberFunctionOk; template friend struct QtPrivate::ConverterFunctor; + template + friend bool qRegisterAssociativeConverter(); + template friend struct QtPrivate::AssociativeValueTypeIsMetaType; #endif #else public: @@ -876,6 +881,173 @@ struct QSequentialIterableConvertFunctor }; } +namespace QtMetaTypePrivate { +template::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 >::value> +struct StlStyleAssociativeContainerAccessor; + +template +struct StlStyleAssociativeContainerAccessor +{ + 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 +struct AssociativeContainerAccessor : public StlStyleAssociativeContainerAccessor +{ +}; + +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 + static int sizeImpl(const void *p) + { return std::distance(static_cast(p)->begin(), + static_cast(p)->end()); } + + template + static void findImpl(const void *container, const void *p, void **iterator) + { IteratorOwner::assign(iterator, + static_cast(container)->find(*static_cast(p))); } + + template + static void advanceImpl(void **p, int step) + { std::advance(*static_cast(*p), step); } + + template + static void beginImpl(const void *container, void **iterator) + { IteratorOwner::assign(iterator, static_cast(container)->begin()); } + + template + static void endImpl(const void *container, void **iterator) + { IteratorOwner::assign(iterator, static_cast(container)->end()); } + + template + static VariantData getKeyImpl(void * const *iterator, int metaTypeId, uint flags) + { return VariantData(metaTypeId, &AssociativeContainerAccessor::getKey(*static_cast(*iterator)), flags); } + + template + static VariantData getValueImpl(void * const *iterator, int metaTypeId, uint flags) + { return VariantData(metaTypeId, &AssociativeContainerAccessor::getValue(*static_cast(*iterator)), flags); } + + template + static void destroyIterImpl(void **iterator) + { IteratorOwner::destroy(iterator); } + + template + static bool equalIterImpl(void * const *iterator, void * const *other) + { return *static_cast(*iterator) == *static_cast(*other); } + +public: + template QAssociativeIterableImpl(const T*p) + : _iterable(p) + , _metaType_id_key(qMetaTypeId()) + , _metaType_flags_key(QTypeInfo::isPointer) + , _metaType_id_value(qMetaTypeId()) + , _metaType_flags_value(QTypeInfo::isPointer) + , _size(sizeImpl) + , _find(findImpl) + , _begin(beginImpl) + , _end(endImpl) + , _advance(advanceImpl) + , _getKey(getKeyImpl) + , _getValue(getValueImpl) + , _destroyIter(destroyIterImpl) + , _equalIter(equalIterImpl) + { + } + + 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 +struct QAssociativeIterableConvertFunctor +{ + QAssociativeIterableConvertFunctor() {} + + QAssociativeIterableImpl operator()(const From& f) const + { + return QAssociativeIterableImpl(&f); + } +}; +} + class QObject; class QWidget; template class QSharedPointer; @@ -978,6 +1150,23 @@ namespace QtPrivate QT_DEFINE_SEQUENTIAL_CONTAINER_TYPE(std::vector) QT_DEFINE_SEQUENTIAL_CONTAINER_TYPE(std::list) + template + struct IsAssociativeContainer + { + enum { Value = false }; + }; + +#define QT_DEFINE_ASSOCIATIVE_CONTAINER_TYPE(CONTAINER) \ + template \ + struct IsAssociativeContainer > \ + { \ + enum { Value = true }; \ + }; + QT_DEFINE_ASSOCIATIVE_CONTAINER_TYPE(QHash) + QT_DEFINE_ASSOCIATIVE_CONTAINER_TYPE(QMap) + QT_DEFINE_ASSOCIATIVE_CONTAINER_TYPE(std::map) + + template::Value> struct SequentialContainerConverterHelper { @@ -1018,6 +1207,60 @@ namespace QtPrivate { }; + template::Value> + struct AssociativeContainerConverterHelper + { + static bool registerConverter(int) + { + return false; + } + }; + + template::Defined> + struct AssociativeValueTypeIsMetaType + { + static bool registerConverter(int) + { + return false; + } + }; + + template + struct AssociativeValueTypeIsMetaType + { + static bool registerConverter(int id) + { + const int toId = qMetaTypeId(); + if (!QMetaType::hasRegisteredConverterFunction(id, toId)) { + static const QtMetaTypePrivate::QAssociativeIterableConvertFunctor o; + static const QtPrivate::ConverterFunctor > f(o); + return QMetaType::registerConverterFunction(&f, id, toId); + } + return true; + } + }; + + template::Defined> + struct KeyAndValueTypeIsMetaType + { + static bool registerConverter(int) + { + return false; + } + }; + + template + struct KeyAndValueTypeIsMetaType : AssociativeValueTypeIsMetaType + { + }; + + template + struct AssociativeContainerConverterHelper : KeyAndValueTypeIsMetaType + { + }; + 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::registerConverter(id); + QtPrivate::AssociativeContainerConverterHelper::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 > f(o); - return QMetaType::registerConverterFunction( - &f, - id, toId); + return QMetaType::registerConverterFunction(&f, id, toId); +} + +template +bool qRegisterAssociativeConverter() +{ + Q_STATIC_ASSERT_X(QMetaTypeId2::Defined + && QMetaTypeId2::Defined, + "The key_type and mapped_type of an associative container must themselves be metatypes."); + + const int id = qMetaTypeId(); + const int toId = qMetaTypeId(); + if (QMetaType::hasRegisteredConverterFunction(id, toId)) + return true; + static const QtMetaTypePrivate::QAssociativeIterableConvertFunctor o; + static const QtPrivate::ConverterFunctor > f(o); + + return QMetaType::registerConverterFunction(&f, id, toId); } QT_END_NAMESPACE diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp index 99fdb4fa4a..37b89b82f0 100644 --- a/src/corelib/kernel/qvariant.cpp +++ b/src/corelib/kernel/qvariant.cpp @@ -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()))) { + 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 diff --git a/src/corelib/kernel/qvariant.h b/src/corelib/kernel/qvariant.h index 5588cdb27c..271aaf13ce 100644 --- a/src/corelib/kernel/qvariant.h +++ b/src/corelib/kernel/qvariant.h @@ -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()) + return *reinterpret_cast(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()) + return *reinterpret_cast(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()) + return *reinterpret_cast(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()) + return *reinterpret_cast(d.data); + return v; + } + + int size() const { return m_impl.size(); } +}; + #ifndef QT_MOC namespace QtPrivate { template @@ -675,6 +772,20 @@ namespace QtPrivate { } }; template<> + struct QVariantValueHelperInterface + { + static QAssociativeIterable invoke(const QVariant &v) + { + if (v.userType() == qMetaTypeId()) { + return QAssociativeIterable(QtMetaTypePrivate::QAssociativeIterableImpl(reinterpret_cast(v.constData()))); + } + if (v.userType() == qMetaTypeId()) { + return QAssociativeIterable(QtMetaTypePrivate::QAssociativeIterableImpl(reinterpret_cast(v.constData()))); + } + return QAssociativeIterable(v.value()); + } + }; + template<> struct QVariantValueHelperInterface { static QVariantList invoke(const QVariant &v) @@ -690,6 +801,37 @@ namespace QtPrivate { return QVariantValueHelper::invoke(v); } }; + template<> + struct QVariantValueHelperInterface + { + static QVariantHash invoke(const QVariant &v) + { + if (QMetaType::hasRegisteredConverterFunction(v.userType(), qMetaTypeId())) { + QAssociativeIterable iter = QVariantValueHelperInterface::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::invoke(v); + } + }; + template<> + struct QVariantValueHelperInterface + { + static QVariantMap invoke(const QVariant &v) + { + if (QMetaType::hasRegisteredConverterFunction(v.userType(), qMetaTypeId())) { + QAssociativeIterable iter = QVariantValueHelperInterface::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::invoke(v); + } + }; } template inline T qvariant_cast(const QVariant &v) -- cgit v1.2.3