From 2e1763d83a1dacfc5b747934fb77fa7cec7bfe47 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Thu, 3 Mar 2016 20:20:42 +0100 Subject: [PATCH] Non-associative containers: add range constructors Something nice we'd like to detect for array-backed containers is if the iterator passed is a Contiguous one; if the type is also trivially copyable / Q_PRIMITIVE_TYPE, we could memcpy() the whole range. However, there's no trait in the Standard to detect contiguous iterators (the best approximation would be detecting if the iterator is actually a pointer). Also, it's probably not smart to do the work now for QVector since QVector needs refactoring anyhow, and this work will be lost. QString and QByteArray are left in another commit. [ChangeLog][QtCore][QVector] Added range constructor. [ChangeLog][QtCore][QVarLengthArray] Added range constructor. [ChangeLog][QtCore][QList] Added range constructor. [ChangeLog][QtCore][QStringList] Added range constructor. [ChangeLog][QtCore][QLinkedList] Added range constructor. [ChangeLog][QtCore][QSet] Added range constructor. Change-Id: I220edb796053c9c4d31a6dbdc7efc5fc0f6678f9 Reviewed-by: Milian Wolff --- src/corelib/tools/qcontainertools_impl.h | 93 +++ src/corelib/tools/qlinkedlist.cpp | 8 + src/corelib/tools/qlinkedlist.h | 12 +- src/corelib/tools/qlist.cpp | 8 + src/corelib/tools/qlist.h | 16 +- src/corelib/tools/qset.h | 16 +- src/corelib/tools/qset.qdoc | 11 + src/corelib/tools/qstringlist.cpp | 7 + src/corelib/tools/qstringlist.h | 4 + src/corelib/tools/qvarlengtharray.h | 13 +- src/corelib/tools/qvarlengtharray.qdoc | 8 + src/corelib/tools/qvector.h | 13 + src/corelib/tools/qvector.qdoc | 7 + src/corelib/tools/tools.pri | 1 + .../tst_containerapisymmetry.cpp | 593 ++++++++++++++++++ .../tools/qstringlist/tst_qstringlist.cpp | 37 ++ 16 files changed, 833 insertions(+), 14 deletions(-) create mode 100644 src/corelib/tools/qcontainertools_impl.h diff --git a/src/corelib/tools/qcontainertools_impl.h b/src/corelib/tools/qcontainertools_impl.h new file mode 100644 index 0000000000..c2de50b145 --- /dev/null +++ b/src/corelib/tools/qcontainertools_impl.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz +** Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo +** 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$ +** +****************************************************************************/ + +#if 0 +#pragma qt_sync_skip_header_check +#pragma qt_sync_stop_processing +#endif + +#ifndef QCONTAINERTOOLS_IMPL_H +#define QCONTAINERTOOLS_IMPL_H + +#include +#include + +#ifndef Q_QDOC + +QT_BEGIN_NAMESPACE + +namespace QtPrivate +{ +template +using IfIsInputIterator = typename std::enable_if< + std::is_convertible::iterator_category, std::input_iterator_tag>::value, + bool>::type; + +template +using IfIsForwardIterator = typename std::enable_if< + std::is_convertible::iterator_category, std::forward_iterator_tag>::value, + bool>::type; + +template +using IfIsNotForwardIterator = typename std::enable_if< + !std::is_convertible::iterator_category, std::forward_iterator_tag>::value, + bool>::type; + +template = true> +void reserveIfForwardIterator(Container *, InputIterator, InputIterator) +{ +} + +template = true> +void reserveIfForwardIterator(Container *c, ForwardIterator f, ForwardIterator l) +{ + c->reserve(static_cast(std::distance(f, l))); +} +} // namespace QtPrivate + +QT_END_NAMESPACE + +#endif // Q_QDOC + +#endif // QCONTAINERTOOLS_IMPL_H diff --git a/src/corelib/tools/qlinkedlist.cpp b/src/corelib/tools/qlinkedlist.cpp index d9d93862e5..c0450f5cd8 100644 --- a/src/corelib/tools/qlinkedlist.cpp +++ b/src/corelib/tools/qlinkedlist.cpp @@ -153,6 +153,14 @@ const QLinkedListData QLinkedListData::shared_null = { initializer lists. */ +/*! \fn template template QLinkedList::QLinkedList(InputIterator first, InputIterator last) + \since 5.14 + + Constructs a list with the contents in the iterator range [\a first, \a last). + + The value type of \c InputIterator must be convertible to \c T. +*/ + /*! \fn template QLinkedList::~QLinkedList() Destroys the list. References to the values in the list, and all diff --git a/src/corelib/tools/qlinkedlist.h b/src/corelib/tools/qlinkedlist.h index 91367a74b3..83f70deceb 100644 --- a/src/corelib/tools/qlinkedlist.h +++ b/src/corelib/tools/qlinkedlist.h @@ -42,6 +42,7 @@ #include #include +#include #include #include @@ -84,11 +85,14 @@ public: inline QLinkedList(const QLinkedList &l) : d(l.d) { d->ref.ref(); if (!d->sharable) detach(); } #if defined(Q_COMPILER_INITIALIZER_LISTS) inline QLinkedList(std::initializer_list list) - : d(const_cast(&QLinkedListData::shared_null)) - { - std::copy(list.begin(), list.end(), std::back_inserter(*this)); - } + : QLinkedList(list.begin(), list.end()) {} #endif + template = true> + inline QLinkedList(InputIterator first, InputIterator last) + : QLinkedList() + { + std::copy(first, last, std::back_inserter(*this)); + } ~QLinkedList(); QLinkedList &operator=(const QLinkedList &); #ifdef Q_COMPILER_RVALUE_REFS diff --git a/src/corelib/tools/qlist.cpp b/src/corelib/tools/qlist.cpp index 6f8084c676..48617f0539 100644 --- a/src/corelib/tools/qlist.cpp +++ b/src/corelib/tools/qlist.cpp @@ -545,6 +545,14 @@ void **QListData::erase(void **xi) \since 5.2 */ +/*! \fn template template QList::QList(InputIterator first, InputIterator last) + \since 5.14 + + Constructs a QList with the contents in the iterator range [\a first, \a last). + + The value type of \c InputIterator must be convertible to \c T. +*/ + /*! \fn template QList QList::mid(int pos, int length) const diff --git a/src/corelib/tools/qlist.h b/src/corelib/tools/qlist.h index 77d8df4a88..dfb8a8a4ab 100644 --- a/src/corelib/tools/qlist.h +++ b/src/corelib/tools/qlist.h @@ -46,6 +46,7 @@ #include #include #include +#include #include #include @@ -169,9 +170,11 @@ public: inline void swap(QList &other) noexcept { qSwap(d, other.d); } #ifdef Q_COMPILER_INITIALIZER_LISTS inline QList(std::initializer_list args) - : d(const_cast(&QListData::shared_null)) - { reserve(int(args.size())); std::copy(args.begin(), args.end(), std::back_inserter(*this)); } + : QList(args.begin(), args.end()) {} #endif + template = true> + QList(InputIterator first, InputIterator last); + bool operator==(const QList &l) const; inline bool operator!=(const QList &l) const { return !(*this == l); } @@ -842,6 +845,15 @@ Q_OUTOFLINE_TEMPLATE QList::~QList() dealloc(d); } +template +template > +QList::QList(InputIterator first, InputIterator last) + : QList() +{ + QtPrivate::reserveIfForwardIterator(this, first, last); + std::copy(first, last, std::back_inserter(*this)); +} + template Q_OUTOFLINE_TEMPLATE bool QList::operator==(const QList &l) const { diff --git a/src/corelib/tools/qset.h b/src/corelib/tools/qset.h index aa915f7ed1..7ae715d247 100644 --- a/src/corelib/tools/qset.h +++ b/src/corelib/tools/qset.h @@ -41,6 +41,8 @@ #define QSET_H #include +#include + #ifdef Q_COMPILER_INITIALIZER_LISTS #include #endif @@ -59,12 +61,16 @@ public: inline QSet() noexcept {} #ifdef Q_COMPILER_INITIALIZER_LISTS inline QSet(std::initializer_list list) - { - reserve(int(list.size())); - for (typename std::initializer_list::const_iterator it = list.begin(); it != list.end(); ++it) - insert(*it); - } + : QSet(list.begin(), list.end()) {} #endif + template = true> + inline QSet(InputIterator first, InputIterator last) + { + QtPrivate::reserveIfForwardIterator(this, first, last); + for (; first != last; ++first) + insert(*first); + } + // compiler-generated copy/move ctor/assignment operators are fine! // compiler-generated destructor is fine! diff --git a/src/corelib/tools/qset.qdoc b/src/corelib/tools/qset.qdoc index 48863f2399..2e7a5a29ce 100644 --- a/src/corelib/tools/qset.qdoc +++ b/src/corelib/tools/qset.qdoc @@ -113,6 +113,17 @@ compiled in C++11 mode. */ +/*! \fn template template QSet::QSet(InputIterator first, InputIterator last) + \since 5.14 + + Constructs a set with the contents in the iterator range [\a first, \a last). + + The value type of \c InputIterator must be convertible to \c T. + + \note If the range [\a first, \a last) contains duplicate elements, + the first one is retained. +*/ + /*! \fn template void QSet::swap(QSet &other) diff --git a/src/corelib/tools/qstringlist.cpp b/src/corelib/tools/qstringlist.cpp index cc6eaf8ad2..49247d66b8 100644 --- a/src/corelib/tools/qstringlist.cpp +++ b/src/corelib/tools/qstringlist.cpp @@ -847,5 +847,12 @@ int QtPrivate::QStringList_removeDuplicates(QStringList *that) lists. */ + /*! \fn template QStringList::QStringList(InputIterator first, InputIterator last) + \since 5.14 + + Constructs a QStringList with the contents in the iterator range [\a first, \a last). + + The value type of \c InputIterator must be convertible to \c QString. + */ QT_END_NAMESPACE diff --git a/src/corelib/tools/qstringlist.h b/src/corelib/tools/qstringlist.h index 6387161269..5ad01a0658 100644 --- a/src/corelib/tools/qstringlist.h +++ b/src/corelib/tools/qstringlist.h @@ -44,6 +44,7 @@ #define QSTRINGLIST_H #include +#include #include #include #include @@ -109,6 +110,9 @@ public: #ifdef Q_COMPILER_INITIALIZER_LISTS inline QStringList(std::initializer_list args) : QList(args) { } #endif + template = true> + inline QStringList(InputIterator first, InputIterator last) + : QList(first, last) { } QStringList &operator=(const QList &other) { QList::operator=(other); return *this; } diff --git a/src/corelib/tools/qvarlengtharray.h b/src/corelib/tools/qvarlengtharray.h index c03fbb2218..c81af68593 100644 --- a/src/corelib/tools/qvarlengtharray.h +++ b/src/corelib/tools/qvarlengtharray.h @@ -43,6 +43,7 @@ #include #include #include +#include #include #include @@ -71,13 +72,19 @@ public: #ifdef Q_COMPILER_INITIALIZER_LISTS QVarLengthArray(std::initializer_list args) - : a(Prealloc), s(0), ptr(reinterpret_cast(array)) + : QVarLengthArray(args.begin(), args.end()) { - if (args.size()) - append(args.begin(), int(args.size())); } #endif + template = true> + inline QVarLengthArray(InputIterator first, InputIterator last) + : QVarLengthArray() + { + QtPrivate::reserveIfForwardIterator(this, first, last); + std::copy(first, last, std::back_inserter(*this)); + } + inline ~QVarLengthArray() { if (QTypeInfo::isComplex) { T *i = ptr + s; diff --git a/src/corelib/tools/qvarlengtharray.qdoc b/src/corelib/tools/qvarlengtharray.qdoc index bc8df82517..80769e3769 100644 --- a/src/corelib/tools/qvarlengtharray.qdoc +++ b/src/corelib/tools/qvarlengtharray.qdoc @@ -110,6 +110,14 @@ lists. */ +/*! \fn template template QVarLengthArray::QVarLengthArray(InputIterator first, InputIterator last) + \since 5.14 + + Constructs an array with the contents in the iterator range [\a first, \a last). + + The value type of \c InputIterator must be convertible to \c T. +*/ + /*! \fn template QVarLengthArray::~QVarLengthArray() diff --git a/src/corelib/tools/qvector.h b/src/corelib/tools/qvector.h index 096c369e51..6cbf794c6c 100644 --- a/src/corelib/tools/qvector.h +++ b/src/corelib/tools/qvector.h @@ -45,6 +45,7 @@ #include #include #include +#include #include #include @@ -81,6 +82,9 @@ public: inline QVector(std::initializer_list args); QVector &operator=(std::initializer_list args); #endif + template = true> + inline QVector(InputIterator first, InputIterator last); + bool operator==(const QVector &v) const; inline bool operator!=(const QVector &v) const { return !(*this == v); } @@ -557,6 +561,15 @@ QT_WARNING_POP # endif // Q_CC_MSVC #endif // Q_COMPILER_INITIALIZER_LISTS +template +template > +QVector::QVector(InputIterator first, InputIterator last) + : QVector() +{ + QtPrivate::reserveIfForwardIterator(this, first, last); + std::copy(first, last, std::back_inserter(*this)); +} + template void QVector::freeData(Data *x) { diff --git a/src/corelib/tools/qvector.qdoc b/src/corelib/tools/qvector.qdoc index 69bbb5f9a2..cb47d36356 100644 --- a/src/corelib/tools/qvector.qdoc +++ b/src/corelib/tools/qvector.qdoc @@ -243,6 +243,13 @@ lists. */ +/*! \fn template template QVector::QVector(InputIterator first, InputIterator last) + \since 5.14 + + Constructs a vector with the contents in the iterator range [\a first, \a last). + + The value type of \c InputIterator must be convertible to \c T. +*/ /*! \fn template QVector::~QVector() diff --git a/src/corelib/tools/tools.pri b/src/corelib/tools/tools.pri index 995bab694e..5dcb6c9ee0 100644 --- a/src/corelib/tools/tools.pri +++ b/src/corelib/tools/tools.pri @@ -18,6 +18,7 @@ HEADERS += \ tools/qcollator.h \ tools/qcollator_p.h \ tools/qcontainerfwd.h \ + tools/qcontainertools_impl.h \ tools/qcryptographichash.h \ tools/qdatetime.h \ tools/qdatetime_p.h \ diff --git a/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp b/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp index 3b8111f1a3..196276c52f 100644 --- a/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp +++ b/tests/auto/corelib/tools/containerapisymmetry/tst_containerapisymmetry.cpp @@ -34,13 +34,375 @@ #include "qstring.h" #include "qvarlengtharray.h" #include "qvector.h" +#include "qdebug.h" +#include +#include #include // for reference +#include +#include + +// MSVC has these containers from the Standard Library, but it lacks +// a __has_include mechanism (that we need to use for other stdlibs). +// For the sake of increasing our test coverage, work around the issue. + +#ifdef Q_CC_MSVC +#define COMPILER_HAS_STDLIB_INCLUDE(x) 1 +#else +#define COMPILER_HAS_STDLIB_INCLUDE(x) QT_HAS_INCLUDE(x) +#endif + +#if COMPILER_HAS_STDLIB_INCLUDE() +#include +#endif +#if COMPILER_HAS_STDLIB_INCLUDE() +#include +#endif + +struct Movable +{ + explicit Movable(int i = 0) Q_DECL_NOTHROW + : i(i) + { + ++instanceCount; + } + + Movable(const Movable &m) + : i(m.i) + { + ++instanceCount; + } + + ~Movable() + { + --instanceCount; + } + + int i; + static int instanceCount; +}; + +int Movable::instanceCount = 0; +bool operator==(Movable lhs, Movable rhs) Q_DECL_NOTHROW { return lhs.i == rhs.i; } +bool operator!=(Movable lhs, Movable rhs) Q_DECL_NOTHROW { return lhs.i != rhs.i; } +bool operator<(Movable lhs, Movable rhs) Q_DECL_NOTHROW { return lhs.i < rhs.i; } + +uint qHash(Movable m, uint seed = 0) Q_DECL_NOTHROW { return qHash(m.i, seed); } +QDebug &operator<<(QDebug &d, Movable m) +{ + const QDebugStateSaver saver(d); + return d.nospace() << "Movable(" << m.i << ")"; +} + +QT_BEGIN_NAMESPACE +Q_DECLARE_TYPEINFO(Movable, Q_MOVABLE_TYPE); +QT_END_NAMESPACE + +struct Complex +{ + explicit Complex(int i = 0) Q_DECL_NOTHROW + : i(i) + { + ++instanceCount; + } + + Complex(const Complex &c) + : i(c.i) + { + ++instanceCount; + } + + ~Complex() + { + --instanceCount; + } + + int i; + static int instanceCount; +}; + +int Complex::instanceCount = 0; +bool operator==(Complex lhs, Complex rhs) Q_DECL_NOTHROW { return lhs.i == rhs.i; } +bool operator!=(Complex lhs, Complex rhs) Q_DECL_NOTHROW { return lhs.i != rhs.i; } +bool operator<(Complex lhs, Complex rhs) Q_DECL_NOTHROW { return lhs.i < rhs.i; } + +uint qHash(Complex c, uint seed = 0) Q_DECL_NOTHROW { return qHash(c.i, seed); } +QDebug &operator<<(QDebug &d, Complex c) +{ + const QDebugStateSaver saver(d); + return d.nospace() << "Complex(" << c.i << ")"; +} + + +struct DuplicateStrategyTestType +{ + explicit DuplicateStrategyTestType(int i = 0) Q_DECL_NOTHROW + : i(i), + j(++counter) + { + } + + int i; + int j; + + static int counter; +}; + +int DuplicateStrategyTestType::counter = 0; + +// only look at the i member, not j. j allows us to identify which instance +// gets inserted in containers that don't allow for duplicates +bool operator==(DuplicateStrategyTestType lhs, DuplicateStrategyTestType rhs) Q_DECL_NOTHROW +{ + return lhs.i == rhs.i; +} + +bool operator!=(DuplicateStrategyTestType lhs, DuplicateStrategyTestType rhs) Q_DECL_NOTHROW +{ + return lhs.i != rhs.i; +} + +bool operator<(DuplicateStrategyTestType lhs, DuplicateStrategyTestType rhs) Q_DECL_NOTHROW +{ + return lhs.i < rhs.i; +} + +uint qHash(DuplicateStrategyTestType c, uint seed = 0) Q_DECL_NOTHROW +{ + return qHash(c.i, seed); +} + +bool reallyEqual(DuplicateStrategyTestType lhs, DuplicateStrategyTestType rhs) Q_DECL_NOTHROW +{ + return lhs.i == rhs.i && lhs.j == rhs.j; +} + +QDebug &operator<<(QDebug &d, DuplicateStrategyTestType c) +{ + const QDebugStateSaver saver(d); + return d.nospace() << "DuplicateStrategyTestType(" << c.i << "," << c.j << ")"; +} + + +namespace std { +template<> +struct hash +{ + std::size_t operator()(Movable m) const Q_DECL_NOTHROW + { + return hash()(m.i); + } +}; + +template<> +struct hash +{ + std::size_t operator()(Complex m) const Q_DECL_NOTHROW + { + return hash()(m.i); + } +}; + +template<> +struct hash +{ + std::size_t operator()(DuplicateStrategyTestType m) const Q_DECL_NOTHROW + { + return hash()(m.i); + } +}; +} + +// work around the fact that QVarLengthArray has a non-type +// template parameter, and that breaks non_associative_container_duplicates_strategy +template +class VarLengthArray : public QVarLengthArray +{ +public: +#ifdef Q_COMPILER_INHERITING_CONSTRUCTORS + using QVarLengthArray::QVarLengthArray; +#else + template + VarLengthArray(InputIterator first, InputIterator last) + : QVarLengthArray(first, last) + { + } + +#ifdef Q_COMPILER_INITIALIZER_LISTS + VarLengthArray(std::initializer_list args) + : QVarLengthArray(args) + { + } +#endif +#endif +}; class tst_ContainerApiSymmetry : public QObject { Q_OBJECT + int m_movableInstanceCount; + int m_complexInstanceCount; + +private Q_SLOTS: + void init(); + void cleanup(); + +private: + template + void ranged_ctor_non_associative_impl() const; + + template class Container> + void non_associative_container_duplicates_strategy() const; + +private Q_SLOTS: + // non associative + void ranged_ctor_std_vector_int() { ranged_ctor_non_associative_impl>(); } + void ranged_ctor_std_vector_char() { ranged_ctor_non_associative_impl>(); } + void ranged_ctor_std_vector_QChar() { ranged_ctor_non_associative_impl>(); } + void ranged_ctor_std_vector_Movable() { ranged_ctor_non_associative_impl>(); } + void ranged_ctor_std_vector_Complex() { ranged_ctor_non_associative_impl>(); } + void ranged_ctor_std_vector_duplicates_strategy() { non_associative_container_duplicates_strategy(); } + + void ranged_ctor_QVector_int() { ranged_ctor_non_associative_impl>(); } + void ranged_ctor_QVector_char() { ranged_ctor_non_associative_impl>(); } + void ranged_ctor_QVector_QChar() { ranged_ctor_non_associative_impl>(); } + void ranged_ctor_QVector_Movable() { ranged_ctor_non_associative_impl>(); } + void ranged_ctor_QVector_Complex() { ranged_ctor_non_associative_impl>(); } + void ranged_ctor_QVector_duplicates_strategy() { non_associative_container_duplicates_strategy(); } + + void ranged_ctor_QVarLengthArray_int() { ranged_ctor_non_associative_impl>(); } + void ranged_ctor_QVarLengthArray_Movable() { ranged_ctor_non_associative_impl>(); } + void ranged_ctor_QVarLengthArray_Complex() { ranged_ctor_non_associative_impl>(); } + void ranged_ctor_QVarLengthArray_duplicates_strategy() { non_associative_container_duplicates_strategy(); } // note the VarLengthArray passed + + void ranged_ctor_QList_int() { ranged_ctor_non_associative_impl>(); } + void ranged_ctor_QList_Movable() { ranged_ctor_non_associative_impl>(); } + void ranged_ctor_QList_Complex() { ranged_ctor_non_associative_impl>(); } + void ranged_ctor_QList_duplicates_strategy() { non_associative_container_duplicates_strategy(); } + + void ranged_ctor_std_list_int() { ranged_ctor_non_associative_impl>(); } + void ranged_ctor_std_list_Movable() { ranged_ctor_non_associative_impl>(); } + void ranged_ctor_std_list_Complex() { ranged_ctor_non_associative_impl>(); } + void ranged_ctor_std_list_duplicates_strategy() { non_associative_container_duplicates_strategy(); } + + void ranged_ctor_std_forward_list_int() { +#if COMPILER_HAS_STDLIB_INCLUDE() + ranged_ctor_non_associative_impl>(); +#else + QSKIP(" is needed for this test"); +#endif + } + + void ranged_ctor_std_forward_list_Movable() { +#if COMPILER_HAS_STDLIB_INCLUDE() + ranged_ctor_non_associative_impl>(); +#else + QSKIP(" is needed for this test"); +#endif + } + + void ranged_ctor_std_forward_list_Complex() { +#if COMPILER_HAS_STDLIB_INCLUDE() + ranged_ctor_non_associative_impl>(); +#else + QSKIP(" is needed for this test"); +#endif + } + + void ranged_ctor_std_forward_list_duplicates_strategy() { +#if COMPILER_HAS_STDLIB_INCLUDE() + non_associative_container_duplicates_strategy(); +#else + QSKIP(" is needed for this test"); +#endif + } + + void ranged_ctor_QLinkedList_int() { ranged_ctor_non_associative_impl>(); } + void ranged_ctor_QLinkedList_Movable() { ranged_ctor_non_associative_impl>(); } + void ranged_ctor_QLinkedList_Complex() { ranged_ctor_non_associative_impl>(); } + void ranged_ctor_QLinkedList_duplicates_strategy() { non_associative_container_duplicates_strategy(); } + + void ranged_ctor_std_set_int() { ranged_ctor_non_associative_impl>(); } + void ranged_ctor_std_set_Movable() { ranged_ctor_non_associative_impl>(); } + void ranged_ctor_std_set_Complex() { ranged_ctor_non_associative_impl>(); } + void ranged_ctor_std_set_duplicates_strategy() { non_associative_container_duplicates_strategy(); } + + void ranged_ctor_std_multiset_int() { ranged_ctor_non_associative_impl>(); } + void ranged_ctor_std_multiset_Movable() { ranged_ctor_non_associative_impl>(); } + void ranged_ctor_std_multiset_Complex() { ranged_ctor_non_associative_impl>(); } + void ranged_ctor_std_multiset_duplicates_strategy() { non_associative_container_duplicates_strategy(); } + + void ranged_ctor_std_unordered_set_int() { +#if COMPILER_HAS_STDLIB_INCLUDE() + ranged_ctor_non_associative_impl>(); +#else + QSKIP(" is needed for this test"); +#endif + } + + void ranged_ctor_std_unordered_set_Movable() { +#if COMPILER_HAS_STDLIB_INCLUDE() + ranged_ctor_non_associative_impl>(); +#else + QSKIP(" is needed for this test"); +#endif + } + + void ranged_ctor_std_unordered_set_Complex() { +#if COMPILER_HAS_STDLIB_INCLUDE() + ranged_ctor_non_associative_impl>(); +#else + QSKIP(" is needed for this test"); +#endif + } + + void ranged_ctor_unordered_set_duplicates_strategy() { +#if COMPILER_HAS_STDLIB_INCLUDE() + non_associative_container_duplicates_strategy(); +#else + QSKIP(" is needed for this test"); +#endif + } + + + void ranged_ctor_std_unordered_multiset_int() { +#if COMPILER_HAS_STDLIB_INCLUDE() + ranged_ctor_non_associative_impl>(); +#else + QSKIP(" is needed for this test"); +#endif + } + + void ranged_ctor_std_unordered_multiset_Movable() { +#if COMPILER_HAS_STDLIB_INCLUDE() + ranged_ctor_non_associative_impl>(); +#else + QSKIP(" is needed for this test"); +#endif + } + + void ranged_ctor_std_unordered_multiset_Complex() { +#if COMPILER_HAS_STDLIB_INCLUDE() + ranged_ctor_non_associative_impl>(); +#else + QSKIP(" is needed for this test"); +#endif + } + + void ranged_ctor_std_unordered_multiset_duplicates_strategy() { +#if COMPILER_HAS_STDLIB_INCLUDE() + non_associative_container_duplicates_strategy(); +#else + QSKIP(" is needed for this test"); +#endif + } + + void ranged_ctor_QSet_int() { ranged_ctor_non_associative_impl>(); } + void ranged_ctor_QSet_Movable() { ranged_ctor_non_associative_impl>(); } + void ranged_ctor_QSet_Complex() { ranged_ctor_non_associative_impl>(); } + void ranged_ctor_QSet_duplicates_strategy() { non_associative_container_duplicates_strategy(); } + private: template void front_back_impl() const; @@ -58,6 +420,237 @@ private Q_SLOTS: void front_back_QByteArray() { front_back_impl(); } }; +void tst_ContainerApiSymmetry::init() +{ + m_movableInstanceCount = Movable::instanceCount; + m_complexInstanceCount = Complex::instanceCount; +} + +void tst_ContainerApiSymmetry::cleanup() +{ + // very simple leak check + QCOMPARE(Movable::instanceCount, m_movableInstanceCount); + QCOMPARE(Complex::instanceCount, m_complexInstanceCount); +} + +template +Container createContainerReference() +{ + using V = typename Container::value_type; + + return {V(0), V(1), V(2), V(0)}; +} + +template +void tst_ContainerApiSymmetry::ranged_ctor_non_associative_impl() const +{ + using V = typename Container::value_type; + + // the double V(0) is deliberate + const auto reference = createContainerReference(); + + // plain array + const V values1[] = { V(0), V(1), V(2), V(0) }; + + const Container c1(values1, values1 + sizeof(values1)/sizeof(values1[0])); + + // from QList + QList l2; + l2 << V(0) << V(1) << V(2) << V(0); + + const Container c2a(l2.begin(), l2.end()); + const Container c2b(l2.cbegin(), l2.cend()); + + // from std::list + std::list l3; + l3.push_back(V(0)); + l3.push_back(V(1)); + l3.push_back(V(2)); + l3.push_back(V(0)); + const Container c3a(l3.begin(), l3.end()); + + // from const std::list + const std::list l3c = l3; + const Container c3b(l3c.begin(), l3c.end()); + + // from itself + const Container c4(reference.begin(), reference.end()); + + QCOMPARE(c1, reference); + QCOMPARE(c2a, reference); + QCOMPARE(c2b, reference); + QCOMPARE(c3a, reference); + QCOMPARE(c3b, reference); + QCOMPARE(c4, reference); +} + + +// type traits for detecting whether a non-associative container +// accepts duplicated values, and if it doesn't, whether construction/insertion +// prefer the new values (overwriting) or the old values (rejecting) + +struct ContainerAcceptsDuplicateValues {}; +struct ContainerOverwritesDuplicateValues {}; +struct ContainerRejectsDuplicateValues {}; + +template +struct ContainerDuplicatedValuesStrategy {}; + +template +struct ContainerDuplicatedValuesStrategy> : ContainerAcceptsDuplicateValues {}; + +template +struct ContainerDuplicatedValuesStrategy> : ContainerAcceptsDuplicateValues {}; + +template +struct ContainerDuplicatedValuesStrategy> : ContainerAcceptsDuplicateValues {}; + +template +struct ContainerDuplicatedValuesStrategy> : ContainerAcceptsDuplicateValues {}; + +template +struct ContainerDuplicatedValuesStrategy> : ContainerAcceptsDuplicateValues {}; + +template +struct ContainerDuplicatedValuesStrategy> : ContainerAcceptsDuplicateValues {}; + +#if COMPILER_HAS_STDLIB_INCLUDE() +template +struct ContainerDuplicatedValuesStrategy> : ContainerAcceptsDuplicateValues {}; +#endif + +template +struct ContainerDuplicatedValuesStrategy> : ContainerAcceptsDuplicateValues {}; + +// assuming https://cplusplus.github.io/LWG/lwg-active.html#2844 resolution +template +struct ContainerDuplicatedValuesStrategy> : ContainerRejectsDuplicateValues {}; + +template +struct ContainerDuplicatedValuesStrategy> : ContainerAcceptsDuplicateValues {}; + +#if COMPILER_HAS_STDLIB_INCLUDE() +// assuming https://cplusplus.github.io/LWG/lwg-active.html#2844 resolution +template +struct ContainerDuplicatedValuesStrategy> : ContainerRejectsDuplicateValues {}; + +template +struct ContainerDuplicatedValuesStrategy> : ContainerAcceptsDuplicateValues {}; +#endif + +template +struct ContainerDuplicatedValuesStrategy> : ContainerRejectsDuplicateValues {}; + +#if defined(Q_COMPILER_INITIALIZER_LISTS) +template +void non_associative_container_check_duplicates_impl(const std::initializer_list &reference, const Container &c, ContainerAcceptsDuplicateValues) +{ + // do a deep check for equality, not ordering + QVERIFY(std::distance(reference.begin(), reference.end()) == std::distance(c.begin(), c.end())); + QVERIFY(std::is_permutation(reference.begin(), reference.end(), c.begin(), &reallyEqual)); +} + +enum class IterationOnReference +{ + ForwardIteration, + ReverseIteration +}; + +template +void non_associative_container_check_duplicates_impl_no_duplicates(const std::initializer_list &reference, const Container &c, IterationOnReference ior) +{ + std::vector valuesAlreadySeen; + + // iterate on reference forward or backwards, depending on ior. this will give + // us the expected semantics when checking for duplicated values into c + auto it = [&reference, ior]() { + switch (ior) { + case IterationOnReference::ForwardIteration: return reference.begin(); + case IterationOnReference::ReverseIteration: return reference.end() - 1; + }; + return std::initializer_list::const_iterator(); + }(); + + const auto &end = [&reference, ior]() { + switch (ior) { + case IterationOnReference::ForwardIteration: return reference.end(); + case IterationOnReference::ReverseIteration: return reference.begin() - 1; + }; + return std::initializer_list::const_iterator(); + }(); + + while (it != end) { + const auto &value = *it; + + // check that there is indeed the same value in the container (using operator==) + const auto &valueInContainerIterator = std::find(c.begin(), c.end(), value); + QVERIFY(valueInContainerIterator != c.end()); + QVERIFY(value == *valueInContainerIterator); + + // if the value is a duplicate, we don't expect to find it in the container + // (when doing a deep comparison). otherwise it should be there + + const auto &valuesAlreadySeenIterator = std::find(valuesAlreadySeen.cbegin(), valuesAlreadySeen.cend(), value); + const bool valueIsDuplicated = (valuesAlreadySeenIterator != valuesAlreadySeen.cend()); + + const auto &reallyEqualCheck = [&value](const DuplicateStrategyTestType &v) { return reallyEqual(value, v); }; + QCOMPARE(std::find_if(c.begin(), c.end(), reallyEqualCheck) == c.end(), valueIsDuplicated); + + valuesAlreadySeen.push_back(value); + + switch (ior) { + case IterationOnReference::ForwardIteration: + ++it; + break; + case IterationOnReference::ReverseIteration: + --it; + break; + }; + } + +} + +template +void non_associative_container_check_duplicates_impl(const std::initializer_list &reference, const Container &c, ContainerRejectsDuplicateValues) +{ + non_associative_container_check_duplicates_impl_no_duplicates(reference, c, IterationOnReference::ForwardIteration); +} + +template +void non_associative_container_check_duplicates_impl(const std::initializer_list &reference, const Container &c, ContainerOverwritesDuplicateValues) +{ + non_associative_container_check_duplicates_impl_no_duplicates(reference, c, IterationOnReference::ReverseIteration); +} + +template +void non_associative_container_check_duplicates(const std::initializer_list &reference, const Container &c) +{ + non_associative_container_check_duplicates_impl(reference, c, ContainerDuplicatedValuesStrategy()); +} + +template class Container> +void tst_ContainerApiSymmetry::non_associative_container_duplicates_strategy() const +{ + // first and last are "duplicates" -- they compare equal for operator==, + // but they differ when using reallyEqual + const std::initializer_list reference{ DuplicateStrategyTestType{0}, + DuplicateStrategyTestType{1}, + DuplicateStrategyTestType{2}, + DuplicateStrategyTestType{0} }; + Container c1{reference}; + non_associative_container_check_duplicates(reference, c1); + + Container c2{reference.begin(), reference.end()}; + non_associative_container_check_duplicates(reference, c2); +} +#else +template class Container> +void tst_ContainerApiSymmetry::non_associative_container_duplicates_strategy() const +{ + QSKIP("Test requires a better compiler"); +} +#endif // Q_COMPILER_INITIALIZER_LISTS + template Container make(int size) { diff --git a/tests/auto/corelib/tools/qstringlist/tst_qstringlist.cpp b/tests/auto/corelib/tools/qstringlist/tst_qstringlist.cpp index 42bdf62a93..37368c3dfc 100644 --- a/tests/auto/corelib/tools/qstringlist/tst_qstringlist.cpp +++ b/tests/auto/corelib/tools/qstringlist/tst_qstringlist.cpp @@ -30,13 +30,17 @@ #include #include #include +#include #include +#include + class tst_QStringList : public QObject { Q_OBJECT private slots: + void constructors(); void sort(); void filter(); void replaceInStrings(); @@ -66,6 +70,39 @@ private slots: extern const char email[]; +void tst_QStringList::constructors() +{ + { + QStringList list; + QVERIFY(list.isEmpty()); + QCOMPARE(list.size(), 0); + QVERIFY(list == QStringList()); + } + { + QString str = "abc"; + QStringList list(str); + QVERIFY(!list.isEmpty()); + QCOMPARE(list.size(), 1); + QCOMPARE(list.at(0), str); + } + { + QStringList list{ "a", "b", "c" }; + QVERIFY(!list.isEmpty()); + QCOMPARE(list.size(), 3); + QCOMPARE(list.at(0), "a"); + QCOMPARE(list.at(1), "b"); + QCOMPARE(list.at(2), "c"); + } + { + const QVector reference{ "a", "b", "c" }; + QCOMPARE(reference.size(), 3); + + QStringList list(reference.cbegin(), reference.cend()); + QCOMPARE(list.size(), reference.size()); + QVERIFY(std::equal(list.cbegin(), list.cend(), reference.cbegin())); + } +} + void tst_QStringList::indexOf_regExp() { QStringList list;