Browse Source

large changes to the iterator interface, update range respectively

master
Niklas Rosenstein 3 years ago
parent
commit
5bfac6e4dc
Signed by: NiklasRosenstein GPG Key ID: 06D269B33D25F6C6
3 changed files with 102 additions and 91 deletions
  1. + 79
    - 85
      include/NiklasRosenstein/iterator.hpp
  2. + 13
    - 0
      include/NiklasRosenstein/macros.h
  3. + 10
    - 6
      include/NiklasRosenstein/range.hpp

+ 79
- 85
include/NiklasRosenstein/iterator.hpp

@ -10,6 +10,7 @@
#pragma once
#include <cassert>
#include <iterator>
#include "macros.h"
/**
* @macro NR_ITERATOR_NOTYPETRAITS
@ -25,86 +26,53 @@
namespace niklasrosenstein {
/**
* The #self_iterator is an adapter class from a new style iterator
* interface to the C++ iterator interface. It can be subclassed to
* implement iterator objects with the following interface:
*
* - `has_next() [const]`
* - `next()`
* - `X operator * () [const]`
*
* The first two methods will be used by the #self_iterator methods
* #operator++(), #operator!=() and #operator bool() to adapt to the
* C++ iterator interface. The dereference operator is required by
* the C++ iterator interface directly.
*
* This class should be used when implementing a simple iterator.
* However, the calls to #begin() and #end() will cause a copy of
* the iterator to be created, thus it will be problematic if your
* iterator allocates and frees resources automatically. Use the
* #niklasrosenstein::iterator class instead.
*/
template <class T>
struct self_iterator {
/**
* Used by the standard C++ iterator interface. Calls `has_next()` to
* check if the end of the iterator was reached.
*/
inline bool operator != (self_iterator const&) const {
return !static_cast<T const*>(this)->has_next();
}
/**
* Increment operator. Calls `next()` to advance to the next element.
*/
inline self_iterator& operator ++ () {
static_cast<T*>(this)->next();
return *this;
}
/**
* C++ iterator interface.
* @{
*/
T& begin() { return *static_cast<T*>(this); }
T& end() { return *static_cast<T*>(this); }
// @}
};
namespace detail {
namespace iterator {
template <typename T, typename = int>
struct get_yield_type {
#ifdef NR_ITERATOR_NOTYPETRAITS
static_assert(false, "no type traits available, specify T::yield_type");
#else
using type = typename std::result_of<decltype(&T::next)(T)>::type;
#endif
};
template <typename T>
struct get_yield_type <T, decltype(T::yield_type)> {
using type = typename T::yield_type;
};
} // namespace iterator
} // namespace detail
/**
* This class is an adapter of the C++ iterator interface for new style
* iterator classes.
* iterator classes. Note that you must not dereference this iterator
* more than once as it will move the value in `operator*()`!
*/
template <typename T>
class iterator_adapter {
/* Pointer to the iterator implementation. */
T* itimpl;
public:
/**
* @typedef yield_type
*
* This type represents the datatype that is yielded by the iterator.
* If #NR_ITERATOR_NOTYPETRAITS is defined, #<type_traits> will not be
* used and #T must provide the #yield_type instead.
**/
#ifdef NR_ITERATOR_NOTYPETRAITS
typedef typename T::yield_type yield_type;
#else
typedef typename std::result_of<decltype(&T::operator *)(T)>::type yield_type;
#endif
using yield_type = typename detail::iterator::get_yield_type<T>::type;
/**
* Passing nullptr to this constructor represents creates a sentinel
* iterator that marks the end of the iteration.
*
* You can only create a C++ iterator adapter when the iterator object
* is new, ie. it is currently situated at the beginning at NOT the
* first element. This constructor will call #has_next() and #next().
*/
inline iterator_adapter(T* itimpl_) : itimpl(nullptr) {
if (itimpl_ && itimpl_->has_next()) {
this->itimpl = itimpl_;
inline iterator_adapter(T* itimpl) : _itimpl(nullptr) {
#if NR_DEBUG
_has_value = false;
#endif
if (itimpl && itimpl->has_next()) {
_itimpl = itimpl;
_value = itimpl->next();
#if NR_DEBUG
_has_value = true;
#endif
}
}
@ -113,32 +81,58 @@ namespace niklasrosenstein {
* to the next item and check if the end of the iteration has been reached.
*/
inline iterator_adapter& operator ++ () {
assert(this->itimpl != nullptr);
this->itimpl->next();
if (!this->itimpl->has_next()) {
this->itimpl = nullptr;
assert(_itimpl != nullptr);
if (_itimpl->has_next()) {
_value = _itimpl->next();
#if NR_DEBUG
_has_value = true;
#endif
}
else {
_itimpl = nullptr;
#if NR_DEBUG
_has_value = false;
#endif
}
return *this;
}
inline bool operator != (iterator_adapter const& other) const {
return this->itimpl != other.itimpl;
return _itimpl != other._itimpl;
}
inline yield_type operator * () const {
assert(this->itimpl != nullptr);
return this->itimpl->operator * ();
inline yield_type operator * () {
assert(_itimpl != nullptr);
#if NR_DEBUG
assert(_has_value && "has already been dereferenced once for this value.");
_has_value = false;
#endif
return std::move(_value);
}
private:
T* _itimpl;
yield_type _value;
#if NR_DEBUG
bool _has_value;
#endif
};
/**
* This is an extension of the #self_iterator that uses an #iterator_adapter
* class for #begin() and #end() instead. It should be used when employing an
* iterator that allocates and frees resource dynamically.
* This is the base class for new-style iterators. A subclass must specify
* itself as the template parameter #T and implement the following functions:
*
* + yield_type next()
* + bool has_next() [const]
*
* Instances of this class are intended to be used either directly by this
* interface or by using a range-based for loop. In case of the range-based
* for loop, the object returns an #iterator_adapter<> which is not supposed
* to be accessed more than it would be by the expansion of the for loop as
* specified in the C++ standard.
*/
template <class T>
class iterator : public self_iterator<T> {
class iterator {
public:
/**
@ -170,15 +164,15 @@ namespace niklasrosenstein {
class movable_iterator_wrapper {
Iterable& _iter;
public:
movable_iterator_wrapper(Iterable& iter) : _iter(iter) {}
movable_iterator_wrapper(movable_iterator_wrapper&& other)
inline movable_iterator_wrapper(Iterable& iter) : _iter(iter) {}
inline movable_iterator_wrapper(movable_iterator_wrapper&& other)
: _iter(other._iter) {}
auto begin() -> decltype(std::begin(_iter)) { return std::begin(_iter); }
auto end() -> decltype(std::end(_iter)) { return std::end(_iter); }
inline auto begin() -> decltype(std::begin(_iter)) { return std::begin(_iter); }
inline auto end() -> decltype(std::end(_iter)) { return std::end(_iter); }
auto begin() const -> decltype(std::begin(_iter)) { return std::begin(_iter); }
auto end() const -> decltype(std::end(_iter)) { return std::end(_iter); }
inline auto begin() const -> decltype(std::begin(_iter)) { return std::begin(_iter); }
inline auto end() const -> decltype(std::end(_iter)) { return std::end(_iter); }
};
} // namespace niklasrosenstein

+ 13
- 0
include/NiklasRosenstein/macros.h

@ -5,6 +5,19 @@
#pragma once
/**
* This macro can be explicitly defined to enable/disable debug
* functionality. Otherwise it will fall back on enabled when
* either `DEBUG` or `_DEBUG` is defined.
*/
#if !defined(NR_DEBUG)
#if defined(DEBUG) || defined(_DEBUG)
#define NR_DEBUG 1
#else
#define NR_DEBUG 0
#endif
#endif
/**
* This macro allows you to create an enclosed scope that can be
* exited with a `break` or `continue` statement.

+ 10
- 6
include/NiklasRosenstein/range.hpp

@ -17,7 +17,7 @@ namespace niklasrosenstein {
* template parameter #T.
*/
template <class T = std::size_t>
class range : public iterator<range<T>> {
class _range : public iterator<_range<T>> {
T _curr, _max, _step;
static inline int sign(T const& val) { return (T(0) < val) - (val < T(0)); }
@ -27,14 +27,14 @@ namespace niklasrosenstein {
/**
* Create a range iterator starting from zero up excluding #max.
*/
inline range(T const& max) : _curr(0), _max(max), _step(1) { }
inline _range(T max) : _curr(0), _max(max), _step(1) { }
/**
* Create a range iterator starting from #min up to excluding #max
* taking the specified #step each turn (must not be zero and the
* sign must match the direction of the iteration).
*/
inline range(T min, T max, T step = 1) : _curr(min), _max(max), _step(step) {
inline _range(T min, T max, T step = 1) : _curr(min), _max(max), _step(step) {
assert(step != 0 && sign(step) == sign(max - min));
}
@ -44,9 +44,13 @@ namespace niklasrosenstein {
inline bool has_next() const { return this->_curr < this->_max; }
inline void next() { this->_curr += _step; }
inline T operator * () const { return this->_curr; }
inline T next() { T temp = _curr; _curr += _step; return temp; }
};
template <class T = std::size_t>
_range<T> range(T max) { return {max}; }
template <class T = std::size_t>
_range<T> range(T min, T max, T step = 1) { return {min, max, step}; }
} // namespace niklasrosenstein

Loading…
Cancel
Save