From 5749a6216cc8e2b62cc15e59e9392f4d2fde73e6 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 8 Apr 2013 02:03:43 +0200 Subject: [PATCH] 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. --- src/lib/iter-adapter.hpp | 169 ++++++++++++++++++++++++++++ src/lib/iter-explorer.hpp | 13 ++- tests/library/iter-adapter-test.cpp | 44 ++++++++ 3 files changed, 224 insertions(+), 2 deletions(-) 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