diff --git a/src/lib/iter-adapter.hpp b/src/lib/iter-adapter.hpp
index 1a6880963..6744ed23c 100644
--- a/src/lib/iter-adapter.hpp
+++ b/src/lib/iter-adapter.hpp
@@ -22,14 +22,46 @@
/** @file iter-adapter.hpp
** Helper template(s) for creating lumiera forward iterators.
- ** This denotes a concept similar to STL's "forward iterator", with
- ** the addition of an bool check to detect iteration end. The latter
- ** is inspired by the \c hasNext() function found in many current
- ** languages supporting iterators. In a similar vein (inspired from
- ** functional programming), we deliberately don't support the various
- ** extended iterator concepts from STL and boost (random access iterators,
- ** output iterators and the like). According to this concept, an iterator
- ** is a promise for pulling values -- and nothing beyond that.
+ ** Usually, these templates will be created and provided by a custom
+ ** container type and accessed by the client through a typedef name
+ ** "iterator" (similar to the usage within the STL). For more advanced
+ ** usage, the providing container might want to subclass these iterators,
+ ** e.g. to provide an additional, specialised API.
+ **
+ ** Depending on the concrete situation, there are several flavours
+ ** - the IterAdapter retains an active callback connection to the
+ ** controlling container, thus allowing arbitrary complex behaviour.
+ ** - the RangeIter allows just to expose a range of elements defined
+ ** by a STL-like pair of "start" and "end" iterators
+ ** - often, objects are managed internally by pointers, while allowing
+ ** the clients to use direct references; to support this usage scenario,
+ ** PtrDerefIter wraps an existing iterator, while dereferencing any value
+ ** automatically on access.
+ **
+ **
+ ** \par Lumiera forward iterator concept
+ **
+ ** Similar to the STL, instead of using a common "Iterator" base class,
+ ** instead we define a common set of functions and behaviour which can
+ ** be expected from any such iterator. These rules are similar to STL's
+ ** "forward iterator", with the addition of an bool check to detect
+ ** iteration end. The latter s inspired by the \c hasNext() function
+ ** found in many current languages supporting iterators. In a similar
+ ** vein (inspired from functional programming), we deliberately don't
+ ** support the various extended iterator concepts from STL and boost
+ ** (random access iterators, output iterators and the like). According
+ ** to this concept, an iterator is a promise for pulling values,
+ ** and nothing beyond that.
+ **
+ ** - Any Lumiera forward iterator can be in a "exhausted" (invalid) state,
+ ** which can be checked by the bool conversion. Especially, any instance
+ ** created by the default ctor is always fixed to that state. This
+ ** state is final and can't be reset, meaning that any iterator is
+ ** a disposable one-way-off object.
+ ** - iterators are copyable and comparable
+ ** - when an iterator is \em not in the exhausted state, it may be
+ ** \em dereferenced to yield the "current" value.
+ ** - moreover, iterators may be incremented until exhaustion.
**
** @todo WIP WIP WIP
** @todo see Ticket #182
@@ -45,20 +77,17 @@
#include "lib/error.hpp"
#include "lib/bool-checkable.hpp"
-#include
-
namespace lib {
- using boost::remove_pointer;
-
/**
* Adapter for building an implementation of the lumiera forward iterator concept.
* The "current position" is represented as an opaque element (usually an nested iterator),
- * with callbacks to the controlling container instance for managing this position.
+ * with callbacks to the controlling container instance for managing this position.
+ * This allows to influence and customise the iteration process to a large extent.
* Basically such an IterAdapter behaves like the similar concept from STL, but
* - it is not just a disguised pointer (meaning, it's more expensive)
* - it checks validity on every operation and may throw
@@ -86,7 +115,7 @@ namespace lib {
: source_(src)
, pos_(startpos)
{
- CON::iterValid(source_,pos_);
+ checkPos();
}
IterAdapter ()
@@ -115,7 +144,7 @@ namespace lib {
operator++()
{
_maybe_throw();
- CON::iterNext (source_,pos_);
+ iterate();
return *this;
}
@@ -124,14 +153,14 @@ namespace lib {
{
_maybe_throw();
IterAdapter oldPos(*this);
- CON::iterNext (source_,pos_);
+ iterate();
return oldPos;
}
bool
isValid () const
{
- return (source_ && CON::iterValid(source_,pos_));
+ return checkPos();
}
bool
@@ -141,6 +170,34 @@ namespace lib {
}
+ protected: /* === iteration control interface === */
+
+ /** ask the controlling container if this position is valid.
+ * @note this function is called before any operation,
+ * thus the container may adjust the position value,
+ * for example setting it to a "stop iteration" mark.
+ */
+ bool
+ checkPos() const
+ {
+ return source_ && CON::hasNext (source_,pos_);
+ }
+
+ /** ask the controlling container to yield the next position.
+ * The call is dispatched only if the current position is valid;
+ * any new position returned is again validated, so to detect
+ * the iteration end as soon as possible.
+ */
+ bool
+ iterate ()
+ {
+ if (!checkPos()) return false;
+
+ CON::iterNext (source_,pos_);
+ return checkPos();
+ }
+
+
private:
void
@@ -169,11 +226,136 @@ namespace lib {
- /** wrapper for an existing Iterator type,
- * automatically dereferencing the output of the former.
- * For this to work, the "source" iterator is expected
- * to be declared on \em pointers rather than on values.
- * @note bool checkable if and only if source is...
+ /**
+ * Accessing a STL element range through a Lumiera forward iterator,
+ * An instance of this iterator adapter is completely self-contained
+ * and allows to iterate once over the range of elements, until
+ * \c pos==end . Thus, a custom container may expose a range of
+ * elements of an embedded STL container, without controlling
+ * the details of the iteration (as is possible using the
+ * more generic IterAdapter).
+ */
+ template
+ class RangeIter
+ : public lib::BoolCheckable >
+ {
+ IT p_;
+ IT e_;
+
+ public:
+ typedef typename IT::pointer pointer;
+ typedef typename IT::reference reference;
+ typedef typename IT::value_type value_type;
+
+ RangeIter (IT const& start, IT const& end)
+ : p_(start)
+ , e_(end)
+ { }
+
+ RangeIter ()
+ : p_(0)
+ , e_(0)
+ { }
+
+
+ /* === lumiera forward iterator concept === */
+
+ reference
+ operator*() const
+ {
+ _maybe_throw();
+ return *p_;
+ }
+
+ pointer
+ operator->() const
+ {
+ _maybe_throw();
+ return &(*p_);
+ }
+
+ RangeIter&
+ operator++()
+ {
+ ++p_;
+ return *this;
+ }
+
+ RangeIter
+ operator++(int)
+ {
+ return RangeIter (p_++,e_);
+ }
+
+ bool
+ isValid () const
+ {
+ return (p_!= IT(0)) && (p_ != e_);
+ }
+
+ bool
+ empty () const
+ {
+ return !isValid();
+ }
+
+ private:
+
+ void
+ _maybe_throw() const
+ {
+ if (!isValid())
+ throw lumiera::error::Invalid ("Can't iterate further",
+ lumiera::error::LUMIERA_ERROR_ITER_EXHAUST);
+ }
+
+
+ /// comparison operator is allowed to access the underlying impl iterator
+ template
+ friend bool operator== (RangeIter const&, RangeIter const&);
+ };
+
+
+
+ /// Supporting equality comparisons...
+ template
+ bool operator== (RangeIter const& il, RangeIter const& ir) { return (!il && !ir) || (il.p_ == ir.p_); }
+
+ template
+ bool operator!= (RangeIter const& il, RangeIter const& ir) { return !(il == ir); }
+
+
+
+
+
+ namespace {
+
+ /** helper to remove pointer,
+ * while retaining const */
+ template
+ struct RemovePtr { typedef T Type; };
+
+ template
+ struct RemovePtr { typedef T Type; };
+
+ template
+ struct RemovePtr { typedef const T Type; };
+
+ template
+ struct RemovePtr { typedef const T Type; };
+
+ template
+ struct RemovePtr { typedef const T Type; };
+
+ }
+
+
+ /**
+ * wrapper for an existing Iterator type,
+ * automatically dereferencing the output of the former.
+ * For this to work, the "source" iterator is expected
+ * to be declared on \em pointers rather than on values.
+ * @note bool checkable if and only if source is...
*/
template
class PtrDerefIter
@@ -183,7 +365,7 @@ namespace lib {
public:
typedef typename IT::value_type pointer;
- typedef typename remove_pointer::type value_type;
+ typedef typename RemovePtr::Type value_type;
typedef value_type& reference;
diff --git a/tests/lib/iter-adapter-test.cpp b/tests/lib/iter-adapter-test.cpp
index d7935dd33..d49878f62 100644
--- a/tests/lib/iter-adapter-test.cpp
+++ b/tests/lib/iter-adapter-test.cpp
@@ -49,7 +49,14 @@ namespace test{
uint NUM_ELMS = 10;
-
+ /**
+ * Example of a more elaborate custom container exposing an iteration API.
+ * While the demo implementation here is based on pointers within a vector,
+ * we hand out a IterAdapter, which will call back when used by the client,
+ * thus allowing us to control the iteration process. Moreover, we provide
+ * a variant of this iterator, which automatically dereferences the pointers,
+ * thus yielding direct references for the client code to use.
+ */
class TestContainer
{
typedef vector _Vec;
@@ -58,6 +65,7 @@ namespace test{
static void killIt (int *it) { delete it; }
+
public:
TestContainer (uint count)
: numberz_(count)
@@ -70,19 +78,21 @@ namespace test{
{
for_each (numberz_, killIt);
}
-
+
+
+ /* ==== Exposing Iterator interface(s) for the clients ====== */
typedef IterAdapter<_Vec::iterator, TestContainer> iterator;
typedef IterAdapter<_Vec::const_iterator, TestContainer> const_iterator;
typedef PtrDerefIter ref_iterator;
typedef PtrDerefIter const_ref_iter;
-
-
+
+
iterator begin () { return iterator (this, numberz_.begin()); }
const_iterator begin () const { return const_iterator (this, numberz_.begin()); }
ref_iterator begin_ref () { return ref_iterator (begin()); }
const_ref_iter begin_ref () const { return const_ref_iter (begin()); }
-
+
iterator end () { return iterator(); }
const_iterator end () const { return const_iterator(); }
@@ -93,16 +103,12 @@ namespace test{
friend class IterAdapter<_Vec::const_iterator,TestContainer>;
- /** Implementation of Iteration-logic: pull next element.
- * Implicitly this includes a test for iteration end.
- */
+ /** Implementation of Iteration-logic: pull next element. */
template
static void
- iterNext (const TestContainer* src, ITER& pos)
+ iterNext (const TestContainer*, ITER& pos)
{
- if (iterValid(src,pos))
- ++pos;
- iterValid(src,pos);
+ ++pos;
}
/** Implementation of Iteration-logic: detect iteration end.
@@ -115,7 +121,7 @@ namespace test{
*/
template
static bool
- iterValid (const TestContainer* src, ITER& pos)
+ hasNext (const TestContainer* src, ITER& pos)
{
REQUIRE (src);
if ((pos != ITER(0)) && (pos != src->numberz_.end()))
@@ -124,17 +130,22 @@ namespace test{
{
pos = ITER(0);
return false;
- }
- }
+ } }
};
- }
+
+ } // (END) impl test dummy container
- /********************************************************************
- * @test create an iterator element for a given container and
- * verify its behaviour in accordance to the concept
- * "lumiera forward iterator"
+
+
+
+
+ /*********************************************************************
+ * @test set up example implementations based on the iterator-adapter
+ * templates and verify the behaviour in accordance to the
+ * concept "lumiera forward iterator"
+ *
* @todo see Ticket #182
*/
class IterAdapter_test : public Test
@@ -145,15 +156,44 @@ namespace test{
{
if (0 < arg.size()) NUM_ELMS = lexical_cast (arg[0]);
+ wrapIterRange ();
+
TestContainer testElms (NUM_ELMS);
simpleUsage (testElms);
iterTypeVariations (testElms);
+ verifyComparisons (testElms);
}
- static void showIt (int* elm) { cout << "::" << *elm; }
+ /** @test usage scenario, where we allow the client to
+ * access a range of elements given by STL iterators,
+ * without any specific iteration behaviour.
+ */
+ void
+ wrapIterRange ()
+ {
+ vector iVec (NUM_ELMS);
+ for (uint i=0; i < NUM_ELMS; ++i)
+ iVec[i] = i;
+
+ typedef vector::iterator I;
+ typedef RangeIter Range;
+
+ Range range (iVec.begin(), iVec.end());
+ ASSERT (!isnil (range) || !NUM_ELMS);
+
+ // now for example the client could....
+ while ( range )
+ cout << "::" << *range++;
+
+ cout << endl;
+ ASSERT (isnil (range));
+ ASSERT (range == Range());
+ }
+
+ /** @test use the IterAdapter as if it was a STL iterator */
void
simpleUsage (TestContainer& elms)
{
@@ -161,7 +201,12 @@ namespace test{
cout << endl;
}
+ static void showIt (int* elm) { cout << "::" << *elm; }
+
+
+ /** @test verify the const and dereferencing variants,
+ * which can be created based on IterAdapter */
void
iterTypeVariations (TestContainer& elms)
{
@@ -191,7 +236,7 @@ namespace test{
// note: the previous run indeed modified
// the element within the container.
- // --(**iter); // doesn't compile, because it's const ///////////////////////////////////TODO: duh! it *does* compile. why?
+ // ++(*iter); // doesn't compile, because it yields a "* const"
}
i = 0;
@@ -212,8 +257,17 @@ namespace test{
{
ASSERT (iter);
ASSERT ((*iter) == i);
+
+ // *iter = i+1; ///////////TODO this should be const, but it isn't
}
-
+ }
+
+
+
+ /** @test iterator comparison, predicates and operators */
+ void
+ verifyComparisons (TestContainer& elms)
+ {
TestContainer::ref_iterator rI (elms.begin_ref());
ASSERT (0 == *rI );
@@ -221,6 +275,19 @@ namespace test{
ASSERT (1 == *rI );
ASSERT (2 == *++rI);
+ TestContainer const& const_elms (elms);
+ TestContainer::const_ref_iter rI2 (const_elms.begin_ref());
+
+ ASSERT (rI2 != rI);
+ ASSERT (rI2 == elms.begin_ref());
+ ASSERT (rI2 == const_elms.begin_ref());
+
+ ++++rI2;
+
+ ASSERT (rI2 == rI);
+ ASSERT (rI2 != ++rI);
+ ASSERT (!isnil (rI2));
+
ASSERT (TestContainer::iterator() == elms.end());
ASSERT (!(TestContainer::iterator()));
ASSERT (!(elms.end()));