From 4fbb2f66d6144953837361e808845edb181b124e Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 25 Aug 2020 11:58:09 +0200 Subject: [PATCH] Add a QMetaSequence interface This is in line with QMetaType and will be used to implement a mutable QSequentialIterable. Later on, a QMetaAssociation will be added as well, to implement a mutable QAssociativeIterable. The code here represents the minimal set of functionality needed to have a practical sequential container. The functionality is not completely orthogonal. In particular, the index based operations could be implemented in terms of iterator-based operations. Task-number: QTBUG-81716 Change-Id: Ibd41eb7db248a774673c701549d9a03cbf2e48b6 Reviewed-by: Fabian Kosmale --- qmake/.prev_CMakeLists.txt | 2 + qmake/CMakeLists.txt | 2 + qmake/Makefile.unix | 6 +- qmake/qmake.pro | 3 + src/corelib/.prev_CMakeLists.txt | 2 + src/corelib/CMakeLists.txt | 2 + src/corelib/global/global.pri | 1 + src/corelib/global/qcontainerinfo.h | 150 ++++ src/corelib/kernel/kernel.pri | 2 + src/corelib/kernel/qmetacontainer.cpp | 697 ++++++++++++++++++ src/corelib/kernel/qmetacontainer.h | 594 +++++++++++++++ src/tools/bootstrap/.prev_CMakeLists.txt | 1 + src/tools/bootstrap/CMakeLists.txt | 1 + src/tools/bootstrap/bootstrap.pro | 1 + .../auto/corelib/kernel/.prev_CMakeLists.txt | 1 + tests/auto/corelib/kernel/CMakeLists.txt | 1 + tests/auto/corelib/kernel/kernel.pro | 1 + .../kernel/qmetacontainer/CMakeLists.txt | 16 + .../kernel/qmetacontainer/qmetacontainer.pro | 7 + .../qmetacontainer/tst_qmetacontainer.cpp | 490 ++++++++++++ 20 files changed, 1979 insertions(+), 1 deletion(-) create mode 100644 src/corelib/global/qcontainerinfo.h create mode 100644 src/corelib/kernel/qmetacontainer.cpp create mode 100644 src/corelib/kernel/qmetacontainer.h create mode 100644 tests/auto/corelib/kernel/qmetacontainer/CMakeLists.txt create mode 100644 tests/auto/corelib/kernel/qmetacontainer/qmetacontainer.pro create mode 100644 tests/auto/corelib/kernel/qmetacontainer/tst_qmetacontainer.cpp diff --git a/qmake/.prev_CMakeLists.txt b/qmake/.prev_CMakeLists.txt index 127da3d71f..8719a3eb54 100644 --- a/qmake/.prev_CMakeLists.txt +++ b/qmake/.prev_CMakeLists.txt @@ -38,6 +38,7 @@ qt_add_tool(${target_name} ../src/3rdparty/pcre2/src/pcre2_ucp.h ../src/3rdparty/pcre2/src/pcre2_valid_utf.c ../src/3rdparty/pcre2/src/pcre2_xclass.c + ../src/corelib/global/qcontainerinfo.h ../src/corelib/global/qglobal.cpp ../src/corelib/global/qglobal.h ../src/corelib/global/qlibraryinfo.cpp ../src/corelib/global/qlogging.cpp @@ -57,6 +58,7 @@ qt_add_tool(${target_name} ../src/corelib/io/qiodevice.cpp ../src/corelib/io/qiodevice.h ../src/corelib/io/qsettings.cpp ../src/corelib/io/qtemporaryfile.cpp ../src/corelib/io/qtemporaryfile.h + ../src/corelib/kernel/qmetacontainer.cpp ../src/corelib/kernel/qmetacontainer.h ../src/corelib/kernel/qmetatype.cpp ../src/corelib/kernel/qmetatype.h ../src/corelib/kernel/qsystemerror.cpp ../src/corelib/kernel/qsystemerror_p.h ../src/corelib/kernel/qvariant.cpp diff --git a/qmake/CMakeLists.txt b/qmake/CMakeLists.txt index 15c85b3400..fc5d488c49 100644 --- a/qmake/CMakeLists.txt +++ b/qmake/CMakeLists.txt @@ -41,6 +41,7 @@ qt_add_tool(${target_name} ../src/3rdparty/pcre2/src/pcre2_ucp.h ../src/3rdparty/pcre2/src/pcre2_valid_utf.c ../src/3rdparty/pcre2/src/pcre2_xclass.c + ../src/corelib/global/qcontainerinfo.h ../src/corelib/global/qendian.cpp # special case ../src/corelib/global/qglobal.cpp ../src/corelib/global/qglobal.h ../src/corelib/global/qlibraryinfo.cpp @@ -65,6 +66,7 @@ qt_add_tool(${target_name} ../src/corelib/io/qiodevice.cpp ../src/corelib/io/qiodevice.h ../src/corelib/io/qsettings.cpp ../src/corelib/io/qtemporaryfile.cpp ../src/corelib/io/qtemporaryfile.h + ../src/corelib/kernel/qmetacontainer.cpp ../src/corelib/kernel/qmetacontainer.h ../src/corelib/kernel/qmetatype.cpp ../src/corelib/kernel/qmetatype.h ../src/corelib/kernel/qsystemerror.cpp ../src/corelib/kernel/qsystemerror_p.h ../src/corelib/kernel/qvariant.cpp diff --git a/qmake/Makefile.unix b/qmake/Makefile.unix index da49bb3177..822150f246 100644 --- a/qmake/Makefile.unix +++ b/qmake/Makefile.unix @@ -25,7 +25,7 @@ QOBJS = \ qiodevice.o qsettings.o qtemporaryfile.o qtextstream.o \ qcborstreamwriter.o qcborvalue.o \ qjsoncbor.o qjsonarray.o qjsondocument.o qjsonobject.o qjsonparser.o qjsonvalue.o \ - qmetatype.o qsystemerror.o qvariant.o \ + qmetacontainer.o qmetatype.o qsystemerror.o qvariant.o \ quuid.o \ qarraydata.o qbitarray.o qbytearray.o qbytearraylist.o qbytearraymatcher.o \ qcalendar.o qgregoriancalendar.o qromancalendar.o \ @@ -96,6 +96,7 @@ DEPEND_SRC = \ $(SOURCE_PATH)/src/corelib/io/qiodevice.cpp \ $(SOURCE_PATH)/src/corelib/io/qsettings.cpp \ $(SOURCE_PATH)/src/corelib/io/qtemporaryfile.cpp \ + $(SOURCE_PATH)/src/corelib/kernel/qmetacontainer.cpp \ $(SOURCE_PATH)/src/corelib/kernel/qmetatype.cpp \ $(SOURCE_PATH)/src/corelib/kernel/qsystemerror.cpp \ $(SOURCE_PATH)/src/corelib/kernel/qvariant.cpp \ @@ -354,6 +355,9 @@ qvsnprintf.o: $(SOURCE_PATH)/src/corelib/text/qvsnprintf.cpp qbytearraymatcher.o: $(SOURCE_PATH)/src/corelib/text/qbytearraymatcher.cpp $(CXX) -c -o $@ $(CXXFLAGS) $< +qmetacontainer.o: $(SOURCE_PATH)/src/corelib/kernel/qmetacontainer.cpp + $(CXX) -c -o $@ $(CXXFLAGS) $< + qmetatype.o: $(SOURCE_PATH)/src/corelib/kernel/qmetatype.cpp $(CXX) -c -o $@ $(CXXFLAGS) $< diff --git a/qmake/qmake.pro b/qmake/qmake.pro index 4ba7a41fad..9decc10edc 100644 --- a/qmake/qmake.pro +++ b/qmake/qmake.pro @@ -148,6 +148,7 @@ SOURCES += \ qlocale_tools.cpp \ qlogging.cpp \ qmalloc.cpp \ + qmetacontainer.cpp \ qmetatype.cpp \ qnumeric.cpp \ qregularexpression.cpp \ @@ -181,6 +182,7 @@ HEADERS += \ qcborvalue.h \ qcborvalue_p.h \ qchar.h \ + qcontainerinfo.h \ qcryptographichash.h \ qdatetime.h \ qdatetime_p.h \ @@ -204,6 +206,7 @@ HEADERS += \ qlocale.h \ qlocale_tools_p.h \ qmap.h \ + qmetacontainer.h \ qmetatype.h \ qnumeric.h \ qregularexpression.h \ diff --git a/src/corelib/.prev_CMakeLists.txt b/src/corelib/.prev_CMakeLists.txt index 747a0d75df..91e73b6c5d 100644 --- a/src/corelib/.prev_CMakeLists.txt +++ b/src/corelib/.prev_CMakeLists.txt @@ -11,6 +11,7 @@ qt_add_module(Core SOURCES global/archdetect.cpp global/qcompilerdetection.h + global/qcontainerinfo.h global/qendian.cpp global/qendian.h global/qendian_p.h global/qflags.h global/qfloat16.cpp global/qfloat16.h @@ -80,6 +81,7 @@ qt_add_module(Core kernel/qeventloop.cpp kernel/qeventloop.h kernel/qfunctions_p.h kernel/qmath.cpp kernel/qmath.h + kernel/qmetacontainer.cpp kernel/qmetacontainer.h kernel/qmetaobject.cpp kernel/qmetaobject.h kernel/qmetaobject_p.h kernel/qmetaobject_moc_p.h kernel/qmetaobjectbuilder.cpp kernel/qmetaobjectbuilder_p.h diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt index 8181250692..04021c8d29 100644 --- a/src/corelib/CMakeLists.txt +++ b/src/corelib/CMakeLists.txt @@ -33,6 +33,7 @@ qt_add_module(Core SOURCES global/archdetect.cpp global/qcompilerdetection.h + global/qcontainerinfo.h global/qendian.cpp global/qendian.h global/qendian_p.h global/qflags.h global/qfloat16.cpp global/qfloat16.h @@ -103,6 +104,7 @@ qt_add_module(Core kernel/qeventloop.cpp kernel/qeventloop.h kernel/qfunctions_p.h kernel/qmath.cpp kernel/qmath.h + kernel/qmetacontainer.cpp kernel/qmetacontainer.h kernel/qmetaobject.cpp kernel/qmetaobject.h kernel/qmetaobject_p.h kernel/qmetaobject_moc_p.h kernel/qmetaobjectbuilder.cpp kernel/qmetaobjectbuilder_p.h diff --git a/src/corelib/global/global.pri b/src/corelib/global/global.pri index 6ba39d41bd..e799e13e4c 100644 --- a/src/corelib/global/global.pri +++ b/src/corelib/global/global.pri @@ -6,6 +6,7 @@ HEADERS += \ global/qoperatingsystemversion_p.h \ global/qsystemdetection.h \ global/qcompilerdetection.h \ + global/qcontainerinfo.h \ global/qprocessordetection.h \ global/qmemory_p.h \ global/qnamespace.h \ diff --git a/src/corelib/global/qcontainerinfo.h b/src/corelib/global/qcontainerinfo.h new file mode 100644 index 0000000000..ad3ef301e4 --- /dev/null +++ b/src/corelib/global/qcontainerinfo.h @@ -0,0 +1,150 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCONTAINERINFO_H +#define QCONTAINERINFO_H + +#include +#include + +QT_BEGIN_NAMESPACE + +namespace QContainerTraits +{ + +template +using value_type = typename C::value_type; + +template +using iterator = typename C::iterator; + +template +using const_iterator = typename C::const_iterator; + +// Some versions of Apple clang warn about the constexpr variables below being unused. +QT_WARNING_PUSH +QT_WARNING_DISABLE_CLANG("-Wunused-const-variable") + +template +constexpr bool has_size_v = false; +template +constexpr bool has_size_v> = true; + +template +constexpr bool has_clear_v = false; +template +constexpr bool has_clear_v> = true; + +template +constexpr bool has_at_v = false; +template +constexpr bool has_at_v> = true; + +template +constexpr bool can_get_at_index_v = false; +template +constexpr bool can_get_at_index_v(decltype(C()[0]))>> = true; + +template +constexpr bool can_set_at_index_v = false; +template +constexpr bool can_set_at_index_v())>> = true; + +template +constexpr bool has_push_front_v = false; +template +constexpr bool has_push_front_v()))>> = true; + +template +constexpr bool has_push_back_v = false; +template +constexpr bool has_push_back_v()))>> = true; + +template +constexpr bool has_insert_v = false; +template +constexpr bool has_insert_v()))>> = true; + +template +constexpr bool has_pop_front_v = false; +template +constexpr bool has_pop_front_v> = true; + +template +constexpr bool has_pop_back_v = false; +template +constexpr bool has_pop_back_v> = true; + +template +constexpr bool has_iterator_v = false; +template +constexpr bool has_iterator_v>> = true; + +template +constexpr bool has_const_iterator_v = false; +template +constexpr bool has_const_iterator_v>> = true; + +template +constexpr bool can_get_at_iterator_v = false; +template +constexpr bool can_get_at_iterator_v(decltype(*C().begin()))>> = true; + +template +constexpr bool can_set_at_iterator_v = false; +template +constexpr bool can_set_at_iterator_v())>> = true; + +template +constexpr bool can_insert_at_iterator_v = false; +template +constexpr bool can_insert_at_iterator_v()))>> = true; + +template +constexpr bool can_erase_at_iterator_v = false; +template +constexpr bool can_erase_at_iterator_v> = true; + +QT_WARNING_POP + +} + +QT_END_NAMESPACE + +#endif // QCONTAINERINFO_H diff --git a/src/corelib/kernel/kernel.pri b/src/corelib/kernel/kernel.pri index 036230e11f..420f0222e4 100644 --- a/src/corelib/kernel/kernel.pri +++ b/src/corelib/kernel/kernel.pri @@ -12,6 +12,7 @@ HEADERS += \ kernel/qcorecmdlineargs_p.h \ kernel/qcoreapplication.h \ kernel/qcoreevent.h \ + kernel/qmetacontainer.h \ kernel/qmetaobject.h \ kernel/qmetatype.h \ kernel/qmimedata.h \ @@ -56,6 +57,7 @@ SOURCES += \ kernel/qeventloop.cpp \ kernel/qcoreapplication.cpp \ kernel/qcoreevent.cpp \ + kernel/qmetacontainer.cpp \ kernel/qmetaobject.cpp \ kernel/qmetatype.cpp \ kernel/qmetaobjectbuilder.cpp \ diff --git a/src/corelib/kernel/qmetacontainer.cpp b/src/corelib/kernel/qmetacontainer.cpp new file mode 100644 index 0000000000..cfe837bef8 --- /dev/null +++ b/src/corelib/kernel/qmetacontainer.cpp @@ -0,0 +1,697 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmetacontainer.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QMetaSequence + \inmodule QtCore + \since 6.0 + \brief The QMetaSequence class allows type erased access to sequential containers. + + \ingroup objectmodel + + The class provides a number of primitive container operations, using void* + as operands. This way, you can manipulate a generic container retrieved from + a Variant without knowing its type. + + The void* arguments to the various methods are typically created by using + a \l QVariant of the respective container or element type, and calling + its \l QVariant::data() or \l QVariant::constData() methods. However, you + can also pass plain pointers to objects of the container or element type. + + Iterator invalidation follows the rules given by the underlying containers + and is not expressed in the API. Therefore, for a truly generic container, + any iterators should be considered invalid after any write operation. +*/ + +/*! + \fn template QMetaSequence QMetaSequence::fromContainer() + \since 6.0 + + Returns the QMetaSequence corresponding to the type given as template parameter. +*/ + +/*! + Returns \c true if the underlying container provides at least a forward + iterator as defined by std::forward_iterator_tag, otherwise returns + \c false. Bi-directional iterators and random access iterators are + specializations of forward iterators. This method will also return + \c true if the container provides one of those. + + QMetaSequence assumes that const and non-const iterators for the same + container have the same iterator traits. + */ +bool QMetaSequence::hasForwardIterator() const +{ + if (!d_ptr) + return false; + return d_ptr->iteratorCapabilities & QtMetaContainerPrivate::ForwardCapability; +} + +/*! + Returns \c true if the underlying container provides a bi-directional + iterator or a random access iterator as defined by + std::bidirectional_iterator_tag and std::random_access_iterator_tag, + respectively. Otherwise returns \c false. + + QMetaSequence assumes that const and non-const iterators for the same + container have the same iterator traits. + */ +bool QMetaSequence::hasBidirectionalIterator() const +{ + if (!d_ptr) + return false; + return d_ptr->iteratorCapabilities & QtMetaContainerPrivate::BiDirectionalCapability; +} + +/*! + Returns \c true if the underlying container provides a random access + iterator as defined by std::random_access_iterator_tag, otherwise returns + \c false. + + QMetaSequence assumes that const and non-const iterators for the same + container have the same iterator traits. + */ +bool QMetaSequence::hasRandomAccessIterator() const +{ + if (!d_ptr) + return false; + return d_ptr->iteratorCapabilities & QtMetaContainerPrivate::RandomAccessCapability; +} + +/*! + Returns the meta type for values stored in the container. + */ +QMetaType QMetaSequence::valueMetaType() const +{ + return d_ptr ? d_ptr->valueMetaType : QMetaType(); +} + +/*! + Returns \c true if the underlying container is ordered, otherwise returns + \c false. A container is considered ordered if elements added to it are + placed in a defined location. Inserting into or adding to an ordered + container will always succeed. Inserting into or adding to an unordered + container may not succeed, for example if the container is a QSet that + already contains the value being inserted. + + \sa addElement(), insertElementAtIterator(), addsAndRemovesElementsAtBegin(), + addsAndRemovesElementsAtEnd() + */ +bool QMetaSequence::isOrdered() const +{ + if (!d_ptr) + return false; + return d_ptr->addRemovePosition != QtMetaContainerPrivate::QMetaSequenceInterface::Random; +} + +/*! + Returns \c true if elements added using \l addElement() are placed at the + beginning of the container, otherwise returns \c false. Likewise + \l removeElement() removes an element from the beginning of the container + if this method returns \c true. + + \sa addElement(), removeElement(), addsAndRemovesElementsAtEnd() + */ +bool QMetaSequence::addsAndRemovesElementsAtBegin() const +{ + if (!d_ptr) + return false; + return d_ptr->addRemovePosition == QtMetaContainerPrivate::QMetaSequenceInterface::AtBegin; +} + +/*! + Returns \c true if elements added using \l addElement() are placed at the + end of the container, otherwise returns \c false. Likewise + \l removeElement() removes an element from the end of the container + if this method returns \c true. + + \sa addElement(), removeElement(), addsAndRemovesElementsAtBegin() + */ +bool QMetaSequence::addsAndRemovesElementsAtEnd() const +{ + if (!d_ptr) + return false; + return d_ptr->addRemovePosition == QtMetaContainerPrivate::QMetaSequenceInterface::AtEnd; +} + +/*! + Returns \c true if the container can be queried for its size, \c false + otherwise. + + \sa size() + */ +bool QMetaSequence::hasSize() const +{ + return d_ptr && d_ptr->sizeFn; +} + +/*! + Returns the number of elements in the given \a container if it can be + queried for its size. Otherwise returns \c -1. + + \sa hasSize() + */ +qsizetype QMetaSequence::size(const void *container) const +{ + return hasSize() ? d_ptr->sizeFn(container) : -1; +} + +/*! + Returns \c true if the container can be cleared, \c false otherwise. + + \sa clear() + */ +bool QMetaSequence::canClear() const +{ + return d_ptr && d_ptr->clearFn; +} + +/*! + Clears the given \a container if it can be cleared. + + \sa canClear() + */ +void QMetaSequence::clear(void *container) const +{ + if (canClear()) + d_ptr->clearFn(container); +} + +/*! + Returns \c true if elements can be retrieved from the container by index, + otherwise \c false. + + \sa elementAtIndex() + */ +bool QMetaSequence::canGetElementAtIndex() const +{ + return d_ptr && d_ptr->elementAtIndexFn; +} + +/*! + Retrieves the element at \a index in the \a container and places it in the + memory location pointed to by \a result, if that is possible. + + \sa canGetElementAtIndex() + */ +void QMetaSequence::elementAtIndex(const void *container, qsizetype index, void *result) const +{ + if (canGetElementAtIndex()) + d_ptr->elementAtIndexFn(container, index, result); +} + +/*! + Returns \c true if an element can be written to the container by index, + otherwise \c false. + + \sa setElementAtIndex() +*/ +bool QMetaSequence::canSetElementAtIndex() const +{ + return d_ptr && d_ptr->setElementAtIndexFn; +} + +/*! + Overwrites the element at \a index in the \a container using the \a element + passed as parameter if that is possible. + + \sa canSetElementAtIndex() + */ +void QMetaSequence::setElementAtIndex(void *container, qsizetype index, const void *element) const +{ + if (canSetElementAtIndex()) + d_ptr->setElementAtIndexFn(container, index, element); +} + +/*! + Returns \c true if elements can be added to the container, \c false + otherwise. + + \sa addElement(), isOrdered() + */ +bool QMetaSequence::canAddElement() const +{ + return d_ptr && d_ptr->addElementFn; +} + +/*! + Adds \a element to the \a container if possible. If \l canAddElement() + returns \c false, the \a element is not added. Else, if + \l addsAndRemovesElementsAtBegin() returns \c true, the \a element is added + to the beginning of the \a container. Else, if + \l addsAndRemovesElementsAtEnd() returns \c true, the \a element is added to + the end of the container. Else, the element is added in an unspecified + place or not at all. The latter is the case for adding elements to an + unordered container, for example \l QSet. + + \sa canAddElement(), addsAndRemovesElementsAtBegin(), + addsAndRemovesElementsAtEnd(), isOrdered(), removeElement() + */ +void QMetaSequence::addElement(void *container, const void *element) const +{ + if (canAddElement()) + d_ptr->addElementFn(container, element); +} + +/*! + Returns \c true if elements can be removed from the container, \c false + otherwise. + + \sa removeElement(), isOrdered() + */ +bool QMetaSequence::canRemoveElement() const +{ + return d_ptr && d_ptr->removeElementFn; +} + +/*! + Removes an element from the \a container if possible. If + \l canRemoveElement() returns \c false, no element is removed. Else, if + \l addsAndRemovesElementsAtBegin() returns \c true, the first element in + the \a container is removed. Else, if \l addsAndRemovesElementsAtEnd() + returns \c true, the last element in the \a container is removed. Else, + an unspecified element or nothing is removed. + + \sa canRemoveElement(), addsAndRemovesElementsAtBegin(), + addsAndRemovesElementsAtEnd(), isOrdered(), addElement() + */ +void QMetaSequence::removeElement(void *container) const +{ + if (canRemoveElement()) + d_ptr->removeElementFn(container); +} + +/*! + Returns \c true if the underlying container offers a non-const iterator, + \c false otherwise. + + \sa begin(), end(), destroyIterator(), compareIterator(), diffIterator(), + advanceIterator(), copyIterator() + */ +bool QMetaSequence::hasIterator() const +{ + if (!d_ptr || !d_ptr->createIteratorFn) + return false; + Q_ASSERT(d_ptr->destroyIteratorFn); + Q_ASSERT(d_ptr->compareIteratorFn); + Q_ASSERT(d_ptr->copyIteratorFn); + Q_ASSERT(d_ptr->advanceIteratorFn); + Q_ASSERT(d_ptr->diffIteratorFn); + return true; +} + +/*! + Creates and returns a non-const iterator pointing to the beginning of + \a container. The iterator is allocated on the heap using new. It has to be + destroyed using \l destroyIterator eventually, to reclaim the memory. + + Returns \c nullptr if the container doesn't offer any non-const iterators. + + \sa end(), constBegin(), constEnd(), destroyIterator() + */ +void *QMetaSequence::begin(void *container) const +{ + return hasIterator() + ? d_ptr->createIteratorFn( + container, QtMetaContainerPrivate::QMetaSequenceInterface::AtBegin) + : nullptr; +} + +/*! + Creates and returns a non-const iterator pointing to the end of + \a container. The iterator is allocated on the heap using new. It has to be + destroyed using \l destroyIterator eventually, to reclaim the memory. + + Returns \c nullptr if the container doesn't offer any non-const iterators. + + \sa hasIterator(), end(), constBegin(), constEnd(), destroyIterator() + */ +void *QMetaSequence::end(void *container) const +{ + return hasIterator() + ? d_ptr->createIteratorFn( + container, QtMetaContainerPrivate::QMetaSequenceInterface::AtEnd) + : nullptr; +} + +/*! + Destroys a non-const \a iterator previously created using \l begin() or + \l end(). + + \sa begin(), end(), destroyConstIterator() + */ +void QMetaSequence::destroyIterator(const void *iterator) const +{ + if (hasIterator()) + d_ptr->destroyIteratorFn(iterator); +} + +/*! + Returns \c true if the non-const iterators \a i and \a j point to the same + element in the container they are iterating over, otherwise returns \c + false. + + \sa begin(), end() + */ +bool QMetaSequence::compareIterator(const void *i, const void *j) const +{ + return hasIterator() ? d_ptr->compareIteratorFn(i, j) : false; +} + +/*! + Copies the non-const iterator \a source into the non-const iterator + \a target. Afterwards compareIterator(target, source) returns \c true. + + \sa begin(), end() + */ +void QMetaSequence::copyIterator(void *target, const void *source) const +{ + if (hasIterator()) + d_ptr->copyIteratorFn(target, source); +} + +/*! + Advances the non-const \a iterator by \a step steps. If \a steps is negative + the \a iterator is moved backwards, towards the beginning of the container. + The behavior is unspecified for negative values of \a step if + \l hasBidirectionalIterator() returns false. + + \sa begin(), end() + */ +void QMetaSequence::advanceIterator(void *iterator, qsizetype step) const +{ + if (hasIterator()) + d_ptr->advanceIteratorFn(iterator, step); +} + +/*! + Returns the distance between the non-const iterators \a i and \a j, the + equivalent of \a i \c - \a j. If \a j is closer to the end of the container + than \a i, the returned value is negative. The behavior is unspecified in + this case if \l hasBidirectionalIterator() returns false. + + \sa begin(), end() + */ +qsizetype QMetaSequence::diffIterator(const void *i, const void *j) const +{ + return hasIterator() ? d_ptr->diffIteratorFn(i, j) : 0; +} + +/*! + Returns \c true if the underlying container can retrieve the value pointed + to by a non-const iterator, \c false otherwise. + + \sa hasIterator(), elementAtIterator() + */ +bool QMetaSequence::canGetElementAtIterator() const +{ + return d_ptr && d_ptr->elementAtIteratorFn; +} + +/*! + Retrieves the element pointed to by the non-const \a iterator and stores it + in the memory location pointed to by \a result, if possible. + + \sa canGetElementAtIterator(), begin(), end() + */ +void QMetaSequence::elementAtIterator(const void *iterator, void *result) const +{ + if (canGetElementAtIterator()) + d_ptr->elementAtIteratorFn(iterator, result); +} + +/*! + Returns \c true if the underlying container can write to the value pointed + to by a non-const iterator, \c false otherwise. + + \sa hasIterator(), setElementAtIterator() + */ +bool QMetaSequence::canSetElementAtIterator() const +{ + return d_ptr && d_ptr->setElementAtIteratorFn; +} + +/*! + Writes \a element to the value pointed to by the non-const \a iterator, if + possible. + + \sa canSetElementAtIterator(), begin(), end() + */ +void QMetaSequence::setElementAtIterator(const void *iterator, const void *element) const +{ + if (canSetElementAtIterator()) + d_ptr->setElementAtIteratorFn(iterator, element); +} + +/*! + Returns \c true if the underlying container can insert a new element, taking + the location pointed to by a non-const iterator into account. + + \sa hasIterator(), insertElementAtIterator() + */ +bool QMetaSequence::canInsertElementAtIterator() const +{ + return d_ptr && d_ptr->insertElementAtIteratorFn; +} + +/*! + Inserts \a element into the \a container, if possible, taking the non-const + \a iterator into account. If \l canInsertElementAtIterator() returns + \c false, the \a element is not inserted. Else if \l isOrdered() returns + \c true, the element is inserted before the element pointed to by + \a iterator. Else, the \a element is inserted at an unspecified place or not + at all. In the latter case, the \a iterator is taken as a hint. If it points + to the correct place for the \a element, the operation may be faster than a + \l addElement() without iterator. + + \sa canInsertElementAtIterator(), isOrdered(), begin(), end() + */ +void QMetaSequence::insertElementAtIterator(void *container, const void *iterator, + const void *element) const +{ + if (canInsertElementAtIterator()) + d_ptr->insertElementAtIteratorFn(container, iterator, element); +} + +/*! + Returns \c true if the element pointed to by a non-const iterator can be + erased, \c false otherwise. + + \sa hasIterator(), eraseElementAtIterator() + */ +bool QMetaSequence::canEraseElementAtIterator() const +{ + return d_ptr && d_ptr->eraseElementAtIteratorFn; +} + +/*! + Erases the element pointed to by the non-const \a iterator from the + \a container, if possible. + + \sa canEraseElementAtIterator(), begin(), end() + */ +void QMetaSequence::eraseElementAtIterator(void *container, const void *iterator) const +{ + if (canEraseElementAtIterator()) + d_ptr->eraseElementAtIteratorFn(container, iterator); +} + +/*! + Returns \c true if the underlying container offers a const iterator, + \c false otherwise. + + \sa constBegin(), constEnd(), destroyConstIterator(), + compareConstIterator(), diffConstIterator(), advanceConstIterator(), + copyConstIterator() + */ +bool QMetaSequence::hasConstIterator() const +{ + if (!d_ptr || !d_ptr->createConstIteratorFn) + return false; + Q_ASSERT(d_ptr->destroyConstIteratorFn); + Q_ASSERT(d_ptr->compareConstIteratorFn); + Q_ASSERT(d_ptr->copyConstIteratorFn); + Q_ASSERT(d_ptr->advanceConstIteratorFn); + Q_ASSERT(d_ptr->diffConstIteratorFn); + return true; +} + +/*! + Creates and returns a const iterator pointing to the beginning of + \a container. The iterator is allocated on the heap using new. It has to be + destroyed using \l destroyConstIterator eventually, to reclaim the memory. + + Returns \c nullptr if the container doesn't offer any const iterators. + + \sa constEnd(), begin(), end(), destroyConstIterator() + */ +void *QMetaSequence::constBegin(const void *container) const +{ + return hasConstIterator() + ? d_ptr->createConstIteratorFn( + container, QtMetaContainerPrivate::QMetaSequenceInterface::AtBegin) + : nullptr; +} + +/*! + Creates and returns a const iterator pointing to the end of + \a container. The iterator is allocated on the heap using new. It has to be + destroyed using \l destroyConstIterator eventually, to reclaim the memory. + + Returns \c nullptr if the container doesn't offer any const iterators. + + \sa constBegin(), begin(), end(), destroyConstIterator() + */ +void *QMetaSequence::constEnd(const void *container) const +{ + return hasConstIterator() + ? d_ptr->createConstIteratorFn( + container, QtMetaContainerPrivate::QMetaSequenceInterface::AtEnd) + : nullptr; +} + +/*! + Destroys a const \a iterator previously created using \l constBegin() or + \l constEnd(). + + \sa constBegin(), constEnd(), destroyIterator() + */ +void QMetaSequence::destroyConstIterator(const void *iterator) const +{ + if (hasConstIterator()) + d_ptr->destroyConstIteratorFn(iterator); +} + +/*! + Returns \c true if the const iterators \a i and \a j point to the same + element in the container they are iterating over, otherwise returns \c + false. + + \sa constBegin(), constEnd() + */ +bool QMetaSequence::compareConstIterator(const void *i, const void *j) const +{ + return hasConstIterator() ? d_ptr->compareConstIteratorFn(i, j) : false; +} + +/*! + Copies the const iterator \a source into the const iterator + \a target. Afterwards compareConstIterator(target, source) returns \c true. + + \sa constBegin(), constEnd() + */ +void QMetaSequence::copyConstIterator(void *target, const void *source) const +{ + if (hasConstIterator()) + d_ptr->copyConstIteratorFn(target, source); +} + +/*! + Advances the const \a iterator by \a step steps. If \a steps is negative + the \a iterator is moved backwards, towards the beginning of the container. + The behavior is unspecified for negative values of \a step if + \l hasBidirectionalIterator() returns false. + + \sa constBegin(), constEnd() + */ +void QMetaSequence::advanceConstIterator(void *iterator, qsizetype step) const +{ + if (hasConstIterator()) + d_ptr->advanceConstIteratorFn(iterator, step); +} + +/*! + Returns the distance between the const iterators \a i and \a j, the + equivalent of \a i \c - \a j. If \a j is closer to the end of the container + than \a i, the returned value is negative. The behavior is unspecified in + this case if \l hasBidirectionalIterator() returns false. + + \sa constBegin(), constEnd() + */ +qsizetype QMetaSequence::diffConstIterator(const void *i, const void *j) const +{ + return hasConstIterator() ? d_ptr->diffConstIteratorFn(i, j) : 0; +} + +/*! + Returns \c true if the underlying container can retrieve the value pointed + to by a const iterator, \c false otherwise. + + \sa hasConstIterator(), elementAtConstIterator() + */ +bool QMetaSequence::canGetElementAtConstIterator() const +{ + return d_ptr && d_ptr->elementAtConstIteratorFn; +} + +/*! + Retrieves the element pointed to by the const \a iterator and stores it + in the memory location pointed to by \a result, if possible. + + \sa canGetElementAtConstIterator(), constBegin(), constEnd() + */ +void QMetaSequence::elementAtConstIterator(const void *iterator, void *result) const +{ + if (canGetElementAtConstIterator()) + d_ptr->elementAtConstIteratorFn(iterator, result); +} + +/*! + \fn bool operator==(QMetaSequence a, QMetaSequence b) + \since 6.0 + \relates QMetaSequence + + Returns \c true if the QMetaSequence \a a represents the same container type + as the QMetaSequence \a b, otherwise returns \c false. +*/ + +/*! + \fn bool operator!=(QMetaSequence a, QMetaSequence b) + \since 6.0 + \relates QMetaSequence + + Returns \c true if the QMetaSequence \a a represents a different container + type than the QMetaSequence \a b, otherwise returns \c false. +*/ + +QT_END_NAMESPACE diff --git a/src/corelib/kernel/qmetacontainer.h b/src/corelib/kernel/qmetacontainer.h new file mode 100644 index 0000000000..c663d1ce24 --- /dev/null +++ b/src/corelib/kernel/qmetacontainer.h @@ -0,0 +1,594 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMETACONTAINER_H +#define QMETACONTAINER_H + +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +namespace QtMetaContainerPrivate { + +enum IteratorCapability : quint8 { + ForwardCapability = 1 << 0, + BiDirectionalCapability = 1 << 1, + RandomAccessCapability = 1 << 2, +}; + +Q_DECLARE_FLAGS(IteratorCapabilities, IteratorCapability) +Q_DECLARE_OPERATORS_FOR_FLAGS(IteratorCapabilities) + +class QMetaSequenceInterface +{ +public: + enum Position : quint8 { AtBegin, AtEnd, Random }; + + ushort revision; + IteratorCapabilities iteratorCapabilities; + Position addRemovePosition; + QMetaType valueMetaType; + + using SizeFn = qsizetype(*)(const void *); + SizeFn sizeFn; + using ClearFn = void(*)(void *); + ClearFn clearFn; + + using ElementAtIndexFn = void(*)(const void *, qsizetype, void *); + ElementAtIndexFn elementAtIndexFn; + using SetElementAtIndexFn = void(*)(void *, qsizetype, const void *); + SetElementAtIndexFn setElementAtIndexFn; + + using AddElementFn = void(*)(void *, const void *); + AddElementFn addElementFn; + using RemoveElementFn = void(*)(void *); + RemoveElementFn removeElementFn; + + using CreateIteratorFn = void *(*)(void *, Position); + CreateIteratorFn createIteratorFn; + using DestroyIteratorFn = void(*)(const void *); + DestroyIteratorFn destroyIteratorFn; + using CompareIteratorFn = bool(*)(const void *, const void *); + CompareIteratorFn compareIteratorFn; + using CopyIteratorFn = void(*)(void *, const void *); + CopyIteratorFn copyIteratorFn; + using AdvanceIteratorFn = void(*)(void *, qsizetype); + AdvanceIteratorFn advanceIteratorFn; + using DiffIteratorFn = qsizetype(*)(const void *, const void *); + DiffIteratorFn diffIteratorFn; + using ElementAtIteratorFn = void(*)(const void *, void *); + ElementAtIteratorFn elementAtIteratorFn; + using SetElementAtIteratorFn = void(*)(const void *, const void *); + SetElementAtIteratorFn setElementAtIteratorFn; + using InsertElementAtIteratorFn = void(*)(void *, const void *, const void *); + InsertElementAtIteratorFn insertElementAtIteratorFn; + using EraseElementAtIteratorFn = void(*)(void *, const void *); + EraseElementAtIteratorFn eraseElementAtIteratorFn; + + using CreateConstIteratorFn = void *(*)(const void *, Position); + CreateConstIteratorFn createConstIteratorFn; + DestroyIteratorFn destroyConstIteratorFn; + CompareIteratorFn compareConstIteratorFn; + CopyIteratorFn copyConstIteratorFn; + AdvanceIteratorFn advanceConstIteratorFn; + DiffIteratorFn diffConstIteratorFn; + ElementAtIteratorFn elementAtConstIteratorFn; +}; + +template +class QMetaSequenceForContainer +{ + template + static constexpr IteratorCapabilities capabilitiesForIterator() + { + using Tag = typename std::iterator_traits::iterator_category; + IteratorCapabilities caps {}; + if constexpr (std::is_base_of_v) + caps |= ForwardCapability; + if constexpr (std::is_base_of_v) + caps |= BiDirectionalCapability; + if constexpr (std::is_base_of_v) + caps |= RandomAccessCapability; + return caps; + } + + static constexpr IteratorCapabilities getIteratorCapabilities() + { + if constexpr (QContainerTraits::has_iterator_v && !std::is_const_v) + return capabilitiesForIterator>(); + else if constexpr (QContainerTraits::has_const_iterator_v) + return capabilitiesForIterator>(); + else + return {}; + } + + static constexpr QMetaSequenceInterface::Position getAddRemovePosition() + { + if constexpr (QContainerTraits::has_push_back_v && QContainerTraits::has_pop_back_v) + return QMetaSequenceInterface::AtEnd; + if constexpr (QContainerTraits::has_push_front_v && QContainerTraits::has_pop_front_v) + return QMetaSequenceInterface::AtBegin; + return QMetaSequenceInterface::Random; + } + + static constexpr QMetaType getValueMetaType() + { + return QMetaType::fromType(); + } + + static constexpr QMetaSequenceInterface::SizeFn getSizeFn() + { + if constexpr (QContainerTraits::has_size_v) { + return [](const void *c) -> qsizetype { return static_cast(c)->size(); }; + } else { + return nullptr; + } + } + + static constexpr QMetaSequenceInterface::ClearFn getClearFn() + { + if constexpr (QContainerTraits::has_clear_v) { + return [](void *c) { return static_cast(c)->clear(); }; + } else { + return nullptr; + } + } + + static constexpr QMetaSequenceInterface::ElementAtIndexFn getElementAtIndexFn() + { + if constexpr (QContainerTraits::has_at_v) { + return [](const void *c, qsizetype i, void *r) { + *static_cast *>(r) + = static_cast(c)->at(i); + }; + } else if constexpr (QContainerTraits::can_get_at_index_v) { + return [](const void *c, qsizetype i, void *r) { + *static_cast *>(r) + = (*static_cast(c))[i]; + }; + } else { + return nullptr; + } + } + + static constexpr QMetaSequenceInterface::SetElementAtIndexFn getSetElementAtIndexFn() + { + if constexpr (QContainerTraits::can_set_at_index_v) { + return [](void *c, qsizetype i, const void *e) { + (*static_cast(c))[i] + = *static_cast *>(e); + }; + } else { + return nullptr; + } + } + + static constexpr QMetaSequenceInterface::AddElementFn getAddElementFn() + { + if constexpr (QContainerTraits::has_push_back_v) { + return [](void *c, const void *v) { + static_cast(c)->push_back( + *static_cast *>(v)); + }; + } else if constexpr (QContainerTraits::has_push_front_v) { + return [](void *c, const void *v) { + static_cast(c)->push_front( + *static_cast *>(v)); + }; + } else if constexpr (QContainerTraits::has_insert_v) { + return [](void *c, const void *v) { + static_cast(c)->insert( + *static_cast *>(v)); + }; + } else { + return nullptr; + } + } + + static constexpr QMetaSequenceInterface::RemoveElementFn getRemoveElementFn() + { + if constexpr (QContainerTraits::has_pop_back_v) { + return [](void *c) { static_cast(c)->pop_back(); }; + } else if constexpr (QContainerTraits::has_pop_front_v) { + return [](void *c) { static_cast(c)->pop_front(); }; + } else { + return nullptr; + } + } + + static constexpr QMetaSequenceInterface::CreateIteratorFn getCreateIteratorFn() + { + if constexpr (QContainerTraits::has_iterator_v && !std::is_const_v) { + return [](void *c, QMetaSequenceInterface::Position p) -> void* { + using Iterator = QContainerTraits::iterator; + switch (p) { + case QMetaSequenceInterface::AtBegin: + case QMetaSequenceInterface::Random: + return new Iterator(static_cast(c)->begin()); + break; + case QMetaSequenceInterface::AtEnd: + return new Iterator(static_cast(c)->end()); + break; + } + return nullptr; + }; + } else { + return nullptr; + } + } + + static constexpr QMetaSequenceInterface::DestroyIteratorFn getDestroyIteratorFn() + { + if constexpr (QContainerTraits::has_iterator_v && !std::is_const_v) { + return [](const void *i) { + using Iterator = QContainerTraits::iterator; + delete static_cast(i); + }; + } else { + return nullptr; + } + } + + static constexpr QMetaSequenceInterface::CompareIteratorFn getCompareIteratorFn() + { + if constexpr (QContainerTraits::has_iterator_v && !std::is_const_v) { + return [](const void *i, const void *j) { + using Iterator = QContainerTraits::iterator; + return *static_cast(i) == *static_cast(j); + }; + } else { + return nullptr; + } + } + + static constexpr QMetaSequenceInterface::CopyIteratorFn getCopyIteratorFn() + { + if constexpr (QContainerTraits::has_iterator_v && !std::is_const_v) { + return [](void *i, const void *j) { + using Iterator = QContainerTraits::iterator; + *static_cast(i) = *static_cast(j); + }; + } else { + return nullptr; + } + } + + static constexpr QMetaSequenceInterface::AdvanceIteratorFn getAdvanceIteratorFn() + { + if constexpr (QContainerTraits::has_iterator_v && !std::is_const_v) { + return [](void *i, qsizetype step) { + std::advance(*static_cast *>(i), step); + }; + } else { + return nullptr; + } + } + + static constexpr QMetaSequenceInterface::DiffIteratorFn getDiffIteratorFn() + { + if constexpr (QContainerTraits::has_iterator_v && !std::is_const_v) { + return [](const void *i, const void *j) -> qsizetype { + return std::distance(*static_cast *>(j), + *static_cast *>(i)); + }; + } else { + return nullptr; + } + } + + static constexpr QMetaSequenceInterface::ElementAtIteratorFn getElementAtIteratorFn() + { + if constexpr (QContainerTraits::has_iterator_v + && QContainerTraits::can_get_at_iterator_v && !std::is_const_v) { + return [](const void *i, void *r) { + *static_cast *>(r) = + *(*static_cast *>(i)); + }; + } else { + return nullptr; + } + } + + static constexpr QMetaSequenceInterface::SetElementAtIteratorFn getSetElementAtIteratorFn() + { + if constexpr (QContainerTraits::has_iterator_v + && QContainerTraits::can_set_at_iterator_v && !std::is_const_v) { + return [](const void *i, const void *e) { + *(*static_cast *>(i)) + = *static_cast *>(e); + }; + } else { + return nullptr; + } + } + + static constexpr QMetaSequenceInterface::InsertElementAtIteratorFn getInsertElementAtIteratorFn() + { + if constexpr (QContainerTraits::has_iterator_v + && QContainerTraits::can_insert_at_iterator_v && !std::is_const_v) { + return [](void *c, const void *i, const void *e) { + static_cast(c)->insert( + *static_cast *>(i), + *static_cast *>(e)); + }; + } else { + return nullptr; + } + } + + static constexpr QMetaSequenceInterface::EraseElementAtIteratorFn getEraseElementAtIteratorFn() + { + if constexpr (QContainerTraits::has_iterator_v + && QContainerTraits::can_erase_at_iterator_v && !std::is_const_v) { + return [](void *c, const void *i) { + static_cast(c)->erase(*static_cast *>(i)); + }; + } else { + return nullptr; + } + } + + static constexpr QMetaSequenceInterface::CreateConstIteratorFn getCreateConstIteratorFn() + { + if constexpr (QContainerTraits::has_const_iterator_v) { + return [](const void *c, QMetaSequenceInterface::Position p) -> void* { + using Iterator = QContainerTraits::const_iterator; + switch (p) { + case QMetaSequenceInterface::AtBegin: + case QMetaSequenceInterface::Random: + return new Iterator(static_cast(c)->begin()); + break; + case QMetaSequenceInterface::AtEnd: + return new Iterator(static_cast(c)->end()); + break; + } + return nullptr; + }; + } else { + return nullptr; + } + } + + static constexpr QMetaSequenceInterface::DestroyIteratorFn getDestroyConstIteratorFn() + { + if constexpr (QContainerTraits::has_const_iterator_v) { + return [](const void *i) { + using Iterator = QContainerTraits::const_iterator; + delete static_cast(i); + }; + } else { + return nullptr; + } + } + + static constexpr QMetaSequenceInterface::CompareIteratorFn getCompareConstIteratorFn() + { + if constexpr (QContainerTraits::has_const_iterator_v) { + return [](const void *i, const void *j) { + using Iterator = QContainerTraits::const_iterator; + return *static_cast(i) == *static_cast(j); + }; + } else { + return nullptr; + } + } + + static constexpr QMetaSequenceInterface::CopyIteratorFn getCopyConstIteratorFn() + { + if constexpr (QContainerTraits::has_const_iterator_v) { + return [](void *i, const void *j) { + using Iterator = QContainerTraits::const_iterator; + *static_cast(i) = *static_cast(j); + }; + } else { + return nullptr; + } + } + + static constexpr QMetaSequenceInterface::AdvanceIteratorFn getAdvanceConstIteratorFn() + { + if constexpr (QContainerTraits::has_const_iterator_v) { + return [](void *i, qsizetype step) { + std::advance(*static_cast *>(i), step); + }; + } else { + return nullptr; + } + } + + static constexpr QMetaSequenceInterface::DiffIteratorFn getDiffConstIteratorFn() + { + if constexpr (QContainerTraits::has_const_iterator_v) { + return [](const void *i, const void *j) -> qsizetype { + return std::distance(*static_cast *>(j), + *static_cast *>(i)); + }; + } else { + return nullptr; + } + } + + static constexpr QMetaSequenceInterface::ElementAtIteratorFn getElementAtConstIteratorFn() + { + if constexpr (QContainerTraits::has_const_iterator_v + && QContainerTraits::can_get_at_iterator_v) { + return [](const void *i, void *r) { + *static_cast *>(r) = + *(*static_cast *>(i)); + }; + } else { + return nullptr; + } + } + +public: + static QMetaSequenceInterface metaSequence; +}; + +template +QMetaSequenceInterface QMetaSequenceForContainer::metaSequence = { + /*.revision=*/ 0, + /*.iteratorCapabilities=*/ getIteratorCapabilities(), + /*.addRemovePosition=*/ getAddRemovePosition(), + /*.valueMetaType=*/ getValueMetaType(), + /*.sizeFn=*/ getSizeFn(), + /*.clearFn=*/ getClearFn(), + /*.elementAtIndexFn=*/ getElementAtIndexFn(), + /*.setElementAtIndexFn=*/ getSetElementAtIndexFn(), + /*.addElementFn=*/ getAddElementFn(), + /*.removeLastElementFn=*/ getRemoveElementFn(), + /*.createIteratorFn=*/ getCreateIteratorFn(), + /*.destroyIteratorFn=*/ getDestroyIteratorFn(), + /*.equalIteratorFn=*/ getCompareIteratorFn(), + /*.copyIteratorFn=*/ getCopyIteratorFn(), + /*.advanceIteratorFn=*/ getAdvanceIteratorFn(), + /*.diffIteratorFn=*/ getDiffIteratorFn(), + /*.elementAtIteratorFn=*/ getElementAtIteratorFn(), + /*.setElementAtIteratorFn=*/ getSetElementAtIteratorFn(), + /*.insertElementAtIteratorFn=*/ getInsertElementAtIteratorFn(), + /*.eraseElementAtIteratorFn=*/ getEraseElementAtIteratorFn(), + /*.createConstIteratorFn=*/ getCreateConstIteratorFn(), + /*.destroyConstIteratorFn=*/ getDestroyConstIteratorFn(), + /*.equalConstIteratorFn=*/ getCompareConstIteratorFn(), + /*.copyConstIteratorFn=*/ getCopyConstIteratorFn(), + /*.advanceConstIteratorFn=*/ getAdvanceConstIteratorFn(), + /*.diffConstIteratorFn=*/ getDiffConstIteratorFn(), + /*.elementAtConstIteratorFn=*/ getElementAtConstIteratorFn(), +}; + +template +constexpr QMetaSequenceInterface *qMetaSequenceInterfaceForContainer() +{ + return &QMetaSequenceForContainer::metaSequence; +} + +} // namespace QtMetaContainerPrivate + +class Q_CORE_EXPORT QMetaSequence +{ +public: + QMetaSequence() = default; + explicit QMetaSequence(QtMetaContainerPrivate::QMetaSequenceInterface *d) : d_ptr(d) {} + + template + static constexpr QMetaSequence fromContainer() + { + return QMetaSequence(QtMetaContainerPrivate::qMetaSequenceInterfaceForContainer()); + } + + bool hasForwardIterator() const; + bool hasBidirectionalIterator() const; + bool hasRandomAccessIterator() const; + + QMetaType valueMetaType() const; + + bool isOrdered() const; + bool addsAndRemovesElementsAtBegin() const; + bool addsAndRemovesElementsAtEnd() const; + + bool hasSize() const; + qsizetype size(const void *container) const; + + bool canClear() const; + void clear(void *container) const; + + bool canGetElementAtIndex() const; + void elementAtIndex(const void *container, qsizetype index, void *result) const; + + bool canSetElementAtIndex() const; + void setElementAtIndex(void *container, qsizetype index, const void *element) const; + + bool canAddElement() const; + void addElement(void *container, const void *element) const; + + bool canRemoveElement() const; + void removeElement(void *container) const; + + bool hasIterator() const; + void *begin(void *container) const; + void *end(void *container) const; + void destroyIterator(const void *iterator) const; + bool compareIterator(const void *i, const void *j) const; + void copyIterator(void *target, const void *source) const; + void advanceIterator(void *iterator, qsizetype step) const; + qsizetype diffIterator(const void *i, const void *j) const; + + bool canGetElementAtIterator() const; + void elementAtIterator(const void *iterator, void *result) const; + + bool canSetElementAtIterator() const; + void setElementAtIterator(const void *iterator, const void *element) const; + + bool canInsertElementAtIterator() const; + void insertElementAtIterator(void *container, const void *iterator, const void *element) const; + + bool canEraseElementAtIterator() const; + void eraseElementAtIterator(void *container, const void *iterator) const; + + bool hasConstIterator() const; + void *constBegin(const void *container) const; + void *constEnd(const void *container) const; + void destroyConstIterator(const void *iterator) const; + bool compareConstIterator(const void *i, const void *j) const; + void copyConstIterator(void *target, const void *source) const; + void advanceConstIterator(void *iterator, qsizetype step) const; + qsizetype diffConstIterator(const void *i, const void *j) const; + + bool canGetElementAtConstIterator() const; + void elementAtConstIterator(const void *iterator, void *result) const; + + friend bool operator==(const QMetaSequence &a, const QMetaSequence &b) + { + return a.d_ptr == b.d_ptr; + } + friend bool operator!=(const QMetaSequence &a, const QMetaSequence &b) + { + return a.d_ptr != b.d_ptr; + } + +private: + QtMetaContainerPrivate::QMetaSequenceInterface *d_ptr = nullptr; +}; + +QT_END_NAMESPACE + +#endif // QMETACONTAINER_H diff --git a/src/tools/bootstrap/.prev_CMakeLists.txt b/src/tools/bootstrap/.prev_CMakeLists.txt index a69351a167..a4bb4f6aac 100644 --- a/src/tools/bootstrap/.prev_CMakeLists.txt +++ b/src/tools/bootstrap/.prev_CMakeLists.txt @@ -75,6 +75,7 @@ qt_add_module(Bootstrap ../../corelib/io/qurlrecode.cpp ../../corelib/kernel/qcoreapplication.cpp ../../corelib/kernel/qcoreglobaldata.cpp + ../../corelib/kernel/qmetacontainer.cpp ../../corelib/kernel/qmetatype.cpp ../../corelib/kernel/qsharedmemory.cpp ../../corelib/kernel/qsystemerror.cpp diff --git a/src/tools/bootstrap/CMakeLists.txt b/src/tools/bootstrap/CMakeLists.txt index 56b1bcbbcf..f5b1c38c39 100644 --- a/src/tools/bootstrap/CMakeLists.txt +++ b/src/tools/bootstrap/CMakeLists.txt @@ -76,6 +76,7 @@ qt_extend_target(Bootstrap ../../corelib/io/qurlrecode.cpp ../../corelib/kernel/qcoreapplication.cpp ../../corelib/kernel/qcoreglobaldata.cpp + ../../corelib/kernel/qmetacontainer.cpp ../../corelib/kernel/qmetatype.cpp ../../corelib/kernel/qsharedmemory.cpp ../../corelib/kernel/qsystemerror.cpp diff --git a/src/tools/bootstrap/bootstrap.pro b/src/tools/bootstrap/bootstrap.pro index 999ceea840..1853ab74b4 100644 --- a/src/tools/bootstrap/bootstrap.pro +++ b/src/tools/bootstrap/bootstrap.pro @@ -62,6 +62,7 @@ SOURCES += \ ../../corelib/io/qurlrecode.cpp \ ../../corelib/kernel/qcoreapplication.cpp \ ../../corelib/kernel/qcoreglobaldata.cpp \ + ../../corelib/kernel/qmetacontainer.cpp \ ../../corelib/kernel/qmetatype.cpp \ ../../corelib/kernel/qvariant.cpp \ ../../corelib/kernel/qsystemerror.cpp \ diff --git a/tests/auto/corelib/kernel/.prev_CMakeLists.txt b/tests/auto/corelib/kernel/.prev_CMakeLists.txt index 39438deb5b..154cc8e766 100644 --- a/tests/auto/corelib/kernel/.prev_CMakeLists.txt +++ b/tests/auto/corelib/kernel/.prev_CMakeLists.txt @@ -5,6 +5,7 @@ add_subdirectory(qdeadlinetimer) add_subdirectory(qelapsedtimer) add_subdirectory(qeventdispatcher) add_subdirectory(qmath) +add_subdirectory(qmetacontainer) add_subdirectory(qmetaobject) add_subdirectory(qmetaobjectbuilder) add_subdirectory(qmetamethod) diff --git a/tests/auto/corelib/kernel/CMakeLists.txt b/tests/auto/corelib/kernel/CMakeLists.txt index 622b4175a5..e1b2db64c0 100644 --- a/tests/auto/corelib/kernel/CMakeLists.txt +++ b/tests/auto/corelib/kernel/CMakeLists.txt @@ -5,6 +5,7 @@ add_subdirectory(qdeadlinetimer) add_subdirectory(qelapsedtimer) add_subdirectory(qeventdispatcher) add_subdirectory(qmath) +add_subdirectory(qmetacontainer) add_subdirectory(qmetaobject) add_subdirectory(qmetaobjectbuilder) add_subdirectory(qmetamethod) diff --git a/tests/auto/corelib/kernel/kernel.pro b/tests/auto/corelib/kernel/kernel.pro index 7c4fd2ec3d..ba39070933 100644 --- a/tests/auto/corelib/kernel/kernel.pro +++ b/tests/auto/corelib/kernel/kernel.pro @@ -6,6 +6,7 @@ SUBDIRS=\ qeventdispatcher \ qeventloop \ qmath \ + qmetacontainer \ qmetaobject \ qmetaobjectbuilder \ qmetamethod \ diff --git a/tests/auto/corelib/kernel/qmetacontainer/CMakeLists.txt b/tests/auto/corelib/kernel/qmetacontainer/CMakeLists.txt new file mode 100644 index 0000000000..6f0192155d --- /dev/null +++ b/tests/auto/corelib/kernel/qmetacontainer/CMakeLists.txt @@ -0,0 +1,16 @@ +# Generated from qmetacontainer.pro. + +##################################################################### +## tst_qmetacontainer Test: +##################################################################### + +# Collect test data +list(APPEND test_data "./typeFlags.bin") + +qt_add_test(tst_qmetacontainer + SOURCES + tst_qmetacontainer.cpp + PUBLIC_LIBRARIES + Qt::CorePrivate + TESTDATA ${test_data} +) diff --git a/tests/auto/corelib/kernel/qmetacontainer/qmetacontainer.pro b/tests/auto/corelib/kernel/qmetacontainer/qmetacontainer.pro new file mode 100644 index 0000000000..65f3eaa10b --- /dev/null +++ b/tests/auto/corelib/kernel/qmetacontainer/qmetacontainer.pro @@ -0,0 +1,7 @@ +CONFIG += testcase +TARGET = tst_qmetacontainer +QT = core-private testlib +SOURCES = tst_qmetacontainer.cpp +TESTDATA=./typeFlags.bin + + diff --git a/tests/auto/corelib/kernel/qmetacontainer/tst_qmetacontainer.cpp b/tests/auto/corelib/kernel/qmetacontainer/tst_qmetacontainer.cpp new file mode 100644 index 0000000000..84343c4a14 --- /dev/null +++ b/tests/auto/corelib/kernel/qmetacontainer/tst_qmetacontainer.cpp @@ -0,0 +1,490 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:GPL-EXCEPT$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +namespace CheckContainerTraits +{ +struct NotAContainer {}; + +static_assert(QContainerTraits::has_size_v>); +static_assert(QContainerTraits::has_size_v>); +static_assert(!QContainerTraits::has_size_v); +static_assert(QContainerTraits::has_size_v>); +static_assert(QContainerTraits::has_size_v>); +static_assert(!QContainerTraits::has_size_v>); + +static_assert(QContainerTraits::has_clear_v>); +static_assert(QContainerTraits::has_clear_v>); +static_assert(!QContainerTraits::has_clear_v); +static_assert(QContainerTraits::has_clear_v>); +static_assert(QContainerTraits::has_clear_v>); +static_assert(QContainerTraits::has_clear_v>); + +static_assert(QContainerTraits::has_at_v>); +static_assert(!QContainerTraits::has_at_v>); +static_assert(!QContainerTraits::has_at_v); +static_assert(QContainerTraits::has_at_v>); +static_assert(!QContainerTraits::has_at_v>); +static_assert(!QContainerTraits::has_at_v>); + +static_assert(QContainerTraits::can_get_at_index_v>); +static_assert(!QContainerTraits::can_get_at_index_v>); +static_assert(!QContainerTraits::can_get_at_index_v); +static_assert(QContainerTraits::can_get_at_index_v>); +static_assert(!QContainerTraits::can_get_at_index_v>); +static_assert(!QContainerTraits::can_get_at_index_v>); + +static_assert(QContainerTraits::can_set_at_index_v>); +static_assert(!QContainerTraits::can_set_at_index_v>); +static_assert(!QContainerTraits::can_set_at_index_v); +static_assert(QContainerTraits::can_set_at_index_v>); +static_assert(!QContainerTraits::can_set_at_index_v>); +static_assert(!QContainerTraits::can_set_at_index_v>); + +static_assert(QContainerTraits::has_push_back_v>); +static_assert(!QContainerTraits::has_push_back_v>); +static_assert(!QContainerTraits::has_push_back_v); +static_assert(QContainerTraits::has_push_back_v>); +static_assert(!QContainerTraits::has_push_back_v>); +static_assert(!QContainerTraits::has_push_back_v>); + +static_assert(QContainerTraits::has_push_front_v>); +static_assert(!QContainerTraits::has_push_front_v>); +static_assert(!QContainerTraits::has_push_front_v); +static_assert(!QContainerTraits::has_push_front_v>); +static_assert(!QContainerTraits::has_push_front_v>); +static_assert(QContainerTraits::has_push_front_v>); + +static_assert(!QContainerTraits::has_insert_v>); +static_assert(QContainerTraits::has_insert_v>); +static_assert(!QContainerTraits::has_insert_v); +static_assert(!QContainerTraits::has_insert_v>); +static_assert(QContainerTraits::has_insert_v>); +static_assert(!QContainerTraits::has_insert_v>); + +static_assert(QContainerTraits::has_pop_back_v>); +static_assert(!QContainerTraits::has_pop_back_v>); +static_assert(!QContainerTraits::has_pop_back_v); +static_assert(QContainerTraits::has_pop_back_v>); +static_assert(!QContainerTraits::has_pop_back_v>); +static_assert(!QContainerTraits::has_pop_back_v>); + +static_assert(QContainerTraits::has_pop_front_v>); +static_assert(!QContainerTraits::has_pop_front_v>); +static_assert(!QContainerTraits::has_pop_front_v); +static_assert(!QContainerTraits::has_pop_front_v>); +static_assert(!QContainerTraits::has_pop_front_v>); +static_assert(QContainerTraits::has_pop_front_v>); + +static_assert(QContainerTraits::has_iterator_v>); +static_assert(QContainerTraits::has_iterator_v>); +static_assert(!QContainerTraits::has_iterator_v); +static_assert(QContainerTraits::has_iterator_v>); +static_assert(QContainerTraits::has_iterator_v>); +static_assert(QContainerTraits::has_iterator_v>); + +static_assert(QContainerTraits::has_const_iterator_v>); +static_assert(QContainerTraits::has_const_iterator_v>); +static_assert(!QContainerTraits::has_const_iterator_v); +static_assert(QContainerTraits::has_const_iterator_v>); +static_assert(QContainerTraits::has_const_iterator_v>); +static_assert(QContainerTraits::has_const_iterator_v>); + +static_assert(QContainerTraits::can_get_at_iterator_v>); +static_assert(QContainerTraits::can_get_at_iterator_v>); +static_assert(!QContainerTraits::can_get_at_iterator_v); +static_assert(QContainerTraits::can_get_at_iterator_v>); +static_assert(QContainerTraits::can_get_at_iterator_v>); +static_assert(QContainerTraits::can_get_at_iterator_v>); + +static_assert(QContainerTraits::can_set_at_iterator_v>); +static_assert(!QContainerTraits::can_set_at_iterator_v>); +static_assert(!QContainerTraits::can_get_at_iterator_v); +static_assert(QContainerTraits::can_set_at_iterator_v>); +static_assert(!QContainerTraits::can_set_at_iterator_v>); +static_assert(QContainerTraits::can_set_at_iterator_v>); + +static_assert(QContainerTraits::can_insert_at_iterator_v>); +static_assert(!QContainerTraits::can_insert_at_iterator_v>); +static_assert(!QContainerTraits::can_insert_at_iterator_v); +static_assert(QContainerTraits::can_insert_at_iterator_v>); +static_assert(!QContainerTraits::can_insert_at_iterator_v>); + +// The iterator is only a hint, but syntactically indistinguishable from others. +// It's explicitly there to be signature compatible with std::vector::insert, though. +// Also, inserting into a set is not guaranteed to actually do anything. +static_assert(QContainerTraits::can_insert_at_iterator_v>); + +static_assert(QContainerTraits::can_erase_at_iterator_v>); +static_assert(QContainerTraits::can_erase_at_iterator_v>); +static_assert(!QContainerTraits::can_erase_at_iterator_v); +static_assert(QContainerTraits::can_erase_at_iterator_v>); +static_assert(QContainerTraits::can_erase_at_iterator_v>); +static_assert(!QContainerTraits::can_erase_at_iterator_v>); + +} + +class tst_QMetaContainer: public QObject +{ + Q_OBJECT + +private: + QVector qvector; + std::vector stdvector; + QSet qset; + std::set stdset; + std::forward_list forwardList; + +private slots: + void init(); + void testSequence_data(); + void testSequence(); + void cleanup(); +}; + +void tst_QMetaContainer::init() +{ + qvector = { QMetaType(), QMetaType::fromType(), QMetaType::fromType() }; + stdvector = { QStringLiteral("foo"), QStringLiteral("bar"), QStringLiteral("baz") }; + qset = { "aaa", "bbb", "ccc" }; + stdset = { 1, 2, 3, 42, 45, 11 }; + forwardList = { + QMetaSequence::fromContainer>(), + QMetaSequence::fromContainer>(), + QMetaSequence::fromContainer>(), + QMetaSequence::fromContainer>(), + QMetaSequence::fromContainer>() + }; +} + +void tst_QMetaContainer::cleanup() +{ + qvector.clear(); + stdvector.clear(); + qset.clear(); + stdset.clear(); + forwardList.clear(); +} + +void tst_QMetaContainer::testSequence_data() +{ + QTest::addColumn("container"); + QTest::addColumn("metaSequence"); + QTest::addColumn("metaType"); + QTest::addColumn("hasSize"); + QTest::addColumn("isIndexed"); + QTest::addColumn("canRemove"); + QTest::addColumn("hasBidirectionalIterator"); + QTest::addColumn("hasRandomAccessIterator"); + QTest::addColumn("canInsertAtIterator"); + QTest::addColumn("canEraseAtIterator"); + QTest::addColumn("isOrdered"); + + QTest::addRow("QVector") + << static_cast(&qvector) + << QMetaSequence::fromContainer>() + << QMetaType::fromType() + << true << true << true << true << true << true << true << true; + QTest::addRow("std::vector") + << static_cast(&stdvector) + << QMetaSequence::fromContainer>() + << QMetaType::fromType() + << true << true << true << true << true << true << true << true; + QTest::addRow("QSet") + << static_cast(&qset) + << QMetaSequence::fromContainer>() + << QMetaType::fromType() + << true << false << false << false << false << false << true << false; + QTest::addRow("std::set") + << static_cast(&stdset) + << QMetaSequence::fromContainer>() + << QMetaType::fromType() + << true << false << false << true << false << true << true << false; + QTest::addRow("std::forward_list") + << static_cast(&forwardList) + << QMetaSequence::fromContainer>() + << QMetaType::fromType() + << false << false << true << false << false << false << false << true; +} + +void tst_QMetaContainer::testSequence() +{ + QFETCH(void *, container); + QFETCH(QMetaSequence, metaSequence); + QFETCH(QMetaType, metaType); + QFETCH(bool, hasSize); + QFETCH(bool, isIndexed); + QFETCH(bool, canRemove); + QFETCH(bool, hasBidirectionalIterator); + QFETCH(bool, hasRandomAccessIterator); + QFETCH(bool, canInsertAtIterator); + QFETCH(bool, canEraseAtIterator); + QFETCH(bool, isOrdered); + + QVERIFY(metaSequence.canAddElement()); + QCOMPARE(metaSequence.hasSize(), hasSize); + QCOMPARE(metaSequence.canGetElementAtIndex(), isIndexed); + QCOMPARE(metaSequence.canSetElementAtIndex(), isIndexed); + QCOMPARE(metaSequence.canRemoveElement(), canRemove); + QCOMPARE(metaSequence.hasBidirectionalIterator(), hasBidirectionalIterator); + QCOMPARE(metaSequence.hasRandomAccessIterator(), hasRandomAccessIterator); + QCOMPARE(metaSequence.canInsertElementAtIterator(), canInsertAtIterator); + QCOMPARE(metaSequence.canEraseElementAtIterator(), canEraseAtIterator); + QCOMPARE(metaSequence.isOrdered(), isOrdered); + + QVariant var1(metaType); + QVariant var2(metaType); + QVariant var3(metaType); + + if (hasSize) { + const qsizetype size = metaSequence.size(container); + + // var1 is invalid, and our sets do not contain an invalid value so far. + metaSequence.addElement(container, var1.constData()); + QCOMPARE(metaSequence.size(container), size + 1); + if (canRemove) { + metaSequence.removeElement(container); + QCOMPARE(metaSequence.size(container), size); + } + } else { + metaSequence.addElement(container, var1.constData()); + if (canRemove) + metaSequence.removeElement(container); + } + + if (isIndexed) { + QVERIFY(hasSize); + const qsizetype size = metaSequence.size(container); + for (qsizetype i = 0; i < size; ++i) { + metaSequence.elementAtIndex(container, i, var1.data()); + metaSequence.elementAtIndex(container, size - i - 1, var2.data()); + + metaSequence.setElementAtIndex(container, i, var2.constData()); + metaSequence.setElementAtIndex(container, size - i - 1, var1.constData()); + + metaSequence.elementAtIndex(container, i, var3.data()); + QCOMPARE(var3, var2); + + metaSequence.elementAtIndex(container, size - i - 1, var3.data()); + QCOMPARE(var3, var1); + } + } + + QVERIFY(metaSequence.hasIterator()); + QVERIFY(metaSequence.hasConstIterator()); + QVERIFY(metaSequence.canGetElementAtIterator()); + QVERIFY(metaSequence.canGetElementAtConstIterator()); + + void *it = metaSequence.begin(container); + void *end = metaSequence.end(container); + QVERIFY(it); + QVERIFY(end); + + void *constIt = metaSequence.constBegin(container); + void *constEnd = metaSequence.constEnd(container); + QVERIFY(constIt); + QVERIFY(constEnd); + + const qsizetype size = metaSequence.diffIterator(end, it); + QCOMPARE(size, metaSequence.diffConstIterator(constEnd, constIt)); + if (hasSize) + QCOMPARE(size, metaSequence.size(container)); + + qsizetype count = 0; + for (; !metaSequence.compareIterator(it, end); + metaSequence.advanceIterator(it, 1), metaSequence.advanceConstIterator(constIt, 1)) { + metaSequence.elementAtIterator(it, var1.data()); + if (isIndexed) { + metaSequence.elementAtIndex(container, count, var2.data()); + QCOMPARE(var1, var2); + } + metaSequence.elementAtConstIterator(constIt, var3.data()); + QCOMPARE(var3, var1); + ++count; + } + + QCOMPARE(count, size); + QVERIFY(metaSequence.compareConstIterator(constIt, constEnd)); + + metaSequence.destroyIterator(it); + metaSequence.destroyIterator(end); + metaSequence.destroyConstIterator(constIt); + metaSequence.destroyConstIterator(constEnd); + + if (metaSequence.canSetElementAtIterator()) { + void *it = metaSequence.begin(container); + void *end = metaSequence.end(container); + QVERIFY(it); + QVERIFY(end); + + for (; !metaSequence.compareIterator(it, end); metaSequence.advanceIterator(it, 1)) { + metaSequence.elementAtIterator(it, var1.data()); + metaSequence.setElementAtIterator(it, var2.constData()); + metaSequence.elementAtIterator(it, var3.data()); + QCOMPARE(var2, var3); + var2 = var1; + } + + metaSequence.destroyIterator(it); + metaSequence.destroyIterator(end); + } + + if (metaSequence.hasBidirectionalIterator()) { + void *it = metaSequence.end(container); + void *end = metaSequence.begin(container); + QVERIFY(it); + QVERIFY(end); + + void *constIt = metaSequence.constEnd(container); + void *constEnd = metaSequence.constBegin(container); + QVERIFY(constIt); + QVERIFY(constEnd); + + qsizetype size = 0; + if (metaSequence.hasRandomAccessIterator()) { + size = metaSequence.diffIterator(end, it); + QCOMPARE(size, metaSequence.diffConstIterator(constEnd, constIt)); + } else { + size = -metaSequence.diffIterator(it, end); + } + + if (hasSize) + QCOMPARE(size, -metaSequence.size(container)); + + qsizetype count = 0; + do { + metaSequence.advanceIterator(it, -1); + metaSequence.advanceConstIterator(constIt, -1); + --count; + + metaSequence.elementAtIterator(it, var1.data()); + if (isIndexed) { + metaSequence.elementAtIndex(container, count - size, var2.data()); + QCOMPARE(var1, var2); + } + metaSequence.elementAtConstIterator(constIt, var3.data()); + QCOMPARE(var3, var1); + } while (!metaSequence.compareIterator(it, end)); + + QCOMPARE(count, size); + QVERIFY(metaSequence.compareConstIterator(constIt, constEnd)); + + metaSequence.destroyIterator(it); + metaSequence.destroyIterator(end); + metaSequence.destroyConstIterator(constIt); + metaSequence.destroyConstIterator(constEnd); + } + + if (canInsertAtIterator) { + void *it = metaSequence.begin(container); + void *end = metaSequence.end(container); + + const qsizetype size = metaSequence.diffIterator(end, it); + metaSequence.destroyIterator(end); + + metaSequence.insertElementAtIterator(container, it, var1.constData()); + metaSequence.destroyIterator(it); + it = metaSequence.begin(container); + metaSequence.insertElementAtIterator(container, it, var2.constData()); + metaSequence.destroyIterator(it); + it = metaSequence.begin(container); + metaSequence.insertElementAtIterator(container, it, var3.constData()); + + metaSequence.destroyIterator(it); + + it = metaSequence.begin(container); + end = metaSequence.end(container); + + const qsizetype newSize = metaSequence.diffIterator(end, it); + + if (metaSequence.isOrdered()) { + QCOMPARE(newSize, size + 3); + QVariant var4(metaType); + metaSequence.elementAtIterator(it, var4.data()); + QCOMPARE(var4, var3); + metaSequence.advanceIterator(it, 1); + metaSequence.elementAtIterator(it, var4.data()); + QCOMPARE(var4, var2); + metaSequence.advanceIterator(it, 1); + metaSequence.elementAtIterator(it, var4.data()); + QCOMPARE(var4, var1); + } else { + QVERIFY(newSize >= size); + } + + if (canEraseAtIterator) { + for (int i = 0; i < newSize; ++i) { + metaSequence.destroyIterator(it); + it = metaSequence.begin(container); + metaSequence.eraseElementAtIterator(container, it); + } + + metaSequence.destroyIterator(it); + it = metaSequence.begin(container); + metaSequence.destroyIterator(end); + end = metaSequence.end(container); + QVERIFY(metaSequence.compareIterator(it, end)); + + metaSequence.addElement(container, var1.constData()); + metaSequence.addElement(container, var2.constData()); + metaSequence.addElement(container, var3.constData()); + } + + metaSequence.destroyIterator(end); + metaSequence.destroyIterator(it); + } + + QVERIFY(metaSequence.canClear()); + constIt = metaSequence.constBegin(container); + constEnd = metaSequence.constEnd(container); + QVERIFY(!metaSequence.compareConstIterator(constIt, constEnd)); + metaSequence.destroyConstIterator(constIt); + metaSequence.destroyConstIterator(constEnd); + + metaSequence.clear(container); + constIt = metaSequence.constBegin(container); + constEnd = metaSequence.constEnd(container); + QVERIFY(metaSequence.compareConstIterator(constIt, constEnd)); + metaSequence.destroyConstIterator(constIt); + metaSequence.destroyConstIterator(constEnd); +} + +QTEST_MAIN(tst_QMetaContainer) +#include "tst_qmetacontainer.moc"