diff options
| author | Chris Adams <chris.adams@qinetic.com.au> | 2021-02-16 17:01:00 +1000 |
|---|---|---|
| committer | Chris Adams <chris.adams@qinetic.com.au> | 2021-04-08 10:39:09 +1000 |
| commit | c97235ff5987e67fd67b3ad32bac67ccefa9bf7b (patch) | |
| tree | 1df24d2ab73e1693fd063235a4f16eff13265f92 | |
| parent | 6c5ad957ea9ea3161b9db0af78e5b293d68d27a9 (diff) | |
Add QmfList for reference-stable list semantics
QmfList provides some QList-esque syntax sugar around std::list,
providing a container with reference-stability (i.e. references
and iterators are not invalidated after non-const operations).
Change-Id: I9ecc2a6f5b926a9ea98425d960d2f915c26975a9
Reviewed-by: Christopher Adams <chris.adams@jolla.com>
Reviewed-by: David Llewellyn-Jones <david.llewellyn-jones@jolla.com>
| -rw-r--r-- | src/libraries/qmfclient/qmfclient.pro | 2 | ||||
| -rw-r--r-- | src/libraries/qmfclient/qmflist.h | 113 | ||||
| -rw-r--r-- | tests/tests.pro | 3 | ||||
| -rw-r--r-- | tests/tst_qmflist/tst_qmflist.cpp | 297 | ||||
| -rw-r--r-- | tests/tst_qmflist/tst_qmflist.pro | 7 |
5 files changed, 420 insertions, 2 deletions
diff --git a/src/libraries/qmfclient/qmfclient.pro b/src/libraries/qmfclient/qmfclient.pro index 2e79810d..0a87d63f 100644 --- a/src/libraries/qmfclient/qmfclient.pro +++ b/src/libraries/qmfclient/qmfclient.pro @@ -19,7 +19,6 @@ contains(DEFINES, USE_HTML_PARSER) { QT += gui } - HEADERS += \ qmailaccount.h \ qmailaccountconfiguration.h \ @@ -58,6 +57,7 @@ HEADERS += \ qmailthreadkey.h \ qmailthreadlistmodel.h \ qmailthreadsortkey.h \ + qmflist.h \ qprivateimplementation.h \ qprivateimplementationdef_p.h \ support/qmailglobal.h \ diff --git a/src/libraries/qmfclient/qmflist.h b/src/libraries/qmfclient/qmflist.h new file mode 100644 index 00000000..1f134e1f --- /dev/null +++ b/src/libraries/qmfclient/qmflist.h @@ -0,0 +1,113 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Messaging Framework. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMF_QMFLIST_H +#define QMF_QMFLIST_H + +#include <algorithm> +#include <list> +#include <QList> + +/* + * QMF made use of stable-reference semantics of QList in Qt5. + * In Qt6, non-const operations will cause reference and + * iterator invalidation, and thus QList cannot be used in + * those cases. + * + * This class provides some QList-like API sugar (at the cost + * of O(N) performance for the sugar operations) around a + * std::list (which provides reference-stability). + */ + +template<class T> +class QmfList : public std::list<T> +{ +public: + ~QmfList() {} + QmfList() : std::list<T>() {} + QmfList(const QmfList<T> &other) : std::list<T>(other) {} + QmfList(std::initializer_list<T> t) : std::list<T>(t) {} + template <typename InputIterator> QmfList(InputIterator start, InputIterator end) : std::list<T>(start, end) {} + QmfList(const std::list<T> &stdlist) : std::list<T>(stdlist) {} + QmfList(const QList<T> &qlist) : std::list<T>() { for (const T &t : qlist) this->push_back(t); } + + QmfList<T> &operator=(const QmfList<T> &other) { this->clear(); for (const T &t : other) this->push_back(t); return *this; } + QmfList<T> &operator+(const QmfList<T> &other) { this->append(other); return *this; } + + qsizetype count() const { return this->size(); } + bool isEmpty() const { return this->count() == 0; }; + void append(const T &t) { this->push_back(t); } + void append(const QmfList<T> &list) { for (const T &t : list) this->push_back(t); } + const T& last() const { return this->back(); } + const T& first() const { return this->front(); } + T takeFirst() { T t = this->front(); this->pop_front(); return t; } + void removeAt(qsizetype index) { this->erase(std::next(this->begin(), index)); } + void removeOne(const T &t) { auto it = std::find(this->begin(), this->end(), t); if (it != this->end()) this->erase(it); } + void removeAll(const T &t) { this->remove(t); } + const T& at(qsizetype index) const { return *std::next(this->cbegin(), index); } + T& operator[](qsizetype index) { return *std::next(this->begin(), index); } + const T& operator[](qsizetype index) const { return *std::next(this->cbegin(), index); } + bool contains(const T &t) const { return std::find(this->cbegin(), this->cend(), t) != this->cend(); } + qsizetype indexOf(const T &t) const { qsizetype i = 0; for (const T &v : *this) { if (t == v) return i; else i++; } return -1; } + + QmfList<T>& operator<<(const T &t) { this->append(t); return *this; } + + QList<T> toQList() const { return QList<T>(this->cbegin(), this->cend()); } + static QmfList<T> fromQList(const QList<T> &list) { return QmfList<T>(list.constBegin(), list.constEnd()); } +}; + +template<typename T> +QmfList<T> operator+(const QmfList<T> &first, const QmfList<T> &second) +{ + QmfList<T> ret = first; + ret.append(second); + return ret; +} + +template <typename T> +QDataStream &operator<<(QDataStream &out, const QmfList<T> &list) +{ + out << list.toQList(); + return out; +} +template <typename T> +QDataStream &operator>>(QDataStream &in, QmfList<T> &list) +{ + QList<T> qlist; + in >> qlist; + list = QmfList<T>::fromQList(qlist); + return in; +} + +#endif // QMF_QMFLIST_H + diff --git a/tests/tests.pro b/tests/tests.pro index cac44948..da454ccc 100644 --- a/tests/tests.pro +++ b/tests/tests.pro @@ -21,7 +21,8 @@ SUBDIRS = \ tst_qmaildisconnected \ tst_qmailnamespace \ tst_locks \ - tst_qmailthread + tst_qmailthread \ + tst_qmflist exists(/usr/bin/gpgme-config) { SUBDIRS += tst_crypto diff --git a/tests/tst_qmflist/tst_qmflist.cpp b/tests/tst_qmflist/tst_qmflist.cpp new file mode 100644 index 00000000..85401c7f --- /dev/null +++ b/tests/tst_qmflist/tst_qmflist.cpp @@ -0,0 +1,297 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Messaging Framework. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** 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 http://www.qt.io/terms-conditions. For further +** information use the contact form at http://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 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** As a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qmflist.h> + +#include <QObject> +#include <QTest> + +//TESTED_CLASS=QmfList +//TESTED_FILES=src/libraries/qmfclient/qmflist.h + +class tst_QmfList : public QObject +{ + Q_OBJECT + +public: + tst_QmfList(); + virtual ~tst_QmfList(); + +private slots: + virtual void initTestCase(); + virtual void cleanupTestCase(); + virtual void init(); + virtual void cleanup(); + + void count(); + void isEmpty(); + void append(); + void append_list(); + void last(); + void first(); + void takeFirst(); + void removeAt(); + void removeOne(); + void removeAll(); + void at(); + void subscript(); + void subscript_const(); + void contains(); + void indexOf(); + + void toQList(); + void fromQList(); +}; + +QTEST_MAIN(tst_QmfList) +#include "tst_qmflist.moc" + +tst_QmfList::tst_QmfList() +{ +} + +tst_QmfList::~tst_QmfList() +{ +} + +void tst_QmfList::initTestCase() +{ +} + +void tst_QmfList::cleanupTestCase() +{ +} + +void tst_QmfList::init() +{ +} + +void tst_QmfList::cleanup() +{ +} + +void tst_QmfList::count() +{ + QmfList<int> list { 5, 6, 7, 8 }; + QCOMPARE(list.count(), 4); + QCOMPARE(list.count(), static_cast<qsizetype>(list.size())); + list.pop_back(); + QCOMPARE(list.count(), 3); + QCOMPARE(list.count(), static_cast<qsizetype>(list.size())); +} + +void tst_QmfList::isEmpty() +{ + QmfList<int> list { 5 }; + QCOMPARE(list.isEmpty(), false); + list.pop_front(); + QCOMPARE(list.isEmpty(), true); + list.push_back(6); + QCOMPARE(list.isEmpty(), false); +} + +void tst_QmfList::append() +{ + QmfList<int> list { 5 }; + list.append(6); + QCOMPARE(list.count(), 2); + QCOMPARE(list.at(0), 5); + QCOMPARE(list.at(1), 6); +} + +void tst_QmfList::append_list() +{ + QmfList<int> list { 5 }; + const QmfList<int> append_list { 6, 7 }; + list.append(append_list); + QCOMPARE(list.count(), 3); + QCOMPARE(list.at(0), 5); + QCOMPARE(list.at(1), 6); + QCOMPARE(list.at(2), 7); +} + +void tst_QmfList::last() +{ + QmfList<int> list { 5, 6, 7 }; + QCOMPARE(list.last(), 7); + list.pop_back(); + QCOMPARE(list.last(), 6); +} + +void tst_QmfList::first() +{ + QmfList<int> list { 5, 6, 7 }; + QCOMPARE(list.first(), 5); + list.pop_front(); + QCOMPARE(list.first(), 6); +} + +void tst_QmfList::takeFirst() +{ + QmfList<int> list { 5, 6, 7 }; + QCOMPARE(list.count(), 3); + QCOMPARE(list.at(0), 5); + QCOMPARE(list.at(1), 6); + QCOMPARE(list.at(2), 7); + int first = list.takeFirst(); + QCOMPARE(first, 5); + QCOMPARE(list.count(), 2); + QCOMPARE(list.at(0), 6); + QCOMPARE(list.at(1), 7); +} + +void tst_QmfList::removeAt() +{ + QmfList<int> list { 5, 6, 7, 5 }; + QCOMPARE(list.front(), 5); + QCOMPARE(list.back(), 5); + QCOMPARE(list.count(), 4); + list.removeAt(0); + QCOMPARE(list.front(), 6); + QCOMPARE(list.back(), 5); + QCOMPARE(list.count(), 3); + list.removeAt(2); + QCOMPARE(list.front(), 6); + QCOMPARE(list.back(), 7); + QCOMPARE(list.count(), 2); +} + +void tst_QmfList::removeOne() +{ + QmfList<int> list { 5, 6, 7, 5 }; + QCOMPARE(list.front(), 5); + QCOMPARE(list.back(), 5); + QCOMPARE(list.count(), 4); + list.removeOne(5); + QCOMPARE(list.front(), 6); + QCOMPARE(list.back(), 5); + QCOMPARE(list.count(), 3); + list.removeOne(5); + QCOMPARE(list.front(), 6); + QCOMPARE(list.back(), 7); + QCOMPARE(list.count(), 2); + list.removeOne(5); + QCOMPARE(list.front(), 6); + QCOMPARE(list.back(), 7); + QCOMPARE(list.count(), 2); +} + +void tst_QmfList::removeAll() +{ + QmfList<int> list { 5, 6, 7, 5 }; + QCOMPARE(list.front(), 5); + QCOMPARE(list.back(), 5); + QCOMPARE(list.count(), 4); + list.removeAll(5); + QCOMPARE(list.front(), 6); + QCOMPARE(list.back(), 7); + QCOMPARE(list.count(), 2); + list.removeAll(5); + QCOMPARE(list.front(), 6); + QCOMPARE(list.back(), 7); + QCOMPARE(list.count(), 2); +} + +void tst_QmfList::at() +{ + const QmfList<int> list { 5, 6, 7, 8 }; + QCOMPARE(list.at(0), 5); + QCOMPARE(list.at(2), 7); +} + +void tst_QmfList::subscript() +{ + QmfList<int> list { 5, 6, 7, 8 }; + list[0] = 55; + list[2] = 77; + QCOMPARE(list.at(0), 55); + QCOMPARE(list.at(2), 77); + + list.push_back(9); + QCOMPARE(list[4], 9); + list[4] = 99; + QCOMPARE(list[4], 99); +} + +void tst_QmfList::subscript_const() +{ + const QmfList<int> list { 5, 6, 7, 8 }; + QCOMPARE(list.at(0), list[0]); + QCOMPARE(list.at(2), list[2]); +} + +void tst_QmfList::contains() +{ + QmfList<int> list { 5, 6, 7, 8 }; + QCOMPARE(list.contains(5), true); + QCOMPARE(list.contains(9), false); + list.append(9); + QCOMPARE(list.contains(9), true); +} + +void tst_QmfList::indexOf() +{ + QmfList<int> list { 5, 6, 7, 8 }; + QCOMPARE(list.indexOf(6), 1); + QCOMPARE(list.indexOf(3), -1); + QCOMPARE(list.indexOf(5), 0); + list.append(2); + QCOMPARE(list.indexOf(2), 4); + list.pop_front(); + QCOMPARE(list.indexOf(5), -1); +} + +void tst_QmfList::toQList() +{ + QmfList<int> qmflist { 5, 6, 7, 8 }; + QList<int> qlist { 5, 6, 7, 8 }; + + QCOMPARE(qmflist.toQList(), qlist); + qmflist.takeFirst(); + QVERIFY(qmflist.toQList() != qlist); + qlist.takeFirst(); + QCOMPARE(qmflist.toQList(), qlist); +} + +void tst_QmfList::fromQList() +{ + QmfList<int> qmflist { 5, 6, 7, 8 }; + QList<int> qlist { 5, 6, 7, 8 }; + + QCOMPARE(QmfList<int>::fromQList(qlist), qmflist); + qlist.takeFirst(); + QVERIFY(QmfList<int>::fromQList(qlist) != qmflist); + qmflist.takeFirst(); + QCOMPARE(QmfList<int>::fromQList(qlist), qmflist); +} + diff --git a/tests/tst_qmflist/tst_qmflist.pro b/tests/tst_qmflist/tst_qmflist.pro new file mode 100644 index 00000000..2c43e0cf --- /dev/null +++ b/tests/tst_qmflist/tst_qmflist.pro @@ -0,0 +1,7 @@ +TEMPLATE = app +TARGET = tst_qmflist +CONFIG += qmfclient + +SOURCES += tst_qmflist.cpp + +include(../tests.pri) |
