compilation-bundle/dwarf-compilation.base/contrib/libsrk31cxx/include/srk31/selective_iterator.hpp

261 lines
9.5 KiB
C++

#ifndef SRK31_SELECTIVE_ITERATOR_HPP_
#define SRK31_SELECTIVE_ITERATOR_HPP_
#include <iterator>
#include <iostream>
#include <cassert>
/* Most of the time, you should use boost::filter_iterator instead.
* But this class applies the predicate to the underlying iterator,
* not to the object yielded by dereferencing it. So this variant is
* useful if there is some nontrivial cost to dereferencing the iterator,
* and the predicate can be evaluated on the iterator without dereferencing.
*
* Another benefit we have is that we provide the core of the iterator as
* a CRTP mixin. The idea here is to avoid boost:: iterators' need for base()
* calls if you want to access methods on the underlying iterator.
*
* To get iterator class J backed by iterators of class I,
* rather than creating a completely new iterator class with an encapsulated
* J, like this:
*
* typedef selective_iterator<Pred, I> J
*
* ... we derive a new class
*
* struct J : I, selective_iterator_mixin<Pred, I, J> {};
*
* such that J is both an I and a selective I.
*
* Then there's no need to call base(). The utility methods on I are available
* directly, but the relevant behaviour is overloaded in the mixin.
*
* PROBLEM: do we want to leave room for impls where the base iterator
* instance is actually not "this" but a contained field? Hmm. NO!
*
* PROBLEM: operator overload ambiguity: how to tell the compiler
* that the selective_iterator_mixin's definitions take precedence
* over I's? */
namespace srk31
{
template <class Pred, class Iter, class MixerIn>
struct selective_iterator_mixin
: public std::iterator_traits<Iter> // borrow container's typedefs
{
bool have_begin;
/* const */ Iter m_begin;
/* const */ Iter m_end;
Pred m_pred;
typedef selective_iterator_mixin<Pred, Iter, MixerIn> self;
typedef MixerIn super;
/* We don't have m_iter. The idea is that "*this",
* appropriately static_cast'd, is m_iter.
*
* This has some surprising consequences. Although we
* want to define various operators and constructors
* here, so that the mixer-in doesn't have to (it can just
* do "using" them), it is confusing in some cases.
* Constructors, including copy and move constructors,
* become responsible for initialising/copying/moving the
* *superobject*'s fields. I *think* this works.... */
const Iter& iter() const { return *static_cast<const MixerIn *>(this); }
Iter& iter() { return *static_cast< MixerIn *>(this); }
void print_range()
{
// std::cerr << "selective_iterator_mixin selecting over range: ";
// unsigned count = 0;
// for (Iter i = this->m_begin; i != this->m_end; i++)
// {
// //std::cerr << *i;
// //std::cerr << " ";
// ++count;
// }
// std::cerr << "of " << count << " elements" << std::endl;
}
// how to support non-copying? get rid of "begin"?
// Then we are "forward_selective_iterator".
// We could just tolerate the copy of the first iterator of every subseq.
// In that case I've already solved it above: just use the fully-specifying constructor.
// BUT we want to support the forward-only case.
// No termination check.
/* These constructors do not match copy/move signatures,
* so we are okay to initialise iter() here (cf. see below for copy/move).
* We are effectively writing over the top of a default-constructed
* subobject. Be careful with the rvalue refs, though. */
// fully specifying constructor
selective_iterator_mixin(Iter&& begin, Iter&& end, Iter&& val, const Pred& pred = Pred())
: m_begin(std::move(begin)), m_end(std::move(end)), m_pred(pred)
{
//std::cerr << "Three-arg moving constructor called for " << this << std::endl;
iter() = std::move(val);
assert(static_cast<Iter&>(*this) == this->m_end
|| this->m_pred(static_cast<Iter&>(*this)));
print_range();
}
// fully specifying constructor
selective_iterator_mixin(const Iter& begin, const Iter& end, const Iter& val, const Pred& pred = Pred())
: m_begin(begin), m_end(end), m_pred(pred)
{
//std::cerr << "Three-arg non-moving constructor called for " << this << std::endl;
iter() = val;
assert(static_cast<Iter&>(*this) == this->m_end
|| this->m_pred(static_cast<Iter&>(*this)));
print_range();
}
// begin/end only -- COPIES BOTH
selective_iterator_mixin(const Iter& begin, const Iter& end, const Pred& pred = Pred())
: have_begin(true), m_begin(begin), m_end(end), m_pred(pred)
{
//std::cerr << "Two-arg non-moving constructor called for " << this << std::endl;
iter() = begin;
while (iter() != this->m_end
&& !this->m_pred(iter()))
{
//std::cerr << "Advancing (1) over one element because pred says no" << std::endl;
++iter();
}
print_range();
}
// begin/end only -- MOVES BOTH
selective_iterator_mixin(Iter&& begin, Iter&& end, const Pred& pred = Pred())
: have_begin(true), m_begin(std::move(begin)), m_end(std::move(end)), m_pred(pred)
{
//std::cerr << "Two-arg moving constructor called for " << this << std::endl;
iter() = m_begin;
while (static_cast<MixerIn&>(*this) != this->m_end
&& !this->m_pred(static_cast<MixerIn&>(*this)))
{
//std::cerr << "Advancing (2) over one element because pred says no" << std::endl;
++iter();
}
print_range();
}
/* Copy and move constructors.
* CARE. These are not as simple as they seem.
* We want them to be inheritable by the mixer-in.
* BUT what does constructor inheritance mean, in a
* multiple-inheritance scenario? Which constructors
* are called for the other bases?
* I think that copy and move constructors are not actually
* inherited, but that a defaulted version is generated
* which copy/move-constructs each inherited subobject
* separately.
* The upshot is that we should *not* assign to iter() here.
* It will get done *before* we are called.
* This is particularly an issue with move semantics: the
* iterator subobject will already have been moved out of
* the argument object, so we should not use that subobject
* in the argument (it will be zeroed).
*
* What about copy/move-assignment operators?
* I *think* they are treated the same way.
*/
// copy constructor
selective_iterator_mixin(const self& arg)
: have_begin(arg.have_begin), m_begin(arg.m_begin), m_end(arg.m_end),
m_pred(arg.m_pred)
{
// print_range();
}
// move constructor
selective_iterator_mixin(self&& arg)
: have_begin(std::move(arg.have_begin)),
m_begin(std::move(arg.m_begin)),
m_end(std::move(arg.m_end)),
m_pred(arg.m_pred)
{
// print_range();
}
// assignment
self& operator=(const self& arg)
{
//assert(arg.m_begin == this->m_begin);
//assert(arg.m_end == this->m_end);
this->m_begin = arg.m_begin;
this->m_end = arg.m_end;
this->m_pred = arg.m_pred;
this->have_begin = arg.have_begin;
//iter() = arg.iter(); // the mixer-in takes care of this by using default operator=
print_range();
return *this;
}
self& operator=(self&& arg) // move assignment
{
//assert(arg.m_begin == this->m_begin);
//assert(arg.m_end == this->m_end);
this->m_begin = std::move(arg.m_begin);
this->m_end = std::move(arg.m_end);
this->m_pred = std::move(arg.m_pred);
this->have_begin = std::move(arg.have_begin);
//iter() = std::move(arg.iter()); // the mixer-in takes care of this by using default operator=
print_range();
return *this;
}
typename std::iterator_traits<Iter>::reference operator*() const { return *iter(); }
typename std::iterator_traits<Iter>::pointer operator->() const { return &*iter(); }
self& operator++() // prefix
{ do { ++iter(); } while (iter() != m_end && !m_pred(iter()));
return *this; }
self operator++(int) // postfix ++, so copying
{ Iter tmp = iter(); ++*this; // re-use prefix form
// return tmp
return self(m_begin, m_end, tmp, m_pred); }
self& operator--() // prefix --
{ // our "--" is a bit broken if we didn't copy the BEGIN iterator
do { --iter(); } while ((!have_begin || (iter() != m_begin)) && !m_pred(iter()));
// if we reached the beginning and pred is still false,
// this means undefined behaviour is okay (the user shouldn't have
// called us) so stop at one memory location *beyond* begin
if ((!have_begin || iter() == m_begin) && !m_pred(iter())) --iter();
return *this; }
self operator--(int) // postfix, so copying
{ Iter tmp = iter(); --*this;
return self(m_begin, m_end, tmp, m_pred); }
bool operator==(const self& arg) const { return iter() == arg.iter(); }
bool operator!=(const self& arg) const { return iter() != arg.iter(); }
};
template <class Pred, class Iter>
class selective_iterator
: public Iter
, public selective_iterator_mixin<Pred, Iter, selective_iterator<Pred, Iter> >
{
//Iter m_iter;
typedef selective_iterator<Pred, Iter> self;
typedef selective_iterator_mixin<Pred, Iter, self> super;
public:
// no default constructor -- we rely on Iter being default-constructable
using super::selective_iterator_mixin;
using super::operator++;
using super::operator--;
//usine super::operator=;
using super::operator!=;
using super::operator==;
using super::operator*;
using super::operator->;
//using Iter::reference;
typedef typename Iter::reference reference;
typedef typename Iter::difference_type difference_type;
typedef typename Iter::value_type value_type;
typedef typename Iter::pointer pointer;
typedef typename Iter::iterator_category iterator_category;
};
} // end namespace srk31
#endif