From fcb6cf1a521b60e388860f2a1aa42d630ebb9779 Mon Sep 17 00:00:00 2001 From: Mathew Sutcliffe Date: Thu, 23 Nov 2017 22:15:23 +0000 Subject: [PATCH] Removed type erasure in containers Summary: Refs T196 Using QVector as this is Qt's recommended container type. Reviewers: #swift_developers, rwinklmeier Reviewed By: #swift_developers, rwinklmeier Subscribers: rwinklmeier, jenkins Tags: #swift_pilot_client Maniphest Tasks: T196 Differential Revision: https://dev.swift-project.org/D61 --- samples/blackmisc/main.cpp | 24 +- samples/blackmisc/samplesperformance.cpp | 95 ---- samples/blackmisc/samplesperformance.h | 3 - src/blackmisc/collection.h | 194 ++------- src/blackmisc/iterator.h | 504 ---------------------- src/blackmisc/propertyindexvariantmap.cpp | 2 +- src/blackmisc/sequence.h | 264 +++--------- tests/blackmisc/testcontainers.cpp | 16 +- 8 files changed, 111 insertions(+), 991 deletions(-) diff --git a/samples/blackmisc/main.cpp b/samples/blackmisc/main.cpp index 844e403cd..9cd9c2157 100644 --- a/samples/blackmisc/main.cpp +++ b/samples/blackmisc/main.cpp @@ -48,13 +48,11 @@ int main(int argc, char *argv[]) qtout << "3 .. Containers" << endl; qtout << "4 .. Metadata" << endl; qtout << "6a .. Performance create / copy / ..." << endl; - qtout << "6b .. 25/100 Performance impl. type" << endl; - qtout << "6c .. 25/20 Performance impl. type" << endl; - qtout << "6d .. 40/20 Interpolator scenario" << endl; - qtout << "6e .. JSON performance" << endl; - qtout << "6f .. JSON model performance (database vs. own JSON)" << endl; - qtout << "6g .. string utils vs.regex" << endl; - qtout << "6h .. string concatenation (+=, arg, ..)" << endl; + qtout << "6b .. 40/20 Interpolator scenario" << endl; + qtout << "6c .. JSON performance" << endl; + qtout << "6d .. JSON model performance (database vs. own JSON)" << endl; + qtout << "6e .. string utils vs.regex" << endl; + qtout << "6f .. string concatenation (+=, arg, ..)" << endl; qtout << "7 .. Algorithms" << endl; qtout << "-----" << endl; qtout << "x .. Bye" << endl; @@ -65,13 +63,11 @@ int main(int argc, char *argv[]) else if (s.startsWith("3")) { CSamplesContainer::samples(); } else if (s.startsWith("4")) { CSamplesMetadata::samples(); } else if (s.startsWith("6a")) { CSamplesPerformance::samplesMisc(qtout); } - else if (s.startsWith("6b")) { CSamplesPerformance::samplesImplementationType(qtout, 25, 100); } - else if (s.startsWith("6c")) { CSamplesPerformance::samplesImplementationType(qtout, 25, 20); } - else if (s.startsWith("6d")) { CSamplesPerformance::interpolatorScenario(qtout, 40, 20); } - else if (s.startsWith("6e")) { CSamplesPerformance::samplesJson(qtout); } - else if (s.startsWith("6f")) { CSamplesPerformance::samplesJsonModel(qtout); } - else if (s.startsWith("6g")) { CSamplesPerformance::samplesStringUtilsVsRegEx(qtout); } - else if (s.startsWith("6h")) { CSamplesPerformance::samplesStringConcat(qtout); } + else if (s.startsWith("6b")) { CSamplesPerformance::interpolatorScenario(qtout, 40, 20); } + else if (s.startsWith("6c")) { CSamplesPerformance::samplesJson(qtout); } + else if (s.startsWith("6d")) { CSamplesPerformance::samplesJsonModel(qtout); } + else if (s.startsWith("6e")) { CSamplesPerformance::samplesStringUtilsVsRegEx(qtout); } + else if (s.startsWith("6f")) { CSamplesPerformance::samplesStringConcat(qtout); } else if (s.startsWith("7")) { CSamplesAlgorithm::samples(); } else if (s.startsWith("x")) { break; } } diff --git a/samples/blackmisc/samplesperformance.cpp b/samples/blackmisc/samplesperformance.cpp index 92522ebac..c0c9e78e0 100644 --- a/samples/blackmisc/samplesperformance.cpp +++ b/samples/blackmisc/samplesperformance.cpp @@ -184,101 +184,6 @@ namespace BlackSample return 0; } - int CSamplesPerformance::samplesImplementationType(QTextStream &out, int numberOfCallsigns, int numberOfTimes) - { - const qint64 baseTimeEpoch = QDateTime::currentMSecsSinceEpoch(); - CAircraftSituationList situations = createSituations(baseTimeEpoch, numberOfCallsigns, numberOfTimes); - - QTime timer; - out << "Created " << situations.size() << " situations" << endl; - - timer.start(); - for (int i = 0; i < 10; i++) - { - for (int cs = 0; cs < numberOfCallsigns; cs++) - { - CCallsign callsign("CS" + QString::number(cs)); - CAircraftSituationList r = situations.findByCallsign(callsign); - Q_ASSERT(r.size() == numberOfTimes); - } - } - out << "Reads by callsigns: " << timer.elapsed() << "ms" << endl; - - timer.start(); - for (int i = 0; i < 10; i++) - { - for (int t = 0; t < numberOfTimes; t++) - { - CAircraftSituationList r = situations.findBefore(baseTimeEpoch + 1 + (DeltaTime * t)); - Q_ASSERT(r.size() == numberOfCallsigns * (t + 1)); - } - } - out << "Reads by times: " << timer.elapsed() << "ms" << endl; - - timer.start(); - for (int t = 0; t < numberOfTimes; t++) - { - for (int cs = 0; cs < numberOfCallsigns; cs++) - { - CCallsign callsign("CS" + QString::number(cs)); - CAircraftSituationList r = situations.findByCallsign(callsign).findBefore(baseTimeEpoch + 1 + (DeltaTime * t)); - Q_UNUSED(r); - } - } - out << "Reads by callsigns / times: " << timer.elapsed() << "ms" << endl; - - timer.start(); - for (int t = 0; t < numberOfTimes; t++) - { - for (int cs = 0; cs < numberOfCallsigns; cs++) - { - CCallsign callsign("CS" + QString::number(cs)); - CAircraftSituationList r = situations.findBefore(baseTimeEpoch + 1 + (DeltaTime * t)).findByCallsign(callsign); - Q_UNUSED(r); - } - } - out << "Reads by times / callsigns: " << timer.elapsed() << "ms" << endl; - - timer.start(); - const QHash splitList = situations.splitPerCallsign(); - Q_ASSERT(splitList.size() == numberOfCallsigns); - for (int t = 0; t < numberOfTimes; t++) - { - for (const CAircraftSituationList &slcs : splitList) - { - CAircraftSituationList r = slcs.findBefore(baseTimeEpoch + 1 + (DeltaTime * t)); - Q_UNUSED(r); - } - } - out << "Split reads by callsigns / times: " << timer.elapsed() << "ms" << endl; - - situations.changeImpl >(); - out << "Changed to QVector" << endl; - timer.start(); - for (int i = 0; i < 10; i++) - { - for (int cs = 0; cs < numberOfCallsigns; cs++) - { - CCallsign callsign("CS" + QString::number(cs)); - CAircraftSituationList r = situations.findByCallsign(callsign); - Q_ASSERT(r.size() == numberOfTimes); - } - } - out << "Reads by callsigns: " << timer.elapsed() << "ms" << endl; - - timer.start(); - for (int i = 0; i < 10; i++) - { - for (int t = 0; t < numberOfTimes; t++) - { - CAircraftSituationList r = situations.findBefore(baseTimeEpoch + 1 + (DeltaTime * t)); - Q_ASSERT(r.size() == numberOfCallsigns * (t + 1)); - } - } - out << "Reads by times: " << timer.elapsed() << "ms" << endl << endl; - return 0; - } - int CSamplesPerformance::interpolatorScenario(QTextStream &out, int numberOfCallsigns, int numberOfTimes) { const qint64 baseTimeEpoch = QDateTime::currentMSecsSinceEpoch(); diff --git a/samples/blackmisc/samplesperformance.h b/samples/blackmisc/samplesperformance.h index e1e91623b..3d05bed9d 100644 --- a/samples/blackmisc/samplesperformance.h +++ b/samples/blackmisc/samplesperformance.h @@ -29,9 +29,6 @@ namespace BlackSample //! Copy, create, RegEx static int samplesMisc(QTextStream &out); - //! Impact of implementation type - static int samplesImplementationType(QTextStream &out, int numberOfCallsigns, int numberOfTimes); - //! Interpolator scenario static int interpolatorScenario(QTextStream &out, int numberOfCallsigns, int numberOfTimes); diff --git a/src/blackmisc/collection.h b/src/blackmisc/collection.h index b066a0669..14dae1425 100644 --- a/src/blackmisc/collection.h +++ b/src/blackmisc/collection.h @@ -12,11 +12,9 @@ #ifndef BLACKMISC_COLLECTION_H #define BLACKMISC_COLLECTION_H -#include "iterator.h" #include "containerbase.h" #include "icon.h" - -#include +#include #include #include #include @@ -24,15 +22,6 @@ #include #include -// conditions matched with pop pragmas at bottom of file -#if defined(QT_CC_CLANG) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wshorten-64-to-32" -#elif defined(Q_CC_MSVC) && defined(Q_OS_WIN64) && defined(QT_CC_WARNINGS) -#pragma warning(push) -#pragma warning(disable:4244) -#endif - namespace BlackMisc { @@ -60,10 +49,8 @@ namespace BlackMisc }; /*! - * Generic type-erased ordered container with value semantics. + * Generic ordered container with value semantics. * \tparam T the type of elements contained. - * - * Can take any suitable container class as its implementation at runtime. */ template class CCollection : @@ -79,138 +66,113 @@ namespace BlackMisc typedef const T &const_reference; typedef T *pointer; typedef const T *const_pointer; - typedef typename Iterators::ConstForwardIterator const_iterator; + typedef typename QOrderedSet::const_iterator const_iterator; typedef const_iterator iterator; // can't modify elements in-place typedef ptrdiff_t difference_type; typedef int size_type; //! @} //! Default constructor. - CCollection() : m_pimpl(new Pimpl>(QOrderedSet())) {} + CCollection() {} //! Initializer list constructor. - CCollection(std::initializer_list il) : m_pimpl(new Pimpl>(QOrderedSet(il))) {} + CCollection(std::initializer_list il) : m_impl(il) {} //! Copy constructor. - CCollection(const CCollection &other) : m_pimpl(other.pimpl() ? other.pimpl()->clone() : nullptr) {} + CCollection(const CCollection &other) = default; //! Constructor from QList. - CCollection(const QList &list) : m_pimpl(new Pimpl>(QOrderedSet(list))) {} + CCollection(const QList &list) : m_impl(list) {} //! Move constructor. - CCollection(CCollection &&other) noexcept(std::is_nothrow_move_constructible::value) : m_pimpl(other.m_pimpl.take()) {} + CCollection(CCollection &&other) = default; //! Copy assignment. - CCollection &operator =(const CCollection &other) { m_pimpl.reset(other.pimpl() ? other.pimpl()->clone() : nullptr); return *this; } + CCollection &operator =(const CCollection &other) = default; //! Move assignment. - CCollection &operator =(CCollection && other) noexcept(std::is_nothrow_move_assignable::value) { m_pimpl.reset(other.m_pimpl.take()); return *this; } + CCollection &operator =(CCollection &&other) = default; //! Destructor. ~CCollection() = default; - //! Create a new collection with a specific implementation type. - //! \tparam C Becomes the collection's implementation type. - //! \param c Initial value for the collection; default is empty, but it could contain elements if desired. The value is copied. - template static CCollection fromImpl(C c = C()) { return CCollection(new Pimpl(std::move(c))); } - - //! Change the implementation type but keep all the same elements, by moving them into the new implementation. - //! \tparam C Becomes the collection's new implementation type. - template void changeImpl(C = C()) { auto c = fromImpl(C()); std::move(begin(), end(), std::inserter(c, c.begin())); *this = std::move(c); } - - //! Like changeImpl, but uses the implementation type of another collection. - //! \pre The other collection must be initialized. - void useImplOf(const CCollection &other) { CCollection c(other.pimpl()->cloneEmpty()); std::move(begin(), end(), std::inserter(c, c.begin())); *this = std::move(c); } + //! Returns iterator at the beginning of the collection. + iterator begin() { return m_impl.begin(); } //! Returns iterator at the beginning of the collection. - iterator begin() { return pimpl() ? pimpl()->begin() : iterator(); } + const_iterator begin() const { return m_impl.begin(); } //! Returns iterator at the beginning of the collection. - const_iterator begin() const { return pimpl() ? pimpl()->begin() : const_iterator(); } - - //! Returns iterator at the beginning of the collection. - const_iterator cbegin() const { return pimpl() ? pimpl()->cbegin() : const_iterator(); } + const_iterator cbegin() const { return m_impl.cbegin(); } //! Returns iterator one past the end of the collection. - iterator end() { return pimpl() ? pimpl()->end() : iterator(); } + iterator end() { return m_impl.end(); } //! Returns const iterator one past the end of the collection. - const_iterator end() const { return pimpl() ? pimpl()->end() : const_iterator(); } + const_iterator end() const { return m_impl.end(); } //! Returns const iterator one past the end of the collection. - const_iterator cend() const { return pimpl() ? pimpl()->cend() : const_iterator(); } + const_iterator cend() const { return m_impl.cend(); } //! Swap this collection with another. - void swap(CCollection &other) noexcept { m_pimpl.swap(other.m_pimpl); } + void swap(CCollection &other) noexcept { m_impl.swap(other.m_impl); } //! Returns number of elements in the collection. - size_type size() const { return pimpl() ? pimpl()->size() : 0; } + size_type size() const { return m_impl.size(); } //! Returns true if the collection is empty. - bool empty() const { return pimpl() ? pimpl()->empty() : true; } + bool empty() const { return m_impl.isEmpty(); } //! Synonym for empty. bool isEmpty() const { return empty(); } //! Removes all elements in the collection. - void clear() { if (pimpl()) pimpl()->clear(); } + void clear() { m_impl.clear(); } //! For compatibility with std::inserter. //! \param hint Ignored. //! \param value The value to insert. - //! \pre The collection must be initialized. iterator insert(const_iterator hint, const T &value) { Q_UNUSED(hint); return insert(value); } //! For compatibility with std::inserter. //! \param hint Ignored. //! \param value The value to move in. - //! \pre The collection must be initialized. iterator insert(const_iterator hint, T &&value) { Q_UNUSED(hint); return insert(std::move(value)); } //! Inserts an element into the collection. //! \return An iterator to the position where value was inserted. - //! \pre The collection must be initialized. - iterator insert(const T &value) { Q_ASSERT(pimpl()); return pimpl()->insert(value); } + iterator insert(const T &value) { return m_impl.insert(value); } //! Moves an element into the collection. //! \return An iterator to the position where value was inserted. - //! \pre The collection must be initialized. - iterator insert(T &&value) { Q_ASSERT(pimpl()); return pimpl()->insert(std::move(value)); } + iterator insert(T &&value) { return m_impl.insert(std::move(value)); } //! Inserts all elements from another collection into this collection. - //! \pre This collection must be initialized. void insert(const CCollection &other) { std::copy(other.begin(), other.end(), std::inserter(*this, begin())); } //! Inserts all elements from another collection into this collection. //! This version moves elements instead of copying. - //! \pre This collection must be initialized. void insert(CCollection &&other) { std::move(other.begin(), other.end(), std::inserter(*this, begin())); } //! Appends all elements from a range at the end of this collection. - //! \pre This collection must be initialized. template void insert(const CRange &range) { std::copy(range.begin(), range.end(), std::back_inserter(*this)); } //! Synonym for insert. //! \return An iterator to the position where value was inserted. - //! \pre The collection must be initialized. iterator push_back(const T &value) { return insert(value); } //! Synonym for insert. //! \return An iterator to the position where value was inserted. - //! \pre The collection must be initialized. iterator push_back(T &&value) { return insert(std::move(value)); } //! Synonym for insert. - //! \pre This collection must be initialized. void push_back(const CCollection &other) { insert(other); } //! Synonym for insert. - //! \pre This collection must be initialized. void push_back(CCollection &&other) { insert(std::move(other)); } //! Synonym for insert. - //! \pre This collection must be initialized. template void push_back(const CRange &range) { std::copy(range.begin(), range.end(), std::back_inserter(*this)); } @@ -243,24 +205,25 @@ namespace BlackMisc //! Remove the element pointed to by the given iterator. //! \return An iterator to the position of the next element after the one removed. - //! \pre The collection must be initialized. - iterator erase(iterator pos) { Q_ASSERT(pimpl()); return pimpl()->erase(pos); } + //! \fixme Relying on implementation detail of QMap to reinterpret_cast to the necessary iterator type. + iterator erase(iterator pos) { return m_impl.erase(reinterpret_cast *&>(pos)); } //! Remove the range of elements between two iterators. //! \return An iterator to the position of the next element after the one removed. - //! \pre The sequence must be initialized. - iterator erase(iterator it1, iterator it2) { Q_ASSERT(pimpl()); return pimpl()->erase(it1, it2); } + //! \fixme Relying on implementation detail of QMap to reinterpret_cast to the necessary iterator type. + iterator erase(iterator it1, iterator it2) + { + while (it1 != it2) { it1 = m_impl.erase(reinterpret_cast *&>(it1)); } + return it1; + } //! Efficient find method using the find of the implementation container. Typically O(log n). //! \return An iterator to the position of the found element, or the end iterator if not found. - //! \pre The sequence must be initialized. - //! \warning Take care that the returned non-const iterator is not compared with a const iterator. - iterator find(const T &value) { Q_ASSERT(pimpl()); return pimpl()->find(value); } + iterator find(const T &value) { return m_impl.find(value); } //! Efficient find method using the find of the implementation container. Typically O(log n). //! \return An iterator to the position of the found element, or the end iterator if not found. - //! \pre The sequence must be initialized. - const_iterator find(const T &value) const { Q_ASSERT(pimpl()); return pimpl()->find(value); } + const_iterator find(const T &value) const { return m_impl.find(value); } //! Efficient remove using the find and erase of the implementation container. Typically O(log n). //! \pre The sequence must be initialized. @@ -300,91 +263,13 @@ namespace BlackMisc } //! Test for equality. - bool operator ==(const CCollection &other) const { return *pimpl() == *other.pimpl(); } + bool operator ==(const CCollection &other) const { return m_impl == other.m_impl; } //! Test for inequality. - bool operator !=(const CCollection &other) const { return !(*this == other); } - - //! Return an opaque pointer to the implementation container. - //! \details Can be useful in unusual debugging situations. - //! \warning Not for general use. - void *getImpl() { return pimpl() ? pimpl()->impl() : nullptr; } + bool operator !=(const CCollection &other) const { return m_impl != other.m_impl; } private: - class PimplBase - { - public: - PimplBase() {} - PimplBase(const PimplBase &) = default; - PimplBase &operator =(const PimplBase &) = delete; - virtual ~PimplBase() {} - virtual PimplBase *clone() const = 0; - virtual PimplBase *cloneEmpty() const = 0; - virtual iterator begin() = 0; - virtual const_iterator begin() const = 0; - virtual const_iterator cbegin() const = 0; - virtual iterator end() = 0; - virtual const_iterator end() const = 0; - virtual const_iterator cend() const = 0; - virtual size_type size() const = 0; - virtual bool empty() const = 0; - virtual void clear() = 0; - virtual iterator insert(const T &value) = 0; - virtual iterator insert(T &&value) = 0; - virtual iterator erase(iterator pos) = 0; - virtual iterator erase(iterator it1, iterator it2) = 0; - virtual iterator find(const T &value) = 0; - virtual const_iterator find(const T &value) const = 0; - virtual bool operator ==(const PimplBase &other) const = 0; - virtual void *impl() = 0; - virtual const void *impl() const = 0; - virtual std::type_index implType() const = 0; - }; - - template class Pimpl : public PimplBase - { - public: - static_assert(std::is_same::value, "CCollection must be initialized from a container with the same value_type."); - Pimpl(C &&c) : m_impl(std::move(c)) {} - PimplBase *clone() const override { return new Pimpl(*this); } - PimplBase *cloneEmpty() const override { return new Pimpl(C()); } - iterator begin() override { return iterator::fromImpl(m_impl.begin()); } - const_iterator begin() const override { return const_iterator::fromImpl(m_impl.cbegin()); } - const_iterator cbegin() const override { return const_iterator::fromImpl(m_impl.cbegin()); } - iterator end() override { return iterator::fromImpl(m_impl.end()); } - const_iterator end() const override { return const_iterator::fromImpl(m_impl.cend()); } - const_iterator cend() const override { return const_iterator::fromImpl(m_impl.cend()); } - size_type size() const override { return static_cast(m_impl.size()); } - bool empty() const override { return m_impl.empty(); } - void clear() override { m_impl.clear(); } - iterator insert(const T &value) override { return iterator::fromImpl(insertHelper(m_impl.insert(value))); } - iterator insert(T &&value) override { return iterator::fromImpl(insertHelper(m_impl.insert(std::move(value)))); } - iterator erase(iterator pos) override { return iterator::fromImpl(m_impl.erase(pos.template getImpl())); } - //iterator erase(iterator it1, iterator it2) override { return iterator::fromImpl(m_impl.erase(it1.template getImpl(), it2.template getImpl())); } - iterator erase(iterator it1, iterator it2) override { while (it1 != it2) { it1 = iterator::fromImpl(m_impl.erase(it1.template getImpl())); } return it1; } - iterator find(const T &value) override { return iterator::fromImpl(m_impl.find(value)); } - const_iterator find(const T &value) const override { return const_iterator::fromImpl(m_impl.find(value)); } - bool operator ==(const PimplBase &other) const override { return implType() == other.implType() ? m_impl == *static_cast(other.impl()) : size() == other.size() && std::equal(begin(), end(), other.begin()); } - void *impl() override { return &m_impl; } - const void *impl() const override { return &m_impl; } - std::type_index implType() const override { return typeid(C); } - private: - C m_impl; - bool implEquals(const PimplBase &other) const { return m_impl == *static_cast(other.impl()); } - bool equals(const PimplBase &other) const { return size() == other.size() && std::equal(begin(), end(), other.begin()); } - // insertHelper: QOrderedSet::insert returns an iterator, but std::set::insert returns a std::pair - template static I insertHelper(I i) { return i; } - template static I insertHelper(std::pair p) { return p.first; } - }; - - using PimplPtr = QScopedPointer; - PimplPtr m_pimpl; - - CCollection(PimplBase *pimpl) : m_pimpl(pimpl) {} // private ctor used by fromImpl() - - // using these methods to access m_pimpl.data() eases the cognitive burden of correctly forwarding const - PimplBase *pimpl() { return m_pimpl.data(); } - const PimplBase *pimpl() const { return m_pimpl.data(); } + QOrderedSet m_impl; }; } //namespace BlackMisc @@ -395,11 +280,4 @@ Q_DECLARE_METATYPE(BlackMisc::CCollection) Q_DECLARE_METATYPE(BlackMisc::CCollection) // CCollection not instantiated due to it being a dumb idea because of rounding issues -// conditions matched with pop pragmas at bottom of file -#if defined(QT_CC_CLANG) -#pragma clang diagnostic pop -#elif defined(Q_CC_MSVC) && defined(Q_OS_WIN64) && defined(QT_CC_WARNINGS) -#pragma warning(pop) -#endif - #endif // guard diff --git a/src/blackmisc/iterator.h b/src/blackmisc/iterator.h index 859242fe0..18bd9914e 100644 --- a/src/blackmisc/iterator.h +++ b/src/blackmisc/iterator.h @@ -14,7 +14,6 @@ #include "optional.h" #include "typetraits.h" -#include #include #include #include @@ -357,509 +356,6 @@ namespace BlackMisc return { std::move(iterators) }; } - /*! - * Generic type-erased const forward iterator with value semantics. - * \tparam T the value_type of the container being iterated over. - * - * Can take any suitable iterator type as its implementation at runtime. - */ - template class ConstForwardIterator - { - public: - //! STL compatibility - //! @{ - typedef ptrdiff_t difference_type; - typedef T value_type; - typedef const T *pointer; - typedef const T &reference; - typedef const T *const_pointer; - typedef const T &const_reference; - typedef std::forward_iterator_tag iterator_category; - //! @} - - //! Default constructor. - ConstForwardIterator() {} - - //! Copy constructor. - ConstForwardIterator(const ConstForwardIterator &other) : m_pimpl(other.pimpl() ? other.pimpl()->clone() : nullptr) {} - - //! Move constructor. - ConstForwardIterator(ConstForwardIterator &&other) noexcept : m_pimpl(other.m_pimpl.take()) {} - - //! Copy assignment. - ConstForwardIterator &operator =(const ConstForwardIterator &other) { m_pimpl.reset(other.pimpl() ? other.pimpl()->clone() : nullptr); return *this; } - - //! Move assignment. - ConstForwardIterator &operator =(ConstForwardIterator &&other) noexcept { m_pimpl.reset(other.m_pimpl.take()); return *this; } - - //! Destructor. - ~ConstForwardIterator() = default; - - //! Create a new iterator with a specific implementation type. - //! \tparam I Becomes the iterator's implementation type. - //! \param i Initial value for the iterator. The value is copied. - template static ConstForwardIterator fromImpl(I i) { return ConstForwardIterator(new Pimpl(std::move(i))); } - - //! Returns a reference to the object pointed to. - //! \pre The iterator must be initialized and valid. - const_reference operator *() const { Q_ASSERT(m_pimpl); return **pimpl(); } - - //! Arrow operator provides access to members of the object pointed to. - //! \pre The iterator must be initialized and valid. - const_pointer operator ->() const { Q_ASSERT(m_pimpl); return &**pimpl(); } - - //! Prefix increment operator advances the iterator. - //! \return Reference to the iterator at the new position. - //! \pre The iterator must be initialized and valid. - ConstForwardIterator &operator ++() { Q_ASSERT(m_pimpl); ++*pimpl(); return *this; } - - //! Postfix increment operator advances the iterator. - //! \return Copy of the iterator in the old position. - //! \pre The iterator must be initialized and valid. - ConstForwardIterator operator ++(int) { Q_ASSERT(m_pimpl); auto copy = *this; ++*pimpl(); return copy; } - - //! Advance the iterator by a certain amount. - //! \return Reference to the iterator at the new position. - //! \pre The iterator must be initialized and valid. - ConstForwardIterator operator +=(difference_type n) { Q_ASSERT(m_pimpl); *pimpl() += n; return *this; } - - //! Advance the iterator by a certain amount. - //! \return Copy of the iterator in its new position. - //! \pre The iterator must be initialized and valid. - ConstForwardIterator operator +(difference_type n) const { auto copy = *this; return copy += n; } - - //! Test for equality. - //! \pre Both iterators must originate from the same collection, and not mix begin/end with cbegin/cend. - bool operator ==(const ConstForwardIterator &other) const { return (pimpl() && other.pimpl()) ? *pimpl() == *other.pimpl() : pimpl() == other.pimpl(); } - - //! Test for inequality. - //! \pre Both iterators must originate from the same collection, and not mix begin/end with cbegin/cend. - bool operator !=(const ConstForwardIterator &other) const { return !(*this == other); } - - //! Return opaque pointer to underlying implementation iterator object. - //! \pre The iterator must have been initialized. - template U &getImpl() { pimpl()->assertType(typeid(std::decay_t)); return *static_cast(pimpl()->impl()); } - - private: - class PimplBase - { - public: - PimplBase() {} - PimplBase(const PimplBase &) = default; - PimplBase &operator =(const PimplBase &) = delete; - virtual ~PimplBase() {} - virtual PimplBase *clone() const = 0; - virtual const_reference operator *() const = 0; - virtual void operator ++() = 0; - virtual void operator +=(difference_type) = 0; - virtual bool operator ==(const PimplBase &) const = 0; - virtual void *impl() = 0; - virtual void assertType(std::type_index) const = 0; - }; - - template class Pimpl : public PimplBase - { - public: - static_assert(std::is_same::value_type>::value, - "ConstForwardIterator must be initialized from an iterator with the same value_type."); - Pimpl(I &&i) : m_impl(std::move(i)) {} - virtual PimplBase *clone() const override { return new Pimpl(*this); } - virtual const_reference operator *() const override { return *m_impl; } - virtual void operator ++() override { ++m_impl; } - virtual void operator +=(difference_type n) override { std::advance(m_impl, n); } - virtual bool operator ==(const PimplBase &other) const override { return m_impl == static_cast(other).m_impl; } - virtual void *impl() override { return &m_impl; } - virtual void assertType(std::type_index ti) const override { Q_ASSERT(ti == typeid(I)); Q_UNUSED(ti); } - private: - I m_impl; - }; - - using PimplPtr = QScopedPointer; - PimplPtr m_pimpl; - - explicit ConstForwardIterator(PimplBase *pimpl) : m_pimpl(pimpl) {} // private ctor used by fromImpl() - - // using these methods to access m_pimpl.data() eases the cognitive burden of correctly forwarding const - PimplBase *pimpl() { return m_pimpl.data(); } - const PimplBase *pimpl() const { return m_pimpl.data(); } - }; - - /*! - * Generic type-erased const random access iterator with value semantics. - * \tparam T the value_type of the container being iterated over. - * - * Can take any suitable iterator type as its implementation at runtime. - */ - template class ConstRandomAccessIterator - { - public: - //! STL compatibility - //! @{ - typedef ptrdiff_t difference_type; - typedef T value_type; - typedef const T *pointer; - typedef const T &reference; - typedef const T *const_pointer; - typedef const T &const_reference; - typedef std::random_access_iterator_tag iterator_category; - //! @} - - //! Default constructor. - ConstRandomAccessIterator() {} - - //! Copy constructor. - ConstRandomAccessIterator(const ConstRandomAccessIterator &other) : m_pimpl(other.pimpl() ? other.pimpl()->clone() : nullptr) {} - - //! Move constructor. - ConstRandomAccessIterator(ConstRandomAccessIterator &&other) noexcept : m_pimpl(other.m_pimpl.take()) {} - - //! Copy assignment. - ConstRandomAccessIterator &operator =(const ConstRandomAccessIterator &other) { m_pimpl.reset(other.pimpl() ? other.pimpl()->clone() : nullptr); return *this; } - - //! Move assignment. - ConstRandomAccessIterator &operator =(ConstRandomAccessIterator &&other) noexcept { m_pimpl.reset(other.m_pimpl.take()); return *this; } - - //! Destructor. - ~ConstRandomAccessIterator() = default; - - //! Create a new iterator with a specific implementation type. - //! \tparam I Becomes the iterator's implementation type. - //! \param i Initial value for the iterator. The value is copied. - template static ConstRandomAccessIterator fromImpl(I i) { return ConstRandomAccessIterator(new Pimpl(std::move(i))); } - - //! Returns a reference to the object pointed to. - //! \pre The iterator must be initialized and valid. - const_reference operator *() const { Q_ASSERT(m_pimpl); return **pimpl(); } - - //! Arrow operator provides access to members of the object pointed to. - //! \pre The iterator must be initialized and valid. - const_pointer operator ->() const { Q_ASSERT(m_pimpl); return &**pimpl(); } - - //! Prefix increment operator advances the iterator. - //! \return Reference to the iterator at the new position. - //! \pre The iterator must be initialized and valid. - ConstRandomAccessIterator &operator ++() { Q_ASSERT(m_pimpl); ++*pimpl(); return *this; } - - //! Postfix increment operator advances the iterator. - //! \return Copy of the iterator in the old position. - //! \pre The iterator must be initialized and valid. - ConstRandomAccessIterator operator ++(int) { Q_ASSERT(m_pimpl); auto copy = *this; ++*pimpl(); return copy; } - - //! Prefix decrement operator backtracks the iterator. - //! \return Reference to the iterator at the new position. - //! \pre The iterator must be initialized and valid. - ConstRandomAccessIterator &operator --() { Q_ASSERT(m_pimpl); --*pimpl(); return *this; } - - //! Postfix decrement operator backtracks the iterator. - //! \return Copy of the iterator at the old position. - //! \pre The iterator must be initialized and valid. - ConstRandomAccessIterator operator --(int) { Q_ASSERT(m_pimpl); auto copy = *this; --*pimpl(); return copy; } - - //! Advance the iterator by a certain amount. - //! \return Reference to the iterator at the new position. - //! \pre The iterator must be initialized and valid. - ConstRandomAccessIterator operator +=(difference_type n) { Q_ASSERT(m_pimpl); *pimpl() += n; return *this; } - - //! Advance the iterator by a certain amount. - //! \return Copy of the iterator in its new position. - //! \pre The iterator must be initialized and valid. - //! @{ - friend ConstRandomAccessIterator operator +(const ConstRandomAccessIterator &i, difference_type n) { auto copy = i; return copy += n; } - friend ConstRandomAccessIterator operator +(difference_type n, const ConstRandomAccessIterator &i) { auto copy = i; return copy += n; } - //! @} - - //! Backtrack the iterator by a certain amount. - //! \return Reference to the iterator at the new position. - //! \pre The iterator must be initialized and valid. - ConstRandomAccessIterator operator -=(difference_type n) { Q_ASSERT(m_pimpl); *pimpl() -= n; return *this; } - - //! Backtrack the iterator by a certain amount. - //! \return Copy of the iterator in its new position. - //! \pre The iterator must be initialized and valid. - ConstRandomAccessIterator operator -(difference_type n) const { auto copy = *this; return copy -= n; } - - //! Return the distance between two iterators. - //! \pre Both iterators must originate from the same collection, and not mix begin/end with cbegin/cend. - difference_type operator -(const ConstRandomAccessIterator &other) const { Q_ASSERT(m_pimpl && other.m_pimpl); return *pimpl() - *other.pimpl(); } - - //! Test for equality. - //! \pre Both iterators must originate from the same collection, and not mix begin/end with cbegin/cend. - bool operator ==(const ConstRandomAccessIterator &other) const { return (pimpl() && other.pimpl()) ? *pimpl() == *other.pimpl() : pimpl() == other.pimpl(); } - - //! Test for inequality. - //! \pre Both iterators must originate from the same collection, and not mix begin/end with cbegin/cend. - bool operator !=(const ConstRandomAccessIterator &other) const { return !(*this == other); } - - //! For sorting. - //! \pre Both iterators must originate from the same collection, and not mix begin/end with cbegin/cend. - //! @{ - bool operator <(const ConstRandomAccessIterator &other) const { Q_ASSERT(m_pimpl && other.m_pimpl); return *pimpl() < *other.pimpl(); } - bool operator >(const ConstRandomAccessIterator &other) const { Q_ASSERT(m_pimpl && other.m_pimpl); return *pimpl() > *other.pimpl(); } - bool operator <=(const ConstRandomAccessIterator &other) const { Q_ASSERT(m_pimpl && other.m_pimpl); return *pimpl() <= *other.pimpl(); } - bool operator >=(const ConstRandomAccessIterator &other) const { Q_ASSERT(m_pimpl && other.m_pimpl); return *pimpl() >= *other.pimpl(); } - //! @} - - //! Subscript operator. - //! \pre `(*this + n)` must be dereferenceable. - reference operator [](difference_type n) const { return *(*this + n); } - - //! Return opaque pointer to underlying implementation iterator object. - //! \pre The iterator must have been initialized. - template U &getImpl() { pimpl()->assertType(typeid(std::decay_t)); return *static_cast(pimpl()->impl()); } - - private: - class PimplBase - { - public: - PimplBase() {} - PimplBase(const PimplBase &) = default; - PimplBase &operator =(const PimplBase &) = delete; - virtual ~PimplBase() {} - virtual PimplBase *clone() const = 0; - virtual const_reference operator *() const = 0; - virtual void operator ++() = 0; - virtual void operator --() = 0; - virtual void operator +=(difference_type) = 0; - virtual void operator -=(difference_type) = 0; - virtual difference_type operator -(const PimplBase &) const = 0; - virtual bool operator ==(const PimplBase &) const = 0; - virtual bool operator <(const PimplBase &) const = 0; - virtual bool operator >(const PimplBase &) const = 0; - virtual bool operator <=(const PimplBase &) const = 0; - virtual bool operator >=(const PimplBase &) const = 0; - virtual void *impl() = 0; - virtual void assertType(std::type_index) const = 0; - }; - - template class Pimpl : public PimplBase - { - public: - static_assert(std::is_same::value_type>::value, - "ConstRandomAccessIterator must be initialized from an iterator with the same value_type."); - static_assert(std::is_same::iterator_category, std::random_access_iterator_tag>::value, - "ConstRandomAccessIterator must be initialized from a random access iterator."); - Pimpl(I &&i) : m_impl(std::move(i)) {} - virtual PimplBase *clone() const override { return new Pimpl(*this); } - virtual const_reference operator *() const override { return *m_impl; } - virtual void operator ++() override { ++m_impl; } - virtual void operator --() override { --m_impl; } - virtual void operator +=(difference_type n) override { m_impl += n; } - virtual void operator -=(difference_type n) override { m_impl -= n; } - virtual difference_type operator -(const PimplBase &other) const override { return m_impl - static_cast(other).m_impl; } - virtual bool operator ==(const PimplBase &other) const override { return m_impl == static_cast(other).m_impl; } - virtual bool operator <(const PimplBase &other) const override { return m_impl < static_cast(other).m_impl; } - virtual bool operator >(const PimplBase &other) const override { return m_impl > static_cast(other).m_impl; } - virtual bool operator <=(const PimplBase &other) const override { return m_impl <= static_cast(other).m_impl; } - virtual bool operator >=(const PimplBase &other) const override { return m_impl >= static_cast(other).m_impl; } - virtual void *impl() override { return &m_impl; } - virtual void assertType(std::type_index ti) const override { Q_ASSERT(ti == typeid(I)); Q_UNUSED(ti); } - private: - I m_impl; - }; - - using PimplPtr = QScopedPointer; - PimplPtr m_pimpl; - - explicit ConstRandomAccessIterator(PimplBase *pimpl) : m_pimpl(pimpl) {} // private ctor used by fromImpl() - - // using these methods to access m_pimpl.data() eases the cognitive burden of correctly forwarding const - PimplBase *pimpl() { return m_pimpl.data(); } - const PimplBase *pimpl() const { return m_pimpl.data(); } - }; - - /*! - * Generic type-erased non-const bidirectional iterator with value semantics. - * \tparam T the value_type of the container being iterated over. - * - * Can take any suitable iterator type as its implementation at runtime. - */ - template class RandomAccessIterator - { - public: - //! STL compatibility - //! @{ - typedef ptrdiff_t difference_type; - typedef T value_type; - typedef T *pointer; - typedef T &reference; - typedef const T *const_pointer; - typedef const T &const_reference; - typedef std::random_access_iterator_tag iterator_category; - //! @} - - //! Default constructor. - RandomAccessIterator() {} - - //! Copy constructor. - RandomAccessIterator(const RandomAccessIterator &other) : m_pimpl(other.pimpl() ? other.pimpl()->clone() : nullptr) {} - - //! Move constructor. - RandomAccessIterator(RandomAccessIterator &&other) noexcept : m_pimpl(other.m_pimpl.take()) {} - - //! Copy assignment. - RandomAccessIterator &operator =(const RandomAccessIterator &other) { m_pimpl.reset(other.pimpl() ? other.pimpl()->clone() : nullptr); return *this; } - - //! Move assignment. - RandomAccessIterator &operator =(RandomAccessIterator &&other) noexcept { m_pimpl.reset(other.m_pimpl.take()); return *this; } - - //! Destructor. - ~RandomAccessIterator() = default; - - //! Create a new iterator with a specific implementation type. - //! \tparam I Becomes the iterator's implementation type. - //! \param i Initial value for the iterator. The value is copied. - template static RandomAccessIterator fromImpl(I i) { return RandomAccessIterator(new Pimpl(std::move(i))); } - - //! Returns a reference to the object pointed to. - //! \pre The iterator must be initialized and valid. - const_reference operator *() const { Q_ASSERT(m_pimpl); return **pimpl(); } - - //! Returns a reference to the object pointed to. - //! \pre The iterator must be initialized and valid. - reference operator *() { Q_ASSERT(m_pimpl); return **pimpl(); } - - //! Arrow operator provides access to members of the object pointed to. - //! \pre The iterator must be initialized and valid. - const_pointer operator ->() const { Q_ASSERT(m_pimpl); return &**pimpl(); } - - //! Arrow operator provides access to members of the object pointed to. - //! \pre The iterator must be initialized and valid. - pointer operator ->() { Q_ASSERT(m_pimpl); return &**pimpl(); } - - //! Prefix increment operator advances the iterator. - //! \return Reference to the iterator at the new position. - //! \pre The iterator must be initialized and valid. - RandomAccessIterator &operator ++() { Q_ASSERT(m_pimpl); ++*pimpl(); return *this; } - - //! Postfix increment operator advances the iterator. - //! \return Copy of the iterator in the old position. - //! \pre The iterator must be initialized and valid. - RandomAccessIterator operator ++(int) { Q_ASSERT(m_pimpl); auto copy = *this; ++*pimpl(); return copy; } - - //! Prefix decrement operator backtracks the iterator. - //! \return Reference to the iterator at the new position. - //! \pre The iterator must be initialized and valid. - RandomAccessIterator &operator --() { Q_ASSERT(m_pimpl); --*pimpl(); return *this; } - - //! Postfix decrement operator backtracks the iterator. - //! \return Copy of the iterator at the old position. - //! \pre The iterator must be initialized and valid. - RandomAccessIterator operator --(int) { Q_ASSERT(m_pimpl); auto copy = *this; --*pimpl(); return copy; } - - //! Advance the iterator by a certain amount. - //! \return Reference to the iterator at the new position. - //! \pre The iterator must be initialized and valid. - RandomAccessIterator operator +=(difference_type n) { Q_ASSERT(m_pimpl); *pimpl() += n; return *this; } - - //! Advance the iterator by a certain amount. - //! \return Copy of the iterator in its new position. - //! \pre The iterator must be initialized and valid. - //! @{ - friend RandomAccessIterator operator +(const RandomAccessIterator &i, difference_type n) { auto copy = i; return copy += n; } - friend RandomAccessIterator operator +(difference_type n, const RandomAccessIterator &i) { auto copy = i; return copy += n; } - //! @} - - //! Backtrack the iterator by a certain amount. - //! \return Reference to the iterator at the new position. - //! \pre The iterator must be initialized and valid. - RandomAccessIterator operator -=(difference_type n) { Q_ASSERT(m_pimpl); *pimpl() -= n; return *this; } - - //! Backtrack the iterator by a certain amount. - //! \return Copy of the iterator in its new position. - //! \pre The iterator must be initialized and valid. - RandomAccessIterator operator -(difference_type n) const { auto copy = *this; return copy -= n; } - - //! Return the distance between two iterators. - //! \pre Both iterators must originate from the same collection, and not mix begin/end with cbegin/cend. - difference_type operator -(const RandomAccessIterator &other) const { Q_ASSERT(m_pimpl && other.m_pimpl); return *pimpl() - *other.pimpl(); } - - //! Test for equality. - //! \pre Both iterators must originate from the same collection, and not mix begin/end with cbegin/cend. - bool operator ==(const RandomAccessIterator &other) const { return (pimpl() && other.pimpl()) ? *pimpl() == *other.pimpl() : pimpl() == other.pimpl(); } - - //! Test for inequality. - //! \pre Both iterators must originate from the same collection, and not mix begin/end with cbegin/cend. - bool operator !=(const RandomAccessIterator &other) const { return !(*this == other); } - - //! For sorting. - //! \pre Both iterators must originate from the same collection, and not mix begin/end with cbegin/cend. - //! @{ - bool operator <(const RandomAccessIterator &other) const { Q_ASSERT(m_pimpl && other.m_pimpl); return *pimpl() < *other.pimpl(); } - bool operator >(const RandomAccessIterator &other) const { Q_ASSERT(m_pimpl && other.m_pimpl); return *pimpl() > *other.pimpl(); } - bool operator <=(const RandomAccessIterator &other) const { Q_ASSERT(m_pimpl && other.m_pimpl); return *pimpl() <= *other.pimpl(); } - bool operator >=(const RandomAccessIterator &other) const { Q_ASSERT(m_pimpl && other.m_pimpl); return *pimpl() >= *other.pimpl(); } - //! @} - - //! Subscript operator. - //! \pre `(*this + n)` must be dereferenceable. - reference operator [](difference_type n) const { return *(*this + n); } - - //! Return opaque pointer to underlying implementation iterator object. - //! \pre The iterator must have been initialized. - template U &getImpl() { pimpl()->assertType(typeid(std::decay_t)); return *static_cast(pimpl()->impl()); } - - private: - class PimplBase - { - public: - PimplBase() {} - PimplBase(const PimplBase &) = default; - PimplBase &operator =(const PimplBase &) = delete; - virtual ~PimplBase() {} - virtual PimplBase *clone() const = 0; - virtual const_reference operator *() const = 0; - virtual reference operator *() = 0; - virtual void operator ++() = 0; - virtual void operator --() = 0; - virtual void operator +=(difference_type) = 0; - virtual void operator -=(difference_type) = 0; - virtual difference_type operator -(const PimplBase &) const = 0; - virtual bool operator ==(const PimplBase &) const = 0; - virtual bool operator <(const PimplBase &) const = 0; - virtual bool operator >(const PimplBase &) const = 0; - virtual bool operator <=(const PimplBase &) const = 0; - virtual bool operator >=(const PimplBase &) const = 0; - virtual void *impl() = 0; - virtual void assertType(std::type_index) const = 0; - }; - - template class Pimpl : public PimplBase - { - public: - static_assert(std::is_same::value_type>::value, - "RandomAccessIterator must be initialized from an iterator with the same value_type."); - static_assert(std::is_same::iterator_category, std::random_access_iterator_tag>::value, - "RandomAccessIterator must be initialized from a random access iterator."); - Pimpl(I &&i) : m_impl(std::move(i)) {} - virtual PimplBase *clone() const override { return new Pimpl(*this); } - virtual const_reference operator *() const override { return *m_impl; } - virtual reference operator *() override { return *m_impl; } - virtual void operator ++() override { ++m_impl; } - virtual void operator --() override { --m_impl; } - virtual void operator +=(difference_type n) override { m_impl += n; } - virtual void operator -=(difference_type n) override { m_impl -= n; } - virtual difference_type operator -(const PimplBase &other) const override { return m_impl - static_cast(other).m_impl; } - virtual bool operator ==(const PimplBase &other) const override { return m_impl == static_cast(other).m_impl; } - virtual bool operator <(const PimplBase &other) const override { return m_impl < static_cast(other).m_impl; } - virtual bool operator >(const PimplBase &other) const override { return m_impl > static_cast(other).m_impl; } - virtual bool operator <=(const PimplBase &other) const override { return m_impl <= static_cast(other).m_impl; } - virtual bool operator >=(const PimplBase &other) const override { return m_impl >= static_cast(other).m_impl; } - virtual void *impl() override { return &m_impl; } - virtual void assertType(std::type_index ti) const override { Q_ASSERT(ti == typeid(I)); Q_UNUSED(ti); } - private: - I m_impl; - }; - - using PimplPtr = QScopedPointer; - PimplPtr m_pimpl; - - explicit RandomAccessIterator(PimplBase *pimpl) : m_pimpl(pimpl) {} // private ctor used by fromImpl() - - // using these methods to access m_pimpl.data() eases the cognitive burden of correctly forwarding const - PimplBase *pimpl() { return m_pimpl.data(); } - const PimplBase *pimpl() const { return m_pimpl.data(); } - }; - } //namespace Iterators } //namespace BlackMisc diff --git a/src/blackmisc/propertyindexvariantmap.cpp b/src/blackmisc/propertyindexvariantmap.cpp index d20c681e6..f87e6eb74 100644 --- a/src/blackmisc/propertyindexvariantmap.cpp +++ b/src/blackmisc/propertyindexvariantmap.cpp @@ -117,7 +117,7 @@ namespace BlackMisc CPropertyIndexList CPropertyIndexVariantMap::indexes() const { - return CPropertyIndexList::fromImpl(this->m_values.keys()); + return CSequence(this->m_values.keys()); } int CPropertyIndexVariantMap::size() const diff --git a/src/blackmisc/sequence.h b/src/blackmisc/sequence.h index 9fe67d416..50bf33bf0 100644 --- a/src/blackmisc/sequence.h +++ b/src/blackmisc/sequence.h @@ -12,34 +12,22 @@ #ifndef BLACKMISC_SEQUENCE_H #define BLACKMISC_SEQUENCE_H -#include "iterator.h" #include "containerbase.h" #include "propertyindex.h" #include "icon.h" -#include +#include #include #include #include #include #include -// conditions matched with pop pragmas at bottom of file -#if defined(QT_CC_CLANG) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wshorten-64-to-32" -#elif defined(Q_CC_MSVC) && defined(Q_OS_WIN64) && defined(QT_CC_WARNINGS) -#pragma warning(push) -#pragma warning(disable:4244) -#endif - namespace BlackMisc { /*! - * Generic type-erased sequential container with value semantics. + * Generic sequential container with value semantics. * \tparam T the type of elements contained. - * - * Can take any suitable container class as its implementation at runtime. */ template class CSequence : @@ -55,231 +43,197 @@ namespace BlackMisc typedef const T &const_reference; typedef T *pointer; typedef const T *const_pointer; - typedef typename Iterators::ConstRandomAccessIterator const_iterator; - typedef typename Iterators::RandomAccessIterator iterator; - typedef std::reverse_iterator const_reverse_iterator; - typedef std::reverse_iterator reverse_iterator; + typedef typename QVector::const_iterator const_iterator; + typedef typename QVector::iterator iterator; + typedef typename QVector::const_reverse_iterator const_reverse_iterator; + typedef typename QVector::reverse_iterator reverse_iterator; typedef ptrdiff_t difference_type; typedef int size_type; //! @} //! Default constructor. - CSequence() : m_pimpl(new Pimpl>(QList())) {} + CSequence() {} //! Initializer list constructor. - CSequence(std::initializer_list il) : m_pimpl(new Pimpl>(QList(il))) {} + CSequence(std::initializer_list il) : m_impl(il) {} + + //! By QVector of type T. + //! @{ + CSequence(const QVector &vector) : m_impl(vector) {} + CSequence(QVector &&vector) : m_impl(std::move(vector)) {} + //! @} //! By QList of type T. - CSequence(const QList &list) : m_pimpl(new Pimpl>(QList(list))) {} + CSequence(const QList &list) : m_impl(list.toVector()) {} //! Copy constructor. - CSequence(const CSequence &other) : m_pimpl(other.pimpl() ? other.pimpl()->clone() : nullptr) {} + CSequence(const CSequence &other) = default; //! Move constructor. - CSequence(CSequence &&other) noexcept(std::is_nothrow_move_constructible::value) : m_pimpl(other.m_pimpl.take()) {} + CSequence(CSequence &&other) = default; //! Copy assignment. - CSequence &operator =(const CSequence &other) { m_pimpl.reset(other.pimpl() ? other.pimpl()->clone() : nullptr); return *this; } + CSequence &operator =(const CSequence &other) = default; //! Move assignment. - CSequence &operator =(CSequence && other) noexcept(std::is_nothrow_move_assignable::value) { m_pimpl.reset(other.m_pimpl.take()); return *this; } + CSequence &operator =(CSequence &&other) = default; //! Destructor. ~CSequence() = default; - //! Create a new sequence with a specific implementation type. - //! \tparam C Becomes the sequence's implementation type. - //! \param c Initial value for the sequence; default is empty, but it could contain elements if desired. The value is copied. - template static CSequence fromImpl(C c = C()) { return CSequence(new Pimpl(std::move(c))); } - - //! Change the implementation type but keep all the same elements, by moving them into the new implementation. - //! \tparam C Becomes the sequence's new implementation type. - template void changeImpl(C = C()) { auto c = fromImpl(C()); std::move(begin(), end(), std::inserter(c, c.begin())); *this = std::move(c); } - - //! Like changeImpl, but uses the implementation type of another sequence. - //! \pre The other sequence must be initialized. - void useImplOf(const CSequence &other) { CSequence c(other.pimpl()->cloneEmpty()); std::move(begin(), end(), std::inserter(c, c.begin())); *this = std::move(c); } - //! Returns iterator at the beginning of the sequence. - iterator begin() { return pimpl() ? pimpl()->begin() : iterator(); } + iterator begin() { return m_impl.begin(); } //! Returns const iterator at the beginning of the sequence. - const_iterator begin() const { return pimpl() ? pimpl()->begin() : const_iterator(); } + const_iterator begin() const { return m_impl.begin(); } //! Returns const iterator at the beginning of the sequence. - const_iterator cbegin() const { return pimpl() ? pimpl()->cbegin() : const_iterator(); } + const_iterator cbegin() const { return m_impl.cbegin(); } //! Returns iterator one past the end of the sequence. - iterator end() { return pimpl() ? pimpl()->end() : iterator(); } + iterator end() { return m_impl.end(); } //! Returns const iterator one past the end of the sequence. - const_iterator end() const { return pimpl() ? pimpl()->end() : const_iterator(); } + const_iterator end() const { return m_impl.end(); } //! Returns const iterator one past the end of the sequence. - const_iterator cend() const { return pimpl() ? pimpl()->cend() : const_iterator(); } + const_iterator cend() const { return m_impl.cend(); } //! Returns iterator at the beginning of the reversed sequence. - reverse_iterator rbegin() { return reverse_iterator(end()); } + reverse_iterator rbegin() { return m_impl.rbegin(); } //! Returns const iterator at the beginning of the reversed sequence. - const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } + const_reverse_iterator rbegin() const { return m_impl.rbegin(); } //! Returns const iterator at the beginning of the reversed sequence. - const_reverse_iterator crbegin() const { return const_reverse_iterator(cend()); } + const_reverse_iterator crbegin() const { return m_impl.crbegin(); } //! Returns iterator at the end of the reversed sequence. - reverse_iterator rend() { return reverse_iterator(begin()); } + reverse_iterator rend() { return m_impl.rend(); } //! Returns const iterator at the end of the reversed sequence. - const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } + const_reverse_iterator rend() const { return m_impl.rend(); } //! Returns const iterator at the end of the reversed sequence. - const_reverse_iterator crend() const { return const_reverse_iterator(cbegin()); } + const_reverse_iterator crend() const { return m_impl.crend(); } //! Swap this sequence with another. - void swap(CSequence &other) noexcept { m_pimpl.swap(other.m_pimpl); } + void swap(CSequence &other) noexcept { m_impl.swap(other.m_impl); } //! Access an element by its index. - //! \pre The sequence must be initialized and the index in bounds. - reference operator [](size_type index) { Q_ASSERT(pimpl()); return pimpl()->operator [](index); } + reference operator [](size_type index) { Q_ASSERT(index >= 0 && index < m_impl.size()); return m_impl[index]; } //! Access an element by its index. - //! \pre The sequence must be initialized and the index in bounds. - const_reference operator [](size_type index) const { Q_ASSERT(pimpl()); return pimpl()->operator [](index); } + const_reference operator [](size_type index) const { Q_ASSERT(index >= 0 && index < m_impl.size()); return m_impl[index]; } //! Access the first element. - //! \pre The sequence must not be empty. - reference front() { Q_ASSERT(!empty()); return pimpl()->front(); } + reference front() { Q_ASSERT(!empty()); return m_impl.front(); } //! Access the first element. - //! \pre The sequence must not be empty. - const_reference front() const { Q_ASSERT(!empty()); return pimpl()->front(); } + const_reference front() const { Q_ASSERT(!empty()); return m_impl.front(); } //! Access the first element, or a default-initialized value if the sequence is empty. - const_reference frontOrDefault() const { static const value_type def {}; return empty() ? def : front(); } + const_reference frontOrDefault() const { static const value_type def {}; return empty() ? def : m_impl.front(); } //! Access the first element, or a default-initialized value if the sequence is empty. - value_type frontOrDefault(value_type def) const { return empty() ? def : front(); } + value_type frontOrDefault(value_type def) const { return empty() ? def : m_impl.front(); } //! Access the last element. - //! \pre The sequence must not be empty. - reference back() { Q_ASSERT(!empty()); return pimpl()->back(); } + reference back() { Q_ASSERT(!empty()); return m_impl.back(); } //! Access the last element. - //! \pre The sequence must not be empty. - const_reference back() const { Q_ASSERT(!empty()); return pimpl()->back(); } + const_reference back() const { Q_ASSERT(!empty()); return m_impl.back(); } //! Access the last element, or a default value if the sequence is empty. - const_reference backOrDefault() const { static const value_type def {}; return empty() ? def : back(); } + const_reference backOrDefault() const { static const value_type def {}; return empty() ? def : m_impl.back(); } //! Access the last element, or a default value if the sequence is empty. - value_type backOrDefault(value_type def) const { return empty() ? def : back(); } + value_type backOrDefault(value_type def) const { return empty() ? def : m_impl.back(); } //! Returns number of elements in the sequence. - size_type size() const { return pimpl() ? pimpl()->size() : 0; } + size_type size() const { return m_impl.size(); } //! Returns true if the sequence is empty. - bool empty() const { return pimpl() ? pimpl()->empty() : true; } + bool empty() const { return m_impl.isEmpty(); } //! Synonym for empty. bool isEmpty() const { return empty(); } //! Removes all elements in the sequence. - void clear() { if (pimpl()) pimpl()->clear(); } + void clear() { m_impl.clear(); } //! Changes the size of the sequence, if it is bigger than the given size. void truncate(size_type maxSize) { if (size() > maxSize) { erase(begin() + maxSize, end()); } } //! Inserts an element into the sequence. //! \return An iterator to the position where value was inserted. - //! \pre The sequence must be initialized. - iterator insert(iterator before, const T &value) { Q_ASSERT(pimpl()); return pimpl()->insert(before, value); } + iterator insert(iterator before, const T &value) { return m_impl.insert(before, value); } //! Moves an element into the sequence. //! \return An iterator to the position where value was inserted. - //! \pre The sequence must be initialized. - iterator insert(iterator before, T &&value) { Q_ASSERT(pimpl()); return pimpl()->insert(before, std::move(value)); } + iterator insert(iterator before, T &&value) { return m_impl.insert(before, std::move(value)); } //! Appends an element at the end of the sequence. - //! \pre The sequence must be initialized. - void push_back(const T &value) { Q_ASSERT(pimpl()); pimpl()->push_back(value); } + void push_back(const T &value) { m_impl.push_back(value); } //! Insert as first element. - //! \pre The sequence must be initialized. void push_front(const T &value) { insert(begin(), value); } //! Move-appends an element at the end of the sequence. - //! \pre The sequence must be initialized. - void push_back(T &&value) { Q_ASSERT(pimpl()); pimpl()->push_back(std::move(value)); } + void push_back(T &&value) { m_impl.push_back(std::move(value)); } //! Move-insert as first element. - //! \pre The sequence must be initialized. void push_front(T &&value) { insert(begin(), std::move(value)); } //! Appends all elements from another sequence at the end of this sequence. - //! \pre This sequence must be initialized. void push_back(const CSequence &other) { std::copy(other.begin(), other.end(), std::back_inserter(*this)); } //! Appends all elements from another sequence at the end of this sequence. //! This version moves elements instead of copying. - //! \pre This sequence must be initialized. void push_back(CSequence &&other) { std::move(other.begin(), other.end(), std::back_inserter(*this)); } //! Appends all elements from a range at the end of this sequence. - //! \pre This sequence must be initialized. template void push_back(const CRange &range) { std::copy(range.begin(), range.end(), std::back_inserter(*this)); } //! Synonym for push_back. - //! \pre The sequence must be initialized. void insert(const T &value) { push_back(value); } //! Synonym for push_back. - //! \pre The sequence must be initialized. void insert(T &&value) { push_back(std::move(value)); } //! Synonym for push_back. - //! \pre The sequence must be initialized. void insert(const CSequence &other) { push_back(other); } //! Synonym for push_back. - //! \pre The sequence must be initialized. void insert(CSequence &&other) { push_back(std::move(other)); } //! Synonym for push_back. - //! \pre This sequence must be initialized. template void insert(const CRange &range) { std::copy(range.begin(), range.end(), std::back_inserter(*this)); } //! Concatenates two sequences and returns the result. - //! \pre This sequence must be initialized. CSequence join(const CSequence &other) const { CSequence copy(*this); copy.push_back(other); return copy; } //! Concatenates a sequence and a range and returns the result. - //! \pre This sequence must be initialized. template CSequence join(const CRange &range) const { CSequence copy(*this); copy.push_back(range); return copy; } //! Removes an element at the end of the sequence. - //! \pre The sequence must contain at least one element. - void pop_back() { Q_ASSERT(!empty()); pimpl()->pop_back(); } + void pop_back() { Q_ASSERT(!empty()); m_impl.pop_back(); } //! Removes an element at the end of the sequence. - //! \pre The sequence must contain at least one element. - void pop_front() { erase(begin()); } + void pop_front() { Q_ASSERT(!empty()); erase(begin()); } //! Remove the element pointed to by the given iterator. //! \return An iterator to the position of the next element after the one removed. - //! \pre The sequence must be initialized. - iterator erase(iterator pos) { Q_ASSERT(pimpl()); return pimpl()->erase(pos); } + iterator erase(iterator pos) { return m_impl.erase(pos); } //! Remove the range of elements between two iterators. //! \return An iterator to the position of the next element after the one removed. - //! \pre The sequence must be initialized. - iterator erase(iterator it1, iterator it2) { Q_ASSERT(pimpl()); return pimpl()->erase(it1, it2); } + iterator erase(iterator it1, iterator it2) { return m_impl.erase(it1, it2); } //! Return an iterator to the first element equal to the given object, or the end iterator if not found. O(n). - //! \warning Take care that the returned non-const iterator is not compared with a const iterator. iterator find(const T &object) { return std::find(begin(), end(), object); } //! Return an iterator to the first element equal to the given object, or the end iterator if not found. O(n). @@ -311,7 +265,6 @@ namespace BlackMisc } //! Remove all elements equal to the given object, if it is contained. - //! \pre The sequence must be initialized. //! \return The number of elements removed. int remove(const T &object) { @@ -322,7 +275,6 @@ namespace BlackMisc } //! Remove elements for which a given predicate returns true. - //! \pre The sequence must be initialized. //! \return The number of elements removed. template int removeIf(Predicate p) @@ -342,7 +294,6 @@ namespace BlackMisc } //! Remove all elements if they are in other - //! \pre The sequence must be initialized. //! \return The number of elements removed. int removeIfIn(const CSequence &other) { @@ -386,7 +337,6 @@ namespace BlackMisc } //! Replace elements for which a given predicate returns true. If there is no match, push the new element on the end. - //! \pre The sequence must be initialized. template void replaceOrAdd(Predicate p, const T &replacement) { @@ -395,7 +345,6 @@ namespace BlackMisc } //! Replace elements matching the given element. If there is no match, push the new element on the end. - //! \pre The sequence must be initialized. void replaceOrAdd(const T &original, const T &replacement) { if (this->contains(original)) { replace(original, replacement); } @@ -406,7 +355,6 @@ namespace BlackMisc //! \param key1 A pointer to a member function of T. //! \param value1 Will be compared to the return value of key1. //! \param replacement All matching elements will be replaced by copies of this one, or a copy will be added. - //! \pre The sequence must be initialized. template void replaceOrAdd(K1 key1, V1 value1, const T &replacement) { @@ -537,110 +485,25 @@ namespace BlackMisc } //! Equals operator. - friend bool operator ==(const CSequence &a, const CSequence &b) - { - if (a.size() != b.size()) { return false; } - return std::equal(a.begin(), a.end(), b.begin()); - } + friend bool operator ==(const CSequence &a, const CSequence &b) { return a.m_impl == b.m_impl; } //! Not equals operator. - friend bool operator !=(const CSequence &a, const CSequence &b) { return !(a == b); } + friend bool operator !=(const CSequence &a, const CSequence &b) { return a.m_impl != b.m_impl; } //! Less than operator. - friend bool operator <(const CSequence &a, const CSequence &b) - { - return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); - } + friend bool operator <(const CSequence &a, const CSequence &b) { return a.m_impl < b.m_impl; } //! Greater than operator. - friend bool operator >(const CSequence &a, const CSequence &b) { return b < a; } + friend bool operator >(const CSequence &a, const CSequence &b) { return a.m_impl > b.m_impl; } //! Less or equal than operator. - friend bool operator <=(const CSequence &a, const CSequence &b) { return !(b < a); } + friend bool operator <=(const CSequence &a, const CSequence &b) { return a.m_impl <= b.m_impl; } //! Greater or equal operator. - friend bool operator >=(const CSequence &a, const CSequence &b) { return !(a < b); } - - //! Return an opaque pointer to the implementation container. - //! \details Can be useful in unusual debugging situations. - //! \warning Not for general use. - void *getImpl() { return pimpl() ? pimpl()->impl() : nullptr; } + friend bool operator >=(const CSequence &a, const CSequence &b) { return a.m_impl >= b.m_impl; } private: - class PimplBase - { - public: - PimplBase() {} - PimplBase(const PimplBase &) = default; - PimplBase &operator =(const PimplBase &) = delete; - virtual ~PimplBase() {} - virtual PimplBase *clone() const = 0; - virtual PimplBase *cloneEmpty() const = 0; - virtual iterator begin() = 0; - virtual const_iterator begin() const = 0; - virtual const_iterator cbegin() const = 0; - virtual iterator end() = 0; - virtual const_iterator end() const = 0; - virtual const_iterator cend() const = 0; - virtual reference operator [](size_type index) = 0; - virtual const_reference operator [](size_type index) const = 0; - virtual reference front() = 0; - virtual const_reference front() const = 0; - virtual reference back() = 0; - virtual const_reference back() const = 0; - virtual size_type size() const = 0; - virtual bool empty() const = 0; - virtual void clear() = 0; - virtual iterator insert(iterator pos, const T &value) = 0; - virtual void push_back(const T &value) = 0; - virtual void push_back(T &&value) = 0; - virtual void pop_back() = 0; - virtual iterator erase(iterator pos) = 0; - virtual iterator erase(iterator it1, iterator it2) = 0; - virtual void *impl() = 0; - }; - - template class Pimpl : public PimplBase - { - public: - static_assert(std::is_same::value, "CSequence must be initialized from a container with the same value_type."); - Pimpl(C &&c) : m_impl(std::move(c)) {} - PimplBase *clone() const override { return new Pimpl(*this); } - PimplBase *cloneEmpty() const override { return new Pimpl(C()); } - iterator begin() override { return iterator::fromImpl(m_impl.begin()); } - const_iterator begin() const override { return const_iterator::fromImpl(m_impl.cbegin()); } - const_iterator cbegin() const override { return const_iterator::fromImpl(m_impl.cbegin()); } - iterator end() override { return iterator::fromImpl(m_impl.end()); } - const_iterator end() const override { return const_iterator::fromImpl(m_impl.cend()); } - const_iterator cend() const override { return const_iterator::fromImpl(m_impl.cend()); } - reference operator [](size_type index) override { return m_impl[index]; } - const_reference operator [](size_type index) const override { return m_impl[index]; } - reference front() override { return m_impl.front(); } - const_reference front() const override { return m_impl.front(); } - reference back() override { return m_impl.back(); } - const_reference back() const override { return m_impl.back(); } - size_type size() const override { return static_cast(m_impl.size()); } - bool empty() const override { return m_impl.empty(); } - void clear() override { m_impl.clear(); } - iterator insert(iterator pos, const T &value) override { return iterator::fromImpl(m_impl.insert(pos.template getImpl(), value)); } - void push_back(const T &value) override { m_impl.push_back(value); } - void push_back(T &&value) override { m_impl.push_back(std::move(value)); } - void pop_back() override { m_impl.pop_back(); } - iterator erase(iterator pos) override { return iterator::fromImpl(m_impl.erase(pos.template getImpl())); } - iterator erase(iterator it1, iterator it2) override { return iterator::fromImpl(m_impl.erase(it1.template getImpl(), it2.template getImpl())); } - void *impl() override { return &m_impl; } - private: - C m_impl; - }; - - using PimplPtr = QScopedPointer; - PimplPtr m_pimpl; - - explicit CSequence(PimplBase *pimpl) : m_pimpl(pimpl) {} // private ctor used by fromImpl() - - // using these methods to access m_pimpl.data() eases the cognitive burden of correctly forwarding const - PimplBase *pimpl() { return m_pimpl.data(); } - const PimplBase *pimpl() const { return m_pimpl.data(); } + QVector m_impl; }; } //namespace BlackMisc @@ -651,11 +514,4 @@ Q_DECLARE_METATYPE(BlackMisc::CSequence) Q_DECLARE_METATYPE(BlackMisc::CSequence) Q_DECLARE_METATYPE(BlackMisc::CSequence) -// conditions matched with pop pragmas at bottom of file -#if defined(QT_CC_CLANG) -#pragma clang diagnostic pop -#elif defined(Q_CC_MSVC) && defined(Q_OS_WIN64) && defined(QT_CC_WARNINGS) -#pragma warning(pop) -#endif - #endif //BLACKMISC_SEQUENCE_H diff --git a/tests/blackmisc/testcontainers.cpp b/tests/blackmisc/testcontainers.cpp index 771398dca..42d22a956 100644 --- a/tests/blackmisc/testcontainers.cpp +++ b/tests/blackmisc/testcontainers.cpp @@ -51,17 +51,13 @@ namespace BlackMiscTest { CCollection c1; QVERIFY2(c1.empty(), "Uninitialized collection is empty"); - auto c2 = CCollection::fromImpl(QSet()); - QVERIFY2(c1 == c2, "Uninitialized and empty collections are equal"); - c1.changeImpl(std::set()); - QVERIFY2(c1 == c2, "Two empty collections are equal"); + auto c2 = c1; + QVERIFY2(c1 == c2, "Copy of collection is equal"); c1.insert(1); QVERIFY2(c1 != c2, "Different collections are not equal"); QVERIFY2(c1.size() == 1, "Collection has expected size"); c2.insert(1); QVERIFY2(c1 == c2, "Collections with equal elements are equal"); - c1.changeImpl(QSet()); - QVERIFY2(c1 == c2, "Collection stays equal after changing implementation"); c1.clear(); QVERIFY2(c1.empty(), "Cleared collection is empty"); c1.insert(2); @@ -74,17 +70,13 @@ namespace BlackMiscTest { CSequence s1; QVERIFY2(s1.empty(), "Uninitialized sequence is empty"); - auto s2 = CSequence::fromImpl(QList()); - QVERIFY2(s1 == s2, "Uninitialized and empty sequence are equal"); - s1.changeImpl(std::vector()); - QVERIFY2(s1 == s2, "Two empty sequences are equal"); + auto s2 = s1; + QVERIFY2(s1 == s2, "Copy of sequence is equal"); s1.push_back(1); QVERIFY2(s1 != s2, "Different sequences are not equal"); QVERIFY2(s1.size() == 1, "Sequence has expected size"); s2.push_back(1); QVERIFY2(s1 == s2, "Sequences with equal elements are equal"); - s1.changeImpl(QVector()); - QVERIFY2(s1 == s2, "Sequence stays equal after changing implementation"); s1.clear(); QVERIFY2(s1.empty(), "Cleared sequence is empty"); s1.push_back(2);