diff options
Diffstat (limited to 'src/corelib/kernel')
-rw-r--r-- | src/corelib/kernel/qmetatype.cpp | 12 | ||||
-rw-r--r-- | src/corelib/kernel/qmetatype.h | 274 | ||||
-rw-r--r-- | src/corelib/kernel/qvariant.cpp | 203 | ||||
-rw-r--r-- | src/corelib/kernel/qvariant.h | 94 |
4 files changed, 580 insertions, 3 deletions
diff --git a/src/corelib/kernel/qmetatype.cpp b/src/corelib/kernel/qmetatype.cpp index 44246e05ca..37ff858530 100644 --- a/src/corelib/kernel/qmetatype.cpp +++ b/src/corelib/kernel/qmetatype.cpp @@ -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> diff --git a/src/corelib/kernel/qmetatype.h b/src/corelib/kernel/qmetatype.h index e173bbe57d..3eb89caea8 100644 --- a/src/corelib/kernel/qmetatype.h +++ b/src/corelib/kernel/qmetatype.h @@ -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 diff --git a/src/corelib/kernel/qvariant.cpp b/src/corelib/kernel/qvariant.cpp index be83979205..7001e5b077 100644 --- a/src/corelib/kernel/qvariant.cpp +++ b/src/corelib/kernel/qvariant.cpp @@ -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 diff --git a/src/corelib/kernel/qvariant.h b/src/corelib/kernel/qvariant.h index 0f4102b70a..97fb8089a4 100644 --- a/src/corelib/kernel/qvariant.h +++ b/src/corelib/kernel/qvariant.h @@ -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) |