From ae4413abdda2ca010c0ded7a2cc4423bef3cb0fb Mon Sep 17 00:00:00 2001 From: Mathew Sutcliffe Date: Thu, 3 Jul 2014 19:03:26 +0100 Subject: [PATCH] refs #290 added iterator adaptors which will allow to construct a CRange representing a transformation of its host container --- src/blackmisc/iterator.h | 200 +++++++++++++++++++++++++++++++++++++++ src/blackmisc/optional.h | 122 ++++++++++++++++++++++++ 2 files changed, 322 insertions(+) create mode 100644 src/blackmisc/optional.h diff --git a/src/blackmisc/iterator.h b/src/blackmisc/iterator.h index 592ec24a2..af845b01e 100644 --- a/src/blackmisc/iterator.h +++ b/src/blackmisc/iterator.h @@ -10,6 +10,7 @@ #ifndef BLACKMISC_ITERATOR_H #define BLACKMISC_ITERATOR_H +#include "optional.h" #include #include #include @@ -21,6 +22,205 @@ namespace BlackMisc namespace Iterators { + /*! + * Iterator wrapper for Qt's STL-style associative container iterators, when dereferenced return the key instead of the value. + * + * By creating a CRange from such iterators, it is possible to create a container of keys without copying them. + */ + template class KeyIterator + : public std::iterator().key())>::type> + { + public: + //! Constructor + KeyIterator(I iterator) : m_iterator(iterator) {} + + //! Advance to the next element. + //! Undefined if iterator is at the end. + //! @{ + KeyIterator &operator ++() { ++m_iterator; return *this; } + KeyIterator operator ++(int) { auto copy = *this; ++m_iterator; return copy; } + //! @} + + //! Regress to the previous element. + //! Undefined if iterator is at the beginning. + //! @{ + KeyIterator &operator --() { --m_iterator; return *this; } + KeyIterator operator --(int) { auto copy = *this; --m_iterator; return copy; } + //! @} + + //! Return the value at this iterator position. + auto value() const -> decltype(std::declval().value()) { return m_iterator.value(); } + + //! Return the key at this iterator position. + //! @{ + auto key() const -> decltype(std::declval().key()) { return m_iterator.key(); } + auto operator *() const -> decltype(std::declval().key()) { return key(); } + //! @} + + //! Indirection operator: pointer to the key at this iterator position. + auto operator ->() const -> typename std::remove_reference().key())>::type * { return &key(); } + + //! Equality operators. + //! @{ + bool operator ==(const KeyIterator &other) const { return m_iterator == other.m_iterator; } + bool operator !=(const KeyIterator &other) const { return m_iterator != other.m_iterator; } + //! @} + + private: + I m_iterator; + }; + + /*! + * Iterator wrapper which applies some transformation function to each element. + * + * By creating a CRange from such iterators, it is possible to perform a transformation on a container without copying elements. + */ + template class TransformIterator + : public std::iterator()(std::declval::value_type>()))>::type> + { + public: + //! The type returned by the transformation function, which may or may not be a reference. + using undecayed_type = decltype(std::declval()(std::declval::value_type>())); + + //! \private A pointer-like wrapper returned by the arrow operator if the transformation function returns by value. + struct PointerWrapper + { + PointerWrapper(typename std::decay::type *obj) : m_obj(std::move(*obj)) {} + typename std::decay::type const *operator ->() const { return &m_obj; } + typename std::decay::type operator *() const { return m_obj; } + // TODO replace operator* above with the following, when our compilers support C++11 ref-qualifiers + //typename std::decay::type operator *() const & { return m_obj; } + //typename std::decay::type operator *() && { return std::move(m_obj); } + private: + const typename std::decay::type m_obj; + }; + + //! The type returned by this iterator's arrow operator, which may be a pointer or a pointer-like wrapper object + using pointer = typename std::conditional::value, + typename std::remove_reference::type *, + PointerWrapper>::type; + + //! Constructor. + TransformIterator(I iterator, F function) : m_iterator(iterator), m_function(function) {} + + //! Implicit conversion from an end iterator. + TransformIterator(I end) : m_iterator(end) {} + + //! Advance to the next element. + //! Undefined if iterator is at the end. + //! @{ + TransformIterator &operator ++() { ++m_iterator; return *this; } + TransformIterator operator ++(int) { auto copy = *this; ++m_iterator; return copy; } + //! @} + + //! Dereference operator, returns the transformed object reference by the iterator. + //! Undefined if iterator is at the end. + undecayed_type operator *() { Q_ASSERT(m_function); return (*m_function)(*m_iterator); } + + //! Indirection operator, returns a pointer to the transformed object, + //! or a pointer-like wrapper object if the transformation function returns by value. + //! Undefined if iterator is at the end. + pointer operator ->() { Q_ASSERT(m_function); auto &&obj = (*m_function)(*m_iterator); return &obj; } + + //! Comparison operators. + //! @{ + bool operator ==(const TransformIterator &other) const { return m_iterator == other.m_iterator; } + bool operator !=(const TransformIterator &other) const { return m_iterator != other.m_iterator; } + bool operator <(const TransformIterator &other) const { return m_iterator < other.m_iterator; } + bool operator <=(const TransformIterator &other) const { return m_iterator <= other.m_iterator; } + bool operator >(const TransformIterator &other) const { return m_iterator > other.m_iterator; } + bool operator >=(const TransformIterator &other) const { return m_iterator >= other.m_iterator; } + //! @} + + private: + I m_iterator; + Optional m_function; + }; + + /*! + * Iterator wrapper which skips over any elements which do not satisfy a given condition predicate. + * + * By creating a CRange from such iterators, it is possible to return the results of predicate methods without copying elements. + */ + template class ConditionalIterator : public std::iterator::value_type> + { + public: + //! Constructor. + ConditionalIterator(I iterator, I end, F predicate) : m_iterator(iterator), m_end(end), m_predicate(predicate) + { + while (m_iterator != m_end && !(*m_predicate)(*m_iterator)) + { + ++m_iterator; + } + } + + //! Implicit conversion from an end iterator. + ConditionalIterator(I end) : m_iterator(end), m_end(end) {} + + //! Advance the iterator to the next element which matches the predicate, or the end if there are none remaining. + //! Undefined if the iterator is already at the end. + //! @{ + ConditionalIterator &operator ++() + { + Q_ASSERT(m_predicate); + do + { + ++m_iterator; + } while (m_iterator != m_end && !(*m_predicate)(*m_iterator)); + return *this; + } + ConditionalIterator operator ++(int) { auto copy = *this; ++(*this); return copy; } + //! @} + + //! Indirection operator, returns the underlying iterator. + //! Undefined if iterator is at the end. + I operator ->() { return m_iterator; } + + //! Dereference operator, returns the object referenced by the iterator. + //! Undefined if iterator is at the end. + typename std::iterator_traits::reference operator *() { return *m_iterator; } + + //! Comparison operators. + //! @{ + bool operator ==(const ConditionalIterator &other) const { return m_iterator == other.m_iterator; } + bool operator !=(const ConditionalIterator &other) const { return m_iterator != other.m_iterator; } + bool operator <(const ConditionalIterator &other) const { return m_iterator < other.m_iterator; } + bool operator <=(const ConditionalIterator &other) const { return m_iterator <= other.m_iterator; } + bool operator >(const ConditionalIterator &other) const { return m_iterator > other.m_iterator; } + bool operator >=(const ConditionalIterator &other) const { return m_iterator >= other.m_iterator; } + //! @} + + private: + I m_iterator; + I m_end; + Optional m_predicate; + }; + + /*! + * Construct a KeyIterator of the appropriate type from deduced template function argument. + */ + template auto makeKeyIterator(I iterator) -> KeyIterator + { + return { iterator }; + } + + /*! + * Construct a TransformIterator of the appropriate type from deduced template function arguments. + */ + template auto makeTransformIterator(I iterator, F function) -> TransformIterator + { + return { iterator, function }; + } + + /*! + * Construct a ConditionalIterator of the appropriate type from deduced template function arguments. + */ + template auto makeConditionalIterator(I iterator, I end, F predicate) -> ConditionalIterator + { + return { iterator, end, predicate }; + } + /*! * \brief Generic type-erased const forward iterator with value semantics. * \tparam T the value_type of the container being iterated over. diff --git a/src/blackmisc/optional.h b/src/blackmisc/optional.h new file mode 100644 index 000000000..414ad2c44 --- /dev/null +++ b/src/blackmisc/optional.h @@ -0,0 +1,122 @@ +/* Copyright (C) 2013 VATSIM Community / authors + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +//! \file + +#ifndef BLACKMISC_OPTIONAL_H +#define BLACKMISC_OPTIONAL_H + +#include +#include + +namespace BlackMisc +{ + + /*! + * Class which can directly contain zero or one object of type T, with pointer semantics. + */ + template + class Optional + { + public: + //! Default constructor. + Optional() : m_isValid(false) {} + + //! Construct from a value. + Optional(T value) : m_isValid(true) { new (m_bytes) T(std::move(value)); } + + //! Construct from a nullptr, equivalent to default constructor. + Optional(std::nullptr_t) : m_isValid(false) {} + + //! Copy constructor. + Optional(const Optional &other) : m_isValid(other.m_isValid) + { + if (other.m_isValid) { new (m_bytes) T(*other); } + } + + //! Move constructor. + Optional(Optional &&other) : m_isValid(other.m_isValid) + { + if (other.m_isValid) { new (m_bytes) T(std::move(*other)); } + } + + //! Assign a nullptr. + Optional &operator =(std::nullptr_t) + { + if (m_isValid) { (*this)->~T(); } + m_isValid = false; + return *this; + } + + //! Copy assignment. + Optional &operator =(const Optional &other) + { + if (m_isValid) { (*this)->~T(); } + if (other.m_isValid) { new (m_bytes) T(*other); } + m_isValid = other.m_isValid; + return *this; + } + + //! Move assignment. + Optional &operator =(Optional &&other) + { + if (m_isValid) { (*this)->~T(); } + if (other.m_isValid) { new (m_bytes) T(std::move(*other)); } + m_isValid = other.m_isValid; + return *this; + } + + //! Destructor. + ~Optional() { if (m_isValid) { (*this)->~T(); } } + + //! Explicit cast to bool, true if this Optional contains a value. + explicit operator bool() const { return m_isValid; } + + //! Dereference operator, returns reference to contained value, undefined if there is no value contained. + T &operator *() { Q_ASSERT(m_isValid); return *reinterpret_cast(m_bytes); } + + //! Dereference operator, returns reference to contained value, undefined if there is no value contained. + const T &operator *() const { Q_ASSERT(m_isValid); return *reinterpret_cast(m_bytes); } + + //! Indirection operator, returns pointer to contained value, undefined if there is no value contained. + T *operator ->() { Q_ASSERT(m_isValid); return reinterpret_cast(m_bytes); } + + //! Indirection operator, returns pointer to contained value, undefined if there is no value contained. + const T *operator ->() const { Q_ASSERT(m_isValid); return reinterpret_cast(m_bytes); } + + private: + bool m_isValid; + char m_bytes[sizeof(T)]; + }; + + /*! + * Efficient swap for two Optional objects. + */ + template + void swap(Optional &a, Optional &b) + { + if (a) + { + if (b) + { + using std::swap; + swap(*a, *b); + } + else + { + b = std::move(a); + a = nullptr; + } + } + else if (b) + { + a = std::move(b); + b = nullptr; + } + } + +} //namespace BlackMisc + +#endif // guard