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
This commit is contained in:
Mathew Sutcliffe
2017-11-23 22:15:23 +00:00
parent 921ef30eda
commit fcb6cf1a52
8 changed files with 111 additions and 991 deletions

View File

@@ -12,11 +12,9 @@
#ifndef BLACKMISC_COLLECTION_H
#define BLACKMISC_COLLECTION_H
#include "iterator.h"
#include "containerbase.h"
#include "icon.h"
#include <QScopedPointer>
#include <QMap>
#include <algorithm>
#include <type_traits>
#include <typeindex>
@@ -24,15 +22,6 @@
#include <utility>
#include <initializer_list>
// 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 T>
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<T> const_iterator;
typedef typename QOrderedSet<T>::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<T>>(QOrderedSet<T>())) {}
CCollection() {}
//! Initializer list constructor.
CCollection(std::initializer_list<T> il) : m_pimpl(new Pimpl<QOrderedSet<T>>(QOrderedSet<T>(il))) {}
CCollection(std::initializer_list<T> 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<T> &list) : m_pimpl(new Pimpl<QOrderedSet<T>>(QOrderedSet<T>(list))) {}
CCollection(const QList<T> &list) : m_impl(list) {}
//! Move constructor.
CCollection(CCollection &&other) noexcept(std::is_nothrow_move_constructible<T>::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<T>::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 <class C> static CCollection fromImpl(C c = C()) { return CCollection(new Pimpl<C>(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 <class C> 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 <typename I>
void insert(const CRange<I> &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 <typename I>
void push_back(const CRange<I> &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<QMapNode<T, T> *&>(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<QMapNode<T, T> *&>(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 C> class Pimpl : public PimplBase
{
public:
static_assert(std::is_same<T, typename C::value_type>::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<size_type>(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<const typename C::iterator>())); }
//iterator erase(iterator it1, iterator it2) override { return iterator::fromImpl(m_impl.erase(it1.template getImpl<const typename C::iterator>(), it2.template getImpl<const typename C::iterator>())); }
iterator erase(iterator it1, iterator it2) override { while (it1 != it2) { it1 = iterator::fromImpl(m_impl.erase(it1.template getImpl<const typename C::iterator>())); } 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<const C *>(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<const C *>(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<interator, bool>
template <class I> static I insertHelper(I i) { return i; }
template <class I> static I insertHelper(std::pair<I, bool> p) { return p.first; }
};
using PimplPtr = QScopedPointer<PimplBase>;
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<T> m_impl;
};
} //namespace BlackMisc
@@ -395,11 +280,4 @@ Q_DECLARE_METATYPE(BlackMisc::CCollection<qlonglong>)
Q_DECLARE_METATYPE(BlackMisc::CCollection<qulonglong>)
// CCollection<double> 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

View File

@@ -14,7 +14,6 @@
#include "optional.h"
#include "typetraits.h"
#include <QScopedPointer>
#include <algorithm>
#include <type_traits>
#include <iterator>
@@ -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 T> 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 <class I> static ConstForwardIterator fromImpl(I i) { return ConstForwardIterator(new Pimpl<I>(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 <typename U> U &getImpl() { pimpl()->assertType(typeid(std::decay_t<U>)); return *static_cast<U*>(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 I> class Pimpl : public PimplBase
{
public:
static_assert(std::is_same<T, typename std::iterator_traits<I>::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<const Pimpl&>(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<PimplBase>;
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 T> 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 <class I> static ConstRandomAccessIterator fromImpl(I i) { return ConstRandomAccessIterator(new Pimpl<I>(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 <typename U> U &getImpl() { pimpl()->assertType(typeid(std::decay_t<U>)); return *static_cast<U*>(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 I> class Pimpl : public PimplBase
{
public:
static_assert(std::is_same<T, typename std::iterator_traits<I>::value_type>::value,
"ConstRandomAccessIterator must be initialized from an iterator with the same value_type.");
static_assert(std::is_same<typename std::iterator_traits<I>::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<const Pimpl&>(other).m_impl; }
virtual bool operator ==(const PimplBase &other) const override { return m_impl == static_cast<const Pimpl&>(other).m_impl; }
virtual bool operator <(const PimplBase &other) const override { return m_impl < static_cast<const Pimpl&>(other).m_impl; }
virtual bool operator >(const PimplBase &other) const override { return m_impl > static_cast<const Pimpl&>(other).m_impl; }
virtual bool operator <=(const PimplBase &other) const override { return m_impl <= static_cast<const Pimpl&>(other).m_impl; }
virtual bool operator >=(const PimplBase &other) const override { return m_impl >= static_cast<const Pimpl&>(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<PimplBase>;
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 T> 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 <class I> static RandomAccessIterator fromImpl(I i) { return RandomAccessIterator(new Pimpl<I>(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 <typename U> U &getImpl() { pimpl()->assertType(typeid(std::decay_t<U>)); return *static_cast<U*>(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 I> class Pimpl : public PimplBase
{
public:
static_assert(std::is_same<T, typename std::iterator_traits<I>::value_type>::value,
"RandomAccessIterator must be initialized from an iterator with the same value_type.");
static_assert(std::is_same<typename std::iterator_traits<I>::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<const Pimpl&>(other).m_impl; }
virtual bool operator ==(const PimplBase &other) const override { return m_impl == static_cast<const Pimpl&>(other).m_impl; }
virtual bool operator <(const PimplBase &other) const override { return m_impl < static_cast<const Pimpl&>(other).m_impl; }
virtual bool operator >(const PimplBase &other) const override { return m_impl > static_cast<const Pimpl&>(other).m_impl; }
virtual bool operator <=(const PimplBase &other) const override { return m_impl <= static_cast<const Pimpl&>(other).m_impl; }
virtual bool operator >=(const PimplBase &other) const override { return m_impl >= static_cast<const Pimpl&>(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<PimplBase>;
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

View File

@@ -117,7 +117,7 @@ namespace BlackMisc
CPropertyIndexList CPropertyIndexVariantMap::indexes() const
{
return CPropertyIndexList::fromImpl(this->m_values.keys());
return CSequence<CPropertyIndex>(this->m_values.keys());
}
int CPropertyIndexVariantMap::size() const

View File

@@ -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 <QScopedPointer>
#include <QVector>
#include <algorithm>
#include <type_traits>
#include <iterator>
#include <utility>
#include <initializer_list>
// 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 T>
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<T> const_iterator;
typedef typename Iterators::RandomAccessIterator<T> iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef typename QVector<T>::const_iterator const_iterator;
typedef typename QVector<T>::iterator iterator;
typedef typename QVector<T>::const_reverse_iterator const_reverse_iterator;
typedef typename QVector<T>::reverse_iterator reverse_iterator;
typedef ptrdiff_t difference_type;
typedef int size_type;
//! @}
//! Default constructor.
CSequence() : m_pimpl(new Pimpl<QList<T>>(QList<T>())) {}
CSequence() {}
//! Initializer list constructor.
CSequence(std::initializer_list<T> il) : m_pimpl(new Pimpl<QList<T>>(QList<T>(il))) {}
CSequence(std::initializer_list<T> il) : m_impl(il) {}
//! By QVector of type T.
//! @{
CSequence(const QVector<T> &vector) : m_impl(vector) {}
CSequence(QVector<T> &&vector) : m_impl(std::move(vector)) {}
//! @}
//! By QList of type T.
CSequence(const QList<T> &list) : m_pimpl(new Pimpl<QList<T>>(QList<T>(list))) {}
CSequence(const QList<T> &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<T>::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<T>::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 <class C> static CSequence fromImpl(C c = C()) { return CSequence(new Pimpl<C>(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 <class C> 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 <typename I>
void push_back(const CRange<I> &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 <typename I>
void insert(const CRange<I> &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 <typename I>
CSequence join(const CRange<I> &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 <class Predicate>
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 <class Predicate>
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 <class K1, class V1>
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 C> class Pimpl : public PimplBase
{
public:
static_assert(std::is_same<T, typename C::value_type>::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<size_type>(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<const typename C::iterator>(), 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<const typename C::iterator>())); }
iterator erase(iterator it1, iterator it2) override { return iterator::fromImpl(m_impl.erase(it1.template getImpl<const typename C::iterator>(), it2.template getImpl<const typename C::iterator>())); }
void *impl() override { return &m_impl; }
private:
C m_impl;
};
using PimplPtr = QScopedPointer<PimplBase>;
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<T> m_impl;
};
} //namespace BlackMisc
@@ -651,11 +514,4 @@ Q_DECLARE_METATYPE(BlackMisc::CSequence<qlonglong>)
Q_DECLARE_METATYPE(BlackMisc::CSequence<qulonglong>)
Q_DECLARE_METATYPE(BlackMisc::CSequence<double>)
// 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