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 <milian.wolff@kdab.com>
This commit is contained in:
Marc Mutz 2016-03-03 20:20:42 +01:00 committed by Giuseppe D'Angelo
parent f363540580
commit 2e1763d83a
16 changed files with 833 additions and 14 deletions

View File

@ -0,0 +1,93 @@
/****************************************************************************
**
** Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com>
** Copyright (C) 2018 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
** 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 <QtCore/qglobal.h>
#include <iterator>
#ifndef Q_QDOC
QT_BEGIN_NAMESPACE
namespace QtPrivate
{
template <typename Iterator>
using IfIsInputIterator = typename std::enable_if<
std::is_convertible<typename std::iterator_traits<Iterator>::iterator_category, std::input_iterator_tag>::value,
bool>::type;
template <typename Iterator>
using IfIsForwardIterator = typename std::enable_if<
std::is_convertible<typename std::iterator_traits<Iterator>::iterator_category, std::forward_iterator_tag>::value,
bool>::type;
template <typename Iterator>
using IfIsNotForwardIterator = typename std::enable_if<
!std::is_convertible<typename std::iterator_traits<Iterator>::iterator_category, std::forward_iterator_tag>::value,
bool>::type;
template <typename Container,
typename InputIterator,
IfIsNotForwardIterator<InputIterator> = true>
void reserveIfForwardIterator(Container *, InputIterator, InputIterator)
{
}
template <typename Container,
typename ForwardIterator,
IfIsForwardIterator<ForwardIterator> = true>
void reserveIfForwardIterator(Container *c, ForwardIterator f, ForwardIterator l)
{
c->reserve(static_cast<typename Container::size_type>(std::distance(f, l)));
}
} // namespace QtPrivate
QT_END_NAMESPACE
#endif // Q_QDOC
#endif // QCONTAINERTOOLS_IMPL_H

View File

@ -153,6 +153,14 @@ const QLinkedListData QLinkedListData::shared_null = {
initializer lists.
*/
/*! \fn template <class T> template<typename InputIterator> QLinkedList<T>::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 <class T> QLinkedList<T>::~QLinkedList()
Destroys the list. References to the values in the list, and all

View File

@ -42,6 +42,7 @@
#include <QtCore/qiterator.h>
#include <QtCore/qrefcount.h>
#include <QtCore/qcontainertools_impl.h>
#include <iterator>
#include <list>
@ -84,11 +85,14 @@ public:
inline QLinkedList(const QLinkedList<T> &l) : d(l.d) { d->ref.ref(); if (!d->sharable) detach(); }
#if defined(Q_COMPILER_INITIALIZER_LISTS)
inline QLinkedList(std::initializer_list<T> list)
: d(const_cast<QLinkedListData *>(&QLinkedListData::shared_null))
{
std::copy(list.begin(), list.end(), std::back_inserter(*this));
}
: QLinkedList(list.begin(), list.end()) {}
#endif
template <typename InputIterator, QtPrivate::IfIsInputIterator<InputIterator> = true>
inline QLinkedList(InputIterator first, InputIterator last)
: QLinkedList()
{
std::copy(first, last, std::back_inserter(*this));
}
~QLinkedList();
QLinkedList<T> &operator=(const QLinkedList<T> &);
#ifdef Q_COMPILER_RVALUE_REFS

View File

@ -545,6 +545,14 @@ void **QListData::erase(void **xi)
\since 5.2
*/
/*! \fn template <class T> template<typename InputIterator> QList<T>::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 <class T> QList<T> QList<T>::mid(int pos, int length) const

View File

@ -46,6 +46,7 @@
#include <QtCore/qarraydata.h>
#include <QtCore/qhashfunctions.h>
#include <QtCore/qvector.h>
#include <QtCore/qcontainertools_impl.h>
#include <iterator>
#include <list>
@ -169,9 +170,11 @@ public:
inline void swap(QList<T> &other) noexcept { qSwap(d, other.d); }
#ifdef Q_COMPILER_INITIALIZER_LISTS
inline QList(std::initializer_list<T> args)
: d(const_cast<QListData::Data *>(&QListData::shared_null))
{ reserve(int(args.size())); std::copy(args.begin(), args.end(), std::back_inserter(*this)); }
: QList(args.begin(), args.end()) {}
#endif
template <typename InputIterator, QtPrivate::IfIsInputIterator<InputIterator> = true>
QList(InputIterator first, InputIterator last);
bool operator==(const QList<T> &l) const;
inline bool operator!=(const QList<T> &l) const { return !(*this == l); }
@ -842,6 +845,15 @@ Q_OUTOFLINE_TEMPLATE QList<T>::~QList()
dealloc(d);
}
template <typename T>
template <typename InputIterator, QtPrivate::IfIsInputIterator<InputIterator>>
QList<T>::QList(InputIterator first, InputIterator last)
: QList()
{
QtPrivate::reserveIfForwardIterator(this, first, last);
std::copy(first, last, std::back_inserter(*this));
}
template <typename T>
Q_OUTOFLINE_TEMPLATE bool QList<T>::operator==(const QList<T> &l) const
{

View File

@ -41,6 +41,8 @@
#define QSET_H
#include <QtCore/qhash.h>
#include <QtCore/qcontainertools_impl.h>
#ifdef Q_COMPILER_INITIALIZER_LISTS
#include <initializer_list>
#endif
@ -59,12 +61,16 @@ public:
inline QSet() noexcept {}
#ifdef Q_COMPILER_INITIALIZER_LISTS
inline QSet(std::initializer_list<T> list)
{
reserve(int(list.size()));
for (typename std::initializer_list<T>::const_iterator it = list.begin(); it != list.end(); ++it)
insert(*it);
}
: QSet(list.begin(), list.end()) {}
#endif
template <typename InputIterator, QtPrivate::IfIsInputIterator<InputIterator> = 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!

View File

@ -113,6 +113,17 @@
compiled in C++11 mode.
*/
/*! \fn template <class T> template<typename InputIterator> QSet<T>::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 <class T> void QSet<T>::swap(QSet<T> &other)

View File

@ -847,5 +847,12 @@ int QtPrivate::QStringList_removeDuplicates(QStringList *that)
lists.
*/
/*! \fn template<typename InputIterator> 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

View File

@ -44,6 +44,7 @@
#define QSTRINGLIST_H
#include <QtCore/qalgorithms.h>
#include <QtCore/qcontainertools_impl.h>
#include <QtCore/qregexp.h>
#include <QtCore/qstring.h>
#include <QtCore/qstringmatcher.h>
@ -109,6 +110,9 @@ public:
#ifdef Q_COMPILER_INITIALIZER_LISTS
inline QStringList(std::initializer_list<QString> args) : QList<QString>(args) { }
#endif
template <typename InputIterator, QtPrivate::IfIsInputIterator<InputIterator> = true>
inline QStringList(InputIterator first, InputIterator last)
: QList<QString>(first, last) { }
QStringList &operator=(const QList<QString> &other)
{ QList<QString>::operator=(other); return *this; }

View File

@ -43,6 +43,7 @@
#include <QtCore/qcontainerfwd.h>
#include <QtCore/qglobal.h>
#include <QtCore/qalgorithms.h>
#include <QtCore/qcontainertools_impl.h>
#include <new>
#include <string.h>
@ -71,13 +72,19 @@ public:
#ifdef Q_COMPILER_INITIALIZER_LISTS
QVarLengthArray(std::initializer_list<T> args)
: a(Prealloc), s(0), ptr(reinterpret_cast<T *>(array))
: QVarLengthArray(args.begin(), args.end())
{
if (args.size())
append(args.begin(), int(args.size()));
}
#endif
template <typename InputIterator, QtPrivate::IfIsInputIterator<InputIterator> = 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<T>::isComplex) {
T *i = ptr + s;

View File

@ -110,6 +110,14 @@
lists.
*/
/*! \fn template<class T, int Prealloc> template<typename InputIterator> QVarLengthArray<T, Prealloc>::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<class T, int Prealloc> QVarLengthArray<T, Prealloc>::~QVarLengthArray()

View File

@ -45,6 +45,7 @@
#include <QtCore/qrefcount.h>
#include <QtCore/qarraydata.h>
#include <QtCore/qhashfunctions.h>
#include <QtCore/qcontainertools_impl.h>
#include <iterator>
#include <vector>
@ -81,6 +82,9 @@ public:
inline QVector(std::initializer_list<T> args);
QVector<T> &operator=(std::initializer_list<T> args);
#endif
template <typename InputIterator, QtPrivate::IfIsInputIterator<InputIterator> = true>
inline QVector(InputIterator first, InputIterator last);
bool operator==(const QVector<T> &v) const;
inline bool operator!=(const QVector<T> &v) const { return !(*this == v); }
@ -557,6 +561,15 @@ QT_WARNING_POP
# endif // Q_CC_MSVC
#endif // Q_COMPILER_INITIALIZER_LISTS
template <typename T>
template <typename InputIterator, QtPrivate::IfIsInputIterator<InputIterator>>
QVector<T>::QVector(InputIterator first, InputIterator last)
: QVector()
{
QtPrivate::reserveIfForwardIterator(this, first, last);
std::copy(first, last, std::back_inserter(*this));
}
template <typename T>
void QVector<T>::freeData(Data *x)
{

View File

@ -243,6 +243,13 @@
lists.
*/
/*! \fn template <typename T> template<typename InputIterator> QVector<T>::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 <typename T> QVector<T>::~QVector()

View File

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

View File

@ -34,13 +34,375 @@
#include "qstring.h"
#include "qvarlengtharray.h"
#include "qvector.h"
#include "qdebug.h"
#include <algorithm>
#include <functional>
#include <vector> // for reference
#include <list>
#include <set>
// 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(<forward_list>)
#include <forward_list>
#endif
#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>)
#include <unordered_set>
#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<Movable>
{
std::size_t operator()(Movable m) const Q_DECL_NOTHROW
{
return hash<int>()(m.i);
}
};
template<>
struct hash<Complex>
{
std::size_t operator()(Complex m) const Q_DECL_NOTHROW
{
return hash<int>()(m.i);
}
};
template<>
struct hash<DuplicateStrategyTestType>
{
std::size_t operator()(DuplicateStrategyTestType m) const Q_DECL_NOTHROW
{
return hash<int>()(m.i);
}
};
}
// work around the fact that QVarLengthArray has a non-type
// template parameter, and that breaks non_associative_container_duplicates_strategy
template<typename T>
class VarLengthArray : public QVarLengthArray<T>
{
public:
#ifdef Q_COMPILER_INHERITING_CONSTRUCTORS
using QVarLengthArray<T>::QVarLengthArray;
#else
template<typename InputIterator>
VarLengthArray(InputIterator first, InputIterator last)
: QVarLengthArray<T>(first, last)
{
}
#ifdef Q_COMPILER_INITIALIZER_LISTS
VarLengthArray(std::initializer_list<T> args)
: QVarLengthArray<T>(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 <typename Container>
void ranged_ctor_non_associative_impl() const;
template<template<typename ... T> 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<std::vector<int>>(); }
void ranged_ctor_std_vector_char() { ranged_ctor_non_associative_impl<std::vector<char>>(); }
void ranged_ctor_std_vector_QChar() { ranged_ctor_non_associative_impl<std::vector<QChar>>(); }
void ranged_ctor_std_vector_Movable() { ranged_ctor_non_associative_impl<std::vector<Movable>>(); }
void ranged_ctor_std_vector_Complex() { ranged_ctor_non_associative_impl<std::vector<Complex>>(); }
void ranged_ctor_std_vector_duplicates_strategy() { non_associative_container_duplicates_strategy<std::vector>(); }
void ranged_ctor_QVector_int() { ranged_ctor_non_associative_impl<QVector<int>>(); }
void ranged_ctor_QVector_char() { ranged_ctor_non_associative_impl<QVector<char>>(); }
void ranged_ctor_QVector_QChar() { ranged_ctor_non_associative_impl<QVector<QChar>>(); }
void ranged_ctor_QVector_Movable() { ranged_ctor_non_associative_impl<QVector<Movable>>(); }
void ranged_ctor_QVector_Complex() { ranged_ctor_non_associative_impl<QVector<Complex>>(); }
void ranged_ctor_QVector_duplicates_strategy() { non_associative_container_duplicates_strategy<QVector>(); }
void ranged_ctor_QVarLengthArray_int() { ranged_ctor_non_associative_impl<QVarLengthArray<int>>(); }
void ranged_ctor_QVarLengthArray_Movable() { ranged_ctor_non_associative_impl<QVarLengthArray<Movable>>(); }
void ranged_ctor_QVarLengthArray_Complex() { ranged_ctor_non_associative_impl<QVarLengthArray<Complex>>(); }
void ranged_ctor_QVarLengthArray_duplicates_strategy() { non_associative_container_duplicates_strategy<VarLengthArray>(); } // note the VarLengthArray passed
void ranged_ctor_QList_int() { ranged_ctor_non_associative_impl<QList<int>>(); }
void ranged_ctor_QList_Movable() { ranged_ctor_non_associative_impl<QList<Movable>>(); }
void ranged_ctor_QList_Complex() { ranged_ctor_non_associative_impl<QList<Complex>>(); }
void ranged_ctor_QList_duplicates_strategy() { non_associative_container_duplicates_strategy<QList>(); }
void ranged_ctor_std_list_int() { ranged_ctor_non_associative_impl<std::list<int>>(); }
void ranged_ctor_std_list_Movable() { ranged_ctor_non_associative_impl<std::list<Movable>>(); }
void ranged_ctor_std_list_Complex() { ranged_ctor_non_associative_impl<std::list<Complex>>(); }
void ranged_ctor_std_list_duplicates_strategy() { non_associative_container_duplicates_strategy<std::list>(); }
void ranged_ctor_std_forward_list_int() {
#if COMPILER_HAS_STDLIB_INCLUDE(<forward_list>)
ranged_ctor_non_associative_impl<std::forward_list<int>>();
#else
QSKIP("<forward_list> is needed for this test");
#endif
}
void ranged_ctor_std_forward_list_Movable() {
#if COMPILER_HAS_STDLIB_INCLUDE(<forward_list>)
ranged_ctor_non_associative_impl<std::forward_list<Movable>>();
#else
QSKIP("<forward_list> is needed for this test");
#endif
}
void ranged_ctor_std_forward_list_Complex() {
#if COMPILER_HAS_STDLIB_INCLUDE(<forward_list>)
ranged_ctor_non_associative_impl<std::forward_list<Complex>>();
#else
QSKIP("<forward_list> is needed for this test");
#endif
}
void ranged_ctor_std_forward_list_duplicates_strategy() {
#if COMPILER_HAS_STDLIB_INCLUDE(<forward_list>)
non_associative_container_duplicates_strategy<std::forward_list>();
#else
QSKIP("<forward_list> is needed for this test");
#endif
}
void ranged_ctor_QLinkedList_int() { ranged_ctor_non_associative_impl<QLinkedList<int>>(); }
void ranged_ctor_QLinkedList_Movable() { ranged_ctor_non_associative_impl<QLinkedList<Movable>>(); }
void ranged_ctor_QLinkedList_Complex() { ranged_ctor_non_associative_impl<QLinkedList<Complex>>(); }
void ranged_ctor_QLinkedList_duplicates_strategy() { non_associative_container_duplicates_strategy<QLinkedList>(); }
void ranged_ctor_std_set_int() { ranged_ctor_non_associative_impl<std::set<int>>(); }
void ranged_ctor_std_set_Movable() { ranged_ctor_non_associative_impl<std::set<Movable>>(); }
void ranged_ctor_std_set_Complex() { ranged_ctor_non_associative_impl<std::set<Complex>>(); }
void ranged_ctor_std_set_duplicates_strategy() { non_associative_container_duplicates_strategy<std::set>(); }
void ranged_ctor_std_multiset_int() { ranged_ctor_non_associative_impl<std::multiset<int>>(); }
void ranged_ctor_std_multiset_Movable() { ranged_ctor_non_associative_impl<std::multiset<Movable>>(); }
void ranged_ctor_std_multiset_Complex() { ranged_ctor_non_associative_impl<std::multiset<Complex>>(); }
void ranged_ctor_std_multiset_duplicates_strategy() { non_associative_container_duplicates_strategy<std::multiset>(); }
void ranged_ctor_std_unordered_set_int() {
#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>)
ranged_ctor_non_associative_impl<std::unordered_set<int>>();
#else
QSKIP("<unordered_set> is needed for this test");
#endif
}
void ranged_ctor_std_unordered_set_Movable() {
#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>)
ranged_ctor_non_associative_impl<std::unordered_set<Movable>>();
#else
QSKIP("<unordered_set> is needed for this test");
#endif
}
void ranged_ctor_std_unordered_set_Complex() {
#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>)
ranged_ctor_non_associative_impl<std::unordered_set<Complex>>();
#else
QSKIP("<unordered_set> is needed for this test");
#endif
}
void ranged_ctor_unordered_set_duplicates_strategy() {
#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>)
non_associative_container_duplicates_strategy<std::unordered_set>();
#else
QSKIP("<unordered_set> is needed for this test");
#endif
}
void ranged_ctor_std_unordered_multiset_int() {
#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>)
ranged_ctor_non_associative_impl<std::unordered_multiset<int>>();
#else
QSKIP("<unordered_set> is needed for this test");
#endif
}
void ranged_ctor_std_unordered_multiset_Movable() {
#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>)
ranged_ctor_non_associative_impl<std::unordered_multiset<Movable>>();
#else
QSKIP("<unordered_set> is needed for this test");
#endif
}
void ranged_ctor_std_unordered_multiset_Complex() {
#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>)
ranged_ctor_non_associative_impl<std::unordered_multiset<Complex>>();
#else
QSKIP("<unordered_set> is needed for this test");
#endif
}
void ranged_ctor_std_unordered_multiset_duplicates_strategy() {
#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>)
non_associative_container_duplicates_strategy<std::unordered_multiset>();
#else
QSKIP("<unordered_set> is needed for this test");
#endif
}
void ranged_ctor_QSet_int() { ranged_ctor_non_associative_impl<QSet<int>>(); }
void ranged_ctor_QSet_Movable() { ranged_ctor_non_associative_impl<QSet<Movable>>(); }
void ranged_ctor_QSet_Complex() { ranged_ctor_non_associative_impl<QSet<Complex>>(); }
void ranged_ctor_QSet_duplicates_strategy() { non_associative_container_duplicates_strategy<QSet>(); }
private:
template <typename Container>
void front_back_impl() const;
@ -58,6 +420,237 @@ private Q_SLOTS:
void front_back_QByteArray() { front_back_impl<QByteArray>(); }
};
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 <typename Container>
Container createContainerReference()
{
using V = typename Container::value_type;
return {V(0), V(1), V(2), V(0)};
}
template <typename Container>
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<Container>();
// 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<V> 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<V> 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<V> 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<typename Container>
struct ContainerDuplicatedValuesStrategy {};
template<typename ... T>
struct ContainerDuplicatedValuesStrategy<std::vector<T...>> : ContainerAcceptsDuplicateValues {};
template<typename ... T>
struct ContainerDuplicatedValuesStrategy<QVector<T...>> : ContainerAcceptsDuplicateValues {};
template<typename ... T>
struct ContainerDuplicatedValuesStrategy<QVarLengthArray<T...>> : ContainerAcceptsDuplicateValues {};
template<typename ... T>
struct ContainerDuplicatedValuesStrategy<VarLengthArray<T...>> : ContainerAcceptsDuplicateValues {};
template<typename ... T>
struct ContainerDuplicatedValuesStrategy<QList<T...>> : ContainerAcceptsDuplicateValues {};
template<typename ... T>
struct ContainerDuplicatedValuesStrategy<std::list<T...>> : ContainerAcceptsDuplicateValues {};
#if COMPILER_HAS_STDLIB_INCLUDE(<forward_list>)
template<typename ... T>
struct ContainerDuplicatedValuesStrategy<std::forward_list<T...>> : ContainerAcceptsDuplicateValues {};
#endif
template<typename ... T>
struct ContainerDuplicatedValuesStrategy<QLinkedList<T...>> : ContainerAcceptsDuplicateValues {};
// assuming https://cplusplus.github.io/LWG/lwg-active.html#2844 resolution
template<typename ... T>
struct ContainerDuplicatedValuesStrategy<std::set<T...>> : ContainerRejectsDuplicateValues {};
template<typename ... T>
struct ContainerDuplicatedValuesStrategy<std::multiset<T...>> : ContainerAcceptsDuplicateValues {};
#if COMPILER_HAS_STDLIB_INCLUDE(<unordered_set>)
// assuming https://cplusplus.github.io/LWG/lwg-active.html#2844 resolution
template<typename ... T>
struct ContainerDuplicatedValuesStrategy<std::unordered_set<T...>> : ContainerRejectsDuplicateValues {};
template<typename ... T>
struct ContainerDuplicatedValuesStrategy<std::unordered_multiset<T...>> : ContainerAcceptsDuplicateValues {};
#endif
template<typename ... T>
struct ContainerDuplicatedValuesStrategy<QSet<T...>> : ContainerRejectsDuplicateValues {};
#if defined(Q_COMPILER_INITIALIZER_LISTS)
template<typename Container>
void non_associative_container_check_duplicates_impl(const std::initializer_list<DuplicateStrategyTestType> &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<typename Container>
void non_associative_container_check_duplicates_impl_no_duplicates(const std::initializer_list<DuplicateStrategyTestType> &reference, const Container &c, IterationOnReference ior)
{
std::vector<DuplicateStrategyTestType> 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<DuplicateStrategyTestType>::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<DuplicateStrategyTestType>::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<typename Container>
void non_associative_container_check_duplicates_impl(const std::initializer_list<DuplicateStrategyTestType> &reference, const Container &c, ContainerRejectsDuplicateValues)
{
non_associative_container_check_duplicates_impl_no_duplicates(reference, c, IterationOnReference::ForwardIteration);
}
template<typename Container>
void non_associative_container_check_duplicates_impl(const std::initializer_list<DuplicateStrategyTestType> &reference, const Container &c, ContainerOverwritesDuplicateValues)
{
non_associative_container_check_duplicates_impl_no_duplicates(reference, c, IterationOnReference::ReverseIteration);
}
template<typename Container>
void non_associative_container_check_duplicates(const std::initializer_list<DuplicateStrategyTestType> &reference, const Container &c)
{
non_associative_container_check_duplicates_impl(reference, c, ContainerDuplicatedValuesStrategy<Container>());
}
template<template<class ... T> 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<DuplicateStrategyTestType> reference{ DuplicateStrategyTestType{0},
DuplicateStrategyTestType{1},
DuplicateStrategyTestType{2},
DuplicateStrategyTestType{0} };
Container<DuplicateStrategyTestType> c1{reference};
non_associative_container_check_duplicates(reference, c1);
Container<DuplicateStrategyTestType> c2{reference.begin(), reference.end()};
non_associative_container_check_duplicates(reference, c2);
}
#else
template<template<class ... T> class Container>
void tst_ContainerApiSymmetry::non_associative_container_duplicates_strategy() const
{
QSKIP("Test requires a better compiler");
}
#endif // Q_COMPILER_INITIALIZER_LISTS
template <typename Container>
Container make(int size)
{

View File

@ -30,13 +30,17 @@
#include <qregexp.h>
#include <qregularexpression.h>
#include <qstringlist.h>
#include <qvector.h>
#include <locale.h>
#include <algorithm>
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<QString> 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;