Browse Source

support reference and normal types separately in the iterator_adapter class

master
Niklas Rosenstein 3 years ago
parent
commit
ee3ad47592
Signed by: NiklasRosenstein GPG Key ID: 06D269B33D25F6C6
1 changed files with 60 additions and 33 deletions
  1. + 60
    - 33
      include/NiklasRosenstein/iterator.hpp

+ 60
- 33
include/NiklasRosenstein/iterator.hpp

@ -10,6 +10,7 @@
#pragma once
#include <cassert>
#include <iterator>
#include <memory>
#include "macros.h"
/**
@ -28,8 +29,9 @@ namespace niklasrosenstein {
namespace detail {
namespace iterator {
template <typename T, typename = int>
struct get_yield_type {
struct yield_type {
#ifdef NR_ITERATOR_NOTYPETRAITS
static_assert(false, "no type traits available, specify T::yield_type");
#else
@ -38,22 +40,65 @@ namespace niklasrosenstein {
};
template <typename T>
struct get_yield_type <T, decltype(T::yield_type)> {
struct yield_type <T, decltype(T::yield_type)> {
using type = typename T::yield_type;
};
template <typename T>
using yield_type_t = typename yield_type<T>::type;
template <typename Y, bool is_reference, bool move_value>
struct _holder_type { };
template <typename Y>
struct _holder_type<Y, false, false> {
// This struct holds a reference type and the internal value will not
// be moved when dereferenced.
Y value;
_holder_type() : value() {}
_holder_type(Y&& value_) : value(std::forward<Y>(value_)) { }
void emplace(Y&& value_) { value = std::forward<Y>(value_); }
Y get() { return value; }
};
template <typename Y>
struct _holder_type<Y, false, true> : _holder_type<Y, false, false> {
// This struct holds a reference type and the internal value WILL be
// moed when dereferenced.
using _holder_type<Y, false, false>::_holder_type;
// TODO: In debug mode, track if the value was already moved.
Y get() { return std::move(value); }
};
template <typename Y, bool move_value>
struct _holder_type<Y, true, move_value> {
static_assert(std::is_reference_v<Y>, "not a reference type");
std::add_pointer_t<std::remove_reference_t<Y>> value;
_holder_type() : value(nullptr) { }
_holder_type(Y value_) : value(value_) { }
void emplace(Y value_) { value = &value_; }
Y get() { assert(value != nullptr && "reference holder type holds a nullptr"); return *value; }
};
template <typename T, bool move_value>
using holder_type = _holder_type<T, std::is_reference_v<T>, move_value>;
} // namespace iterator
} // namespace detail
/**
* This class is an adapter of the C++ iterator interface for new style
* iterator classes. Note that you must not dereference this iterator
* more than once as it will move the value in `operator*()`!
* iterator classes. Note that if the *move_value* argument is #true,
* the iterator adapter must not be dereferenced more than once (unless
* #yield_type is a reference type, in which case the *move_value*
* argument doesn't matter).
*/
template <typename T>
template <typename T, bool move_value=true>
class iterator_adapter {
public:
using yield_type = typename detail::iterator::get_yield_type<T>::type;
using yield_type = typename detail::iterator::yield_type_t<T>;
using holder_type = detail::iterator::holder_type<yield_type, move_value>;
/**
* Passing nullptr to this constructor represents creates a sentinel
@ -64,15 +109,10 @@ namespace niklasrosenstein {
* first element. This constructor will call #has_next() and #next().
*/
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
_holder = std::make_shared<holder_type>();
_holder->emplace(itimpl->next());
}
}
@ -83,16 +123,11 @@ namespace niklasrosenstein {
inline iterator_adapter& operator ++ () {
assert(_itimpl != nullptr);
if (_itimpl->has_next()) {
_value = _itimpl->next();
#if NR_DEBUG
_has_value = true;
#endif
_holder->emplace(_itimpl->next());
}
else {
_itimpl = nullptr;
#if NR_DEBUG
_has_value = false;
#endif
_holder = nullptr;
}
return *this;
}
@ -103,19 +138,12 @@ namespace niklasrosenstein {
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);
return _holder->get();
}
private:
T* _itimpl;
yield_type _value;
#if NR_DEBUG
bool _has_value;
#endif
std::shared_ptr<holder_type> _holder;
};
/**
@ -131,7 +159,7 @@ namespace niklasrosenstein {
* to be accessed more than it would be by the expansion of the for loop as
* specified in the C++ standard.
*/
template <class T>
template <class T, bool move_value=true>
class iterator {
public:
@ -140,14 +168,13 @@ namespace niklasrosenstein {
* not really mark the begin of the iteration, rather the current state of
* the iterator.
*/
inline iterator_adapter<T> begin() { return iterator_adapter<T>(static_cast<T*>(this)); }
inline iterator_adapter<T, move_value> begin() { return iterator_adapter<T>(static_cast<T*>(this)); }
/**
* Returns the sentinel #iterator_adapter that marks the end of the
* iteration.
*/
inline iterator_adapter<T> end() const { return iterator_adapter<T>(nullptr); }
inline iterator_adapter<T, move_value> end() const { return iterator_adapter<T>(nullptr); }
};
/**

Loading…
Cancel
Save