diff --git a/src/lib/iter-adapter-stl.hpp b/src/lib/iter-adapter-stl.hpp index 632f63bd7..75ad75682 100644 --- a/src/lib/iter-adapter-stl.hpp +++ b/src/lib/iter-adapter-stl.hpp @@ -26,7 +26,7 @@ ** repackaged as lumiera forward iterators. Mostly the purpose ** is ease of use, we don't create an abstraction barrier or try to ** hide implementation details. (see iter-source.hpp for such an - ** abstraction facility). As a benefit, these adapters can be + ** abstraction facility). As a benefit, these adapters can be ** considered low overhead. ** ** @see iter-adapter-stl-test.cpp @@ -50,7 +50,7 @@ namespace lib { namespace iter_stl { - /** + /** * helper baseclass to simplify * defining customised wrapped STL iterators */ @@ -117,6 +117,9 @@ namespace iter_stl { }; + + + namespace { // traits and helpers... template @@ -195,7 +198,8 @@ namespace iter_stl { typedef RangeIter Range; typedef DistinctIter DistinctVals; }; - } + + }//(End) traits/helpers @@ -230,13 +234,13 @@ namespace iter_stl { /** build a Lumiera Forward Iterator to suppress * any repetitions in the given sequence. - */ + */ template inline typename _SeqT::DistinctVals eachDistinct (SEQ& seq) { typedef typename _SeqT::Range Range; - typedef typename _SeqT::DistinctVals DistinctValues; + typedef typename _SeqT::DistinctVals DistinctValues; return DistinctValues (Range (seq.begin(), seq.end())); } @@ -272,5 +276,126 @@ namespace iter_stl { } + + + /** + * materialised iterator contents. + * At construction, the given source iterator + * is immediately discharged into an internal buffer (vector). + * This captured value sequence can be retrieved once as + * Lumiera Forward Iterator + */ + template + class IterSnapshot + : public lib::BoolCheckable > + { + typedef std::vector Sequence; + + mutable + Sequence buffer_; + size_t pos_; + + + public: + /** create empty snapshot */ + IterSnapshot() + : buffer_() + , pos_(0) + { } + + /** take snapshot by discharging a copy + * of the given Lumiera Forward iterator + * @warning depending on the implementation + * backing the source iterator, this + * might or might not yield side-effects. + */ + template + IterSnapshot (IT const& src) + : buffer_() + , pos_(0) + { + for (IT copy(src); copy; ++copy) + buffer_.push_back(*copy); + } + + /** take snapshot from STL iterator */ + template + IterSnapshot (IT const& begin, IT const& end) + : buffer_() + , pos_(0) + { + for (IT p(begin); p!=end; ++p) + buffer_.push_back(*p); + } + + + + /* === lumiera forward iterator concept === */ + + typedef VAL* pointer; + typedef VAL& reference; + typedef VAL value_type; + + reference + operator*() const + { + _maybe_throw(); + return buffer_[pos_]; + } + + pointer + operator->() const + { + _maybe_throw(); + return &buffer_[pos_]; + } + + IterSnapshot& + operator++() + { + _maybe_throw(); + ++pos_; + return *this; + } + + bool + isValid () const + { + return pos_ < buffer_.size(); + } + + bool + empty () const + { + return !isValid(); + } + + + /** equality is based both on the actual contents of the snapshots + * and the current iterator position */ + friend bool + operator== (IterSnapshot const& snap1, IterSnapshot const& snap2) + { + return snap1.buffer_ == snap2.buffer_ + && snap1.pos_ == snap2.pos_ ; + } + + friend bool + operator!= (IterSnapshot const& snap1, IterSnapshot const& snap2) + { + return ! (snap1 == snap2); + } + + + private: + void + _maybe_throw() const + { + if (!isValid()) + _throwIterExhausted(); + } + }; + + }} // namespace lib::iter_stl #endif diff --git a/tests/40components.tests b/tests/40components.tests index 5911f945c..113d065f8 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -345,7 +345,33 @@ out: ::0::1::2::3::4::5::6::7::8::9::10::11::12::13::14::15::16::17::18::19 END -PLANNED "Lumiera Iterator Adapter for STL" IterAdapterSTL_test <() << endl; + namespace { + uint NUM_ELMS = 10; + } + + @@ -76,11 +71,14 @@ namespace test{ /************************************************************************ - * @test provide test STL containers to verify some of the adapters - * to expose typical container usage patterns as lumiera iterators. - * - keys and values of a map + * @test verify some of the adapters to expose typical container + * usage patterns as Lumiera Forward Iterators. + * - distinct values from a sequence + * - keys and values of a map / multimap + * - distinct keys from a multimap * - multimap values associated with a given key - * + * - snapshot of an iterator, to be retrieved as iterator + * * @see RangeIter * @see iter-adapter.hpp * @see iter-adapter-stl.hpp @@ -103,6 +101,8 @@ namespace test{ iterateValues4Key (getTestMultiMap_int (NUM_ELMS)); iterateValues4Key (getTestMultiMap_int (NUM_ELMS)); + + checkIteratorSnapshot(); } @@ -127,7 +127,7 @@ namespace test{ TEST_ITER (iter::eachValForKey, (mumap, 0)); // non-existing key should yield empty iterator - CHECK (! iter::eachValForKey (mumap, NUM_ELMS)); + CHECK (! iter::eachValForKey (mumap, NUM_ELMS)); } @@ -157,10 +157,85 @@ namespace test{ vec.push_back (1); TEST_ITER(iter::eachDistinct, (vec)); } + + + void + checkIteratorSnapshot() + { + typedef VecI::iterator Iter; + typedef RangeIter Range; + typedef iter::IterSnapshot Snapshot; + + VecI vec = getTestSeq_int (NUM_ELMS); + Snapshot capture1 (vec.begin(), vec.end()); + + Range range_of_all (vec.begin(), vec.end()); + Snapshot capture2 (range_of_all); + CHECK (range_of_all); // snapshot doesn't affect given source iterator pos + CHECK (capture2); + + CHECK (vec.begin() == range_of_all.getPos()); + CHECK (vec.end() == range_of_all.getEnd()); + CHECK (!isnil (vec)); + + // concurrent or intermittent modification + vec.clear(); + CHECK (isnil (vec)); + CHECK (vec.end() != range_of_all.getEnd()); // range_of_all is now corrupted + + CHECK (capture1); // ...but the snapshots remain unaffected + CHECK (capture2); + CHECK (capture1 == capture2); // can compare snapshots, based on actual contents + + vec.push_back(22); + vec.push_back(44); + Snapshot capture3 (vec.begin(), vec.end()); // take another snapshot from current contents + CHECK (capture3); + CHECK (capture3 != capture1); + CHECK (capture3 != capture2); + + + uint sum_should_be = (NUM_ELMS-1)*NUM_ELMS/2; + + CHECK (sum_should_be == sumAll (capture1)); + CHECK (!capture1); // this one is exhausted now + CHECK ( capture2); // ...but the others are really independent + CHECK ( capture3); + CHECK (capture1 != capture2); // comparison includes the current position + + CHECK (sum_should_be == sumAll (capture2)); + CHECK (!capture1); + CHECK (!capture2); + CHECK ( capture3); + CHECK (capture1 == capture2); // now again equal (both exhausted and equal contents) + + CHECK (22+44 == sumAll (capture3)); + CHECK (!capture1); + CHECK (!capture2); + CHECK (!capture3); + CHECK (capture1 == capture2); + CHECK (capture3 != capture1); // all exhausted, but the difference in contents remains + CHECK (capture3 != capture2); + } + + template + uint + sumAll (IT& it) + { + uint sum(0); + cout << "snapshot"; + while (it) + { + cout << "-" << *it; + sum += *it; + ++it; + } + cout << endl; + return sum; + } }; LAUNCHER (IterAdapterSTL_test, "unit common"); }} // namespace lib::test -