Library: iterator wrapper to expose the address
...for the very specific situation when we want to explore an existing data structure, and the exploration assumes value semantics. The workaround then is to use pointers as values.
This commit is contained in:
parent
4a7b4b0a8d
commit
5749a6216c
3 changed files with 224 additions and 2 deletions
|
|
@ -41,6 +41,11 @@
|
|||
** the clients to use direct references; to support this usage scenario,
|
||||
** PtrDerefIter wraps an existing iterator, while dereferencing any value
|
||||
** automatically on access.
|
||||
** - for some (very specific) usage situations we intend to explore the
|
||||
** contents of a stable and unmodifiable data structure through pointers.
|
||||
** The AddressExposingIter wraps another Lumiera Forward Iterator and
|
||||
** exposes addresses -- assuming the used source iterator is exposing
|
||||
** references to pre-existing storage locations (not temporaries).
|
||||
**
|
||||
** There are many further ways of yielding a Lumiera forward iterator.
|
||||
** For example, lib::IterSource builds a "iterable" source of data elements,
|
||||
|
|
@ -686,5 +691,169 @@ namespace lib {
|
|||
bool operator!= (PtrDerefIter<I1> const& il, PtrDerefIter<I2> const& ir) { return !(il == ir); }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* wrapper for an existing Iterator type to expose the address of each value yielded.
|
||||
* Typically this can be used to build visitation sequences based on values living
|
||||
* within a stable data structure (e.g. unmodifiable STL vector)
|
||||
* @warning use of this wrapper might lead to taking the address of temporaries.
|
||||
* The continued existence of the exposed storage locations must be guaranteed.
|
||||
* @note bool checkable if and only if source is...
|
||||
*/
|
||||
template<class IT>
|
||||
class AddressExposingIter
|
||||
: public lib::BoolCheckable<AddressExposingIter<IT> >
|
||||
{
|
||||
IT i_; ///< nested source iterator
|
||||
|
||||
|
||||
public:
|
||||
typedef typename IT::value_type * value_type;
|
||||
typedef typename IT::value_type ** pointer;
|
||||
typedef typename IT::value_type *& reference;
|
||||
|
||||
|
||||
|
||||
/** AddressExposingIter is always created
|
||||
* by wrapping an existing iterator.
|
||||
*/
|
||||
explicit
|
||||
AddressExposingIter (IT srcIter)
|
||||
: i_(srcIter)
|
||||
{ }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/* === lumiera forward iterator concept === */
|
||||
|
||||
value_type
|
||||
operator*() const
|
||||
{
|
||||
return &(*i_);
|
||||
}
|
||||
|
||||
typename IT::pointer
|
||||
operator->() const
|
||||
{
|
||||
return i_.operator->();
|
||||
}
|
||||
|
||||
AddressExposingIter&
|
||||
operator++()
|
||||
{
|
||||
++i_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool
|
||||
isValid () const
|
||||
{
|
||||
return bool(i_);
|
||||
}
|
||||
|
||||
bool
|
||||
empty () const
|
||||
{
|
||||
return !isValid();
|
||||
}
|
||||
|
||||
|
||||
/** access the wrapped implementation iterator */
|
||||
IT const&
|
||||
getBase() const
|
||||
{
|
||||
return i_;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/// Supporting equality comparisons...
|
||||
template<class I1, class I2>
|
||||
bool operator== (AddressExposingIter<I1> const& il, AddressExposingIter<I2> const& ir) { return il.getBase() == ir.getBase(); }
|
||||
|
||||
template<class I1, class I2>
|
||||
bool operator!= (AddressExposingIter<I1> const& il, AddressExposingIter<I2> const& ir) { return !(il == ir); }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/** wrapper to declare exposed values const */
|
||||
template<class IT>
|
||||
class ConstIter
|
||||
: public lib::BoolCheckable<ConstIter<IT> >
|
||||
{
|
||||
IT i_; ///< nested source iterator
|
||||
|
||||
|
||||
public:
|
||||
typedef const typename IT::value_type value_type;
|
||||
typedef const typename IT::pointer pointer;
|
||||
typedef const typename IT::reference reference;
|
||||
|
||||
ConstIter (IT srcIter)
|
||||
: i_(srcIter)
|
||||
{ }
|
||||
|
||||
|
||||
|
||||
|
||||
/* === lumiera forward iterator concept === */
|
||||
|
||||
reference
|
||||
operator*() const
|
||||
{
|
||||
return *i_;
|
||||
}
|
||||
|
||||
pointer
|
||||
operator->() const
|
||||
{
|
||||
return i_.operator->();
|
||||
}
|
||||
|
||||
ConstIter&
|
||||
operator++()
|
||||
{
|
||||
++i_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool
|
||||
isValid () const
|
||||
{
|
||||
return bool(i_);
|
||||
}
|
||||
|
||||
bool
|
||||
empty () const
|
||||
{
|
||||
return !isValid();
|
||||
}
|
||||
|
||||
|
||||
/** access the wrapped implementation iterator */
|
||||
IT const&
|
||||
getBase() const
|
||||
{
|
||||
return i_;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/// Supporting equality comparisons...
|
||||
template<class I1, class I2>
|
||||
bool operator== (ConstIter<I1> const& il, ConstIter<I2> const& ir) { return il.getBase() == ir.getBase(); }
|
||||
|
||||
template<class I1, class I2>
|
||||
bool operator!= (ConstIter<I1> const& il, ConstIter<I2> const& ir) { return !(il == ir); }
|
||||
|
||||
|
||||
|
||||
} // namespace lib
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -155,6 +155,14 @@ namespace lib {
|
|||
* independent, new result sequence based on this first element).
|
||||
* Afterwards, the source is \em advanced and then \em copied
|
||||
* into the result iterator.
|
||||
* @warning IterExplorer and all the provided combination strategies are deliberately
|
||||
* designed to work on sequences <b>of values</b>. These values will indeed
|
||||
* be \em copied for invocation of the exploration function. The rationale
|
||||
* for this choice is data locality and the danger of dangling references
|
||||
* to an implicitly created temporary (since in it is so common practice
|
||||
* in C++ to use \c const& ). Thus, if you need to work on references
|
||||
* to a common data structure, you need to use either pointers or
|
||||
* some reference wrapper explicitly as value type right from start.
|
||||
* @param SRC the source sequence or iterator to wrap
|
||||
* @param _COM_ "Combinator" strategy template. When binding (\c >>= ) a function,
|
||||
* an instance of that strategy becomes the new SRC for the created new
|
||||
|
|
@ -514,7 +522,7 @@ namespace lib {
|
|||
class RecursiveExhaustingEvaluation
|
||||
{
|
||||
typedef typename _Fun<FUN>::Ret ResultIter;
|
||||
typedef typename SRC::value_type Val;
|
||||
typedef typename SRC::value_type Val; // note: deliberately using the value
|
||||
typedef function<ResultIter(Val)> Explorer;
|
||||
typedef _BUF_<ResultIter> Buffer;
|
||||
|
||||
|
|
@ -591,7 +599,8 @@ namespace lib {
|
|||
friend reference
|
||||
yield (RecursiveExhaustingEvaluation const& seq)
|
||||
{
|
||||
return *(seq.feed());
|
||||
reference result = *(seq.feed());
|
||||
return result;
|
||||
}
|
||||
|
||||
friend void
|
||||
|
|
|
|||
|
|
@ -194,6 +194,7 @@ namespace test{
|
|||
|
||||
iterTypeVariations (testElms);
|
||||
verifyComparisons (testElms);
|
||||
exposeDataAddresses();
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -313,6 +314,49 @@ namespace test{
|
|||
}
|
||||
|
||||
|
||||
/** @test build an iterator to expose
|
||||
* the address of underlying data elements */
|
||||
void
|
||||
exposeDataAddresses()
|
||||
{
|
||||
vector<int> numbz;
|
||||
for (uint i=0; i < NUM_ELMS; ++i)
|
||||
numbz.push_back(i);
|
||||
|
||||
typedef vector<int>::iterator RawIter;
|
||||
typedef RangeIter<RawIter> Range;
|
||||
typedef AddressExposingIter<Range> AddrIter;
|
||||
|
||||
AddrIter ii(Range(numbz.begin(), numbz.end()));
|
||||
for (uint i=0; i < numbz.size(); ++i)
|
||||
{
|
||||
CHECK (ii);
|
||||
int* p = *ii;
|
||||
CHECK (p == & numbz[i]);
|
||||
++ii;
|
||||
}
|
||||
CHECK (!ii);
|
||||
|
||||
// building a const iterator needs to be done in a somewhat weird way;
|
||||
// since we're exposing the pointer as value, the solution is to add
|
||||
// the const on the immediately wrapped iterator type
|
||||
typedef vector<int>::const_iterator ConstRawIter;
|
||||
typedef ConstIter<Range> ConstRange;
|
||||
typedef AddressExposingIter<ConstRange> ConstAddrIter;
|
||||
|
||||
ConstAddrIter iic(ConstRange(Range(numbz.begin(), numbz.end())));
|
||||
for (uint i=0; i < numbz.size(); ++i)
|
||||
{
|
||||
CHECK (iic);
|
||||
const int* p = *iic;
|
||||
CHECK (p == & numbz[i]);
|
||||
++iic;
|
||||
}
|
||||
CHECK (!iic);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/** @test iterator comparison, predicates and operators */
|
||||
void
|
||||
|
|
|
|||
Loading…
Reference in a new issue