diff --git a/src/blackmisc/collection.h b/src/blackmisc/collection.h index 2fe56dc2e..59253850a 100644 --- a/src/blackmisc/collection.h +++ b/src/blackmisc/collection.h @@ -172,6 +172,20 @@ namespace BlackMisc */ iterator erase(iterator it1, iterator it2) { Q_ASSERT(pimpl()); return pimpl()->erase(it1, it2); } + /*! + * \brief 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. + */ + iterator find(const T &value) { Q_ASSERT(pimpl()); return pimpl()->find(value); } + + /*! + * \brief 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); } + /*! * \brief Test for equality. * \todo Improve inefficient implementation. @@ -203,16 +217,9 @@ namespace BlackMisc virtual iterator insert(const 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; - protected: - // using SFINAE to choose whether to implement insert() in terms of either push_back() or insert(), depending on which is available - // https://groups.google.com/forum/#!original/comp.lang.c++.moderated/T3x6lvmvvkQ/mfY5VTDJ--UJ - class yes { char x; }; class no { yes x[2]; }; template struct typecheck {}; - struct base { void push_back(); }; template struct derived : public C, public base {}; - static yes hasPushHelper(...); template static no hasPushHelper(D *, typecheck * = 0); - template struct hasPush : public std::integral_constant*)0)) == sizeof(yes)> {}; - template static iterator insertImpl(typename std::enable_if< hasPush::value, C>::type &c, const T &value) { c.push_back(value); return iterator::fromImpl(c.end() - 1); } - template static iterator insertImpl(typename std::enable_if < !hasPush::value, C >::type &c, const T &value) { return iterator::fromImpl(c.insert(value)); } }; template class Pimpl : public PimplBase @@ -231,13 +238,18 @@ namespace BlackMisc size_type size() const { return m_impl.size(); } bool empty() const { return m_impl.empty(); } void clear() { m_impl.clear(); } - iterator insert(const T &value) { return PimplBase::insertImpl(m_impl, value); } + iterator insert(const T &value) { return iterator::fromImpl(insertHelper(m_impl.insert(value))); } iterator erase(iterator pos) { return iterator::fromImpl(m_impl.erase(*static_cast(pos.getImpl()))); } //iterator erase(iterator it1, iterator it2) { return iterator::fromImpl(m_impl.erase(*static_cast(it1.getImpl()), *static_cast(it2.getImpl()))); } iterator erase(iterator it1, iterator it2) { while (it1 != it2) { it1 = iterator::fromImpl(m_impl.erase(*static_cast(it1.getImpl()))); } return it1; } + iterator find(const T &value) { return iterator::fromImpl(m_impl.find(value)); } + const_iterator find(const T &value) const { return const_iterator::fromImpl(m_impl.find(value)); } bool operator ==(const PimplBase &other) const { Pimpl copy = C(); for (auto i = other.cbegin(); i != other.cend(); ++i) copy.insert(*i); return m_impl == copy.m_impl; } private: C m_impl; + // insertHelper: QSet::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; } }; typedef QScopedPointer PimplPtr; diff --git a/src/blackmisc/containerbase.h b/src/blackmisc/containerbase.h index 7586938eb..6873b33ba 100644 --- a/src/blackmisc/containerbase.h +++ b/src/blackmisc/containerbase.h @@ -95,11 +95,11 @@ namespace BlackMisc } /*! - * \brief Return true if there is an element equal to given object + * \brief Return true if there is an element equal to given object. Uses the most efficient implementation available. */ bool contains(const T &object) const { - return std::find(derived().begin(), derived().end(), object) != derived().end(); + return derived().find(object) != derived().end(); } /*! diff --git a/src/blackmisc/iterator.h b/src/blackmisc/iterator.h index 56b2c9d2d..06a96e27b 100644 --- a/src/blackmisc/iterator.h +++ b/src/blackmisc/iterator.h @@ -152,7 +152,7 @@ namespace BlackMisc virtual PimplBase *clone() const { return new Pimpl(*this); } virtual const_reference operator *() const { return *m_impl; } virtual void operator ++() { ++m_impl; } - virtual void operator +=(difference_type n) { m_impl += n; } + virtual void operator +=(difference_type n) { std::advance(m_impl, n); } virtual bool operator ==(const PimplBase &other) const { return m_impl == static_cast(other).m_impl; } virtual void *impl() { return &m_impl; } private: diff --git a/src/blackmisc/sequence.h b/src/blackmisc/sequence.h index 500fb4343..1b2a09e45 100644 --- a/src/blackmisc/sequence.h +++ b/src/blackmisc/sequence.h @@ -219,6 +219,11 @@ namespace BlackMisc */ iterator erase(iterator it1, iterator it2) { Q_ASSERT(pimpl()); return pimpl()->erase(it1, it2); } + /*! + * \brief Return an iterator to the first element equal to the given object, or the end iterator if not found. O(n). + */ + iterator find(const T &object) const { return std::find(begin(), end(), object); } + /*! * \brief Modify by applying a value map to each element for which a given predicate returns true. */ diff --git a/tests/blackmisc/testcontainers.cpp b/tests/blackmisc/testcontainers.cpp index fbd73612c..b4815cbc0 100644 --- a/tests/blackmisc/testcontainers.cpp +++ b/tests/blackmisc/testcontainers.cpp @@ -7,8 +7,10 @@ #include "blackmisc/collection.h" #include "blackmisc/sequence.h" #include -#include +#include +#include #include +#include using namespace BlackMisc; @@ -19,9 +21,9 @@ namespace BlackMiscTest { CCollection c1; QVERIFY2(c1.empty(), "Uninitialized collection is empty"); - auto c2 = CCollection::fromImpl(QList()); + auto c2 = CCollection::fromImpl(QSet()); QVERIFY2(c1 == c2, "Uninitialized and empty collections are equal"); - c1.changeImpl(std::vector()); + c1.changeImpl(std::set()); QVERIFY2(c1 == c2, "Two empty collections are equal"); c1.insert(1); QVERIFY2(c1 != c2, "Different collections are not equal");