diff --git a/src/lib/iter-adapter.hpp b/src/lib/iter-adapter.hpp index bb6908b5b..49f080846 100644 --- a/src/lib/iter-adapter.hpp +++ b/src/lib/iter-adapter.hpp @@ -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 const& il, PtrDerefIter 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 AddressExposingIter + : public lib::BoolCheckable > + { + 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 + bool operator== (AddressExposingIter const& il, AddressExposingIter const& ir) { return il.getBase() == ir.getBase(); } + + template + bool operator!= (AddressExposingIter const& il, AddressExposingIter const& ir) { return !(il == ir); } + + + + + + + /** wrapper to declare exposed values const */ + template + class ConstIter + : public lib::BoolCheckable > + { + 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 + bool operator== (ConstIter const& il, ConstIter const& ir) { return il.getBase() == ir.getBase(); } + + template + bool operator!= (ConstIter const& il, ConstIter const& ir) { return !(il == ir); } + + + } // namespace lib #endif diff --git a/src/lib/iter-explorer.hpp b/src/lib/iter-explorer.hpp index d9f1e79f9..a2b27527a 100644 --- a/src/lib/iter-explorer.hpp +++ b/src/lib/iter-explorer.hpp @@ -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 of values. 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::Ret ResultIter; - typedef typename SRC::value_type Val; + typedef typename SRC::value_type Val; // note: deliberately using the value typedef function Explorer; typedef _BUF_ Buffer; @@ -591,7 +599,8 @@ namespace lib { friend reference yield (RecursiveExhaustingEvaluation const& seq) { - return *(seq.feed()); + reference result = *(seq.feed()); + return result; } friend void diff --git a/tests/library/iter-adapter-test.cpp b/tests/library/iter-adapter-test.cpp index bc72817de..ac5ede2a7 100644 --- a/tests/library/iter-adapter-test.cpp +++ b/tests/library/iter-adapter-test.cpp @@ -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 numbz; + for (uint i=0; i < NUM_ELMS; ++i) + numbz.push_back(i); + + typedef vector::iterator RawIter; + typedef RangeIter Range; + typedef AddressExposingIter 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::const_iterator ConstRawIter; + typedef ConstIter ConstRange; + typedef AddressExposingIter 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