From 2c330003468d350cb0cdbe96f4cc17fd50b1f0e3 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 25 Jun 2012 05:00:56 +0200 Subject: [PATCH] complement IterStack by a similar wrapper for queue-like access --- src/lib/iter-stack.hpp | 159 ++++++++++++++++++++++++++++++++-- tests/40components.tests | 4 + tests/lib/iter-queue-test.cpp | 115 ++++++++++++++++++++++++ tests/lib/iter-stack-test.cpp | 2 +- 4 files changed, 272 insertions(+), 8 deletions(-) create mode 100644 tests/lib/iter-queue-test.cpp diff --git a/src/lib/iter-stack.hpp b/src/lib/iter-stack.hpp index 7370947e4..29f20b8a0 100644 --- a/src/lib/iter-stack.hpp +++ b/src/lib/iter-stack.hpp @@ -20,6 +20,28 @@ */ +/** @file iter-stack.hpp + ** Conveniently iterable stack and queue containers. + ** Implemented as thin wrapper on top of std::deque + ** In addition to providing a simplified interface, these wrappers comply + ** to the "Lumiera Forward Iterator" concept, allowing to retrieve and discharge the + ** contents of the container in a standard fashion, especially by using util::for_each, + ** or by feeding them into a iterator based pipeline. + ** + ** @remarks contrary to the usual (STL) containers, this wrapper \em is an iterator + ** and a container at the same time. Which opens the interesting possibility + ** to intermix retrieval and feeding of new elements. It can be seen as a + ** zero overhead adapter to ease the use within Lumiera's library framework. + ** @note EX_STRONG, since std::deque gives this guarantee for push and pop operations + ** @warning STL containers aren't thread safe. + ** + ** @see IterStack_test + ** @see IterQueue_test + ** @see iter-adapter.hpp + ** @see itertools.hpp + ** @see IterSource (completely opaque iterator) + ** + */ #ifndef LIB_ITER_STACK_H @@ -39,9 +61,10 @@ namespace lib { namespace { using util::unConst; - /** - * Wrapper to mark a std::stack instance for use - * as "state core" within lib::IterStateWrapper + /** + * Wrapper to mark a std::deque instance for use + * as "state core" within lib::IterStateWrapper. + * Iteration will pop elements from the back side. */ template struct IterDequeStorage @@ -75,7 +98,8 @@ namespace lib { /** * A Stack which can be popped by iterating. - * This is a simple helper built on top of std::stack. + * This is a simple helper built by wrapping up + * STL's double ended queue (deque). * Thus, each instance holds the full state, which is * actually kept in heap allocated storage. Pushing of * new elements and iterator use may be mixed. @@ -91,22 +115,143 @@ namespace lib { // using default create and copy operations - void + IterStack& push (TY const& elm) { this->stateCore().push_back (elm); + return *this; } TY pop() { this->__throw_if_empty(); - TY frontElement (this->stateCore().back()); + TY topElement (this->stateCore().back()); this->stateCore().pop_back(); - return frontElement; + return topElement; } }; + + + /** + * A Queue which can be pulled by iterating. + * As a variation of the IterStack frontend, here the + * exposed additional interface works like a queue: + * New elements can be fed, and the iteration will + * consume the contents first-in first-out. The + * implementation is based on std::deque, providing + * both feed and the iterating operation in amortised + * constant time, using chunk wise heap allocations. + * Feeding of new elements into the queue and + * retrieving old elements by iteration + * may be mixed freely. + */ + template + struct IterQueue + : IterStateWrapper > + { + + // using default create and copy operations + + IterQueue& + feed (TY const& elm) + { + this->stateCore().push_front (elm); + return *this; + } + + TY + pop() + { + this->__throw_if_empty(); + TY firstElement (this->stateCore().back()); + this->stateCore().pop_back(); + return firstElement; + } + + + /** + * Adapter for use as opaque sequence. + * This builder exposes generic operations + * to prepare and pre-fill a sequence + */ + struct Builder + { + Builder() { } + Builder(IterQueue const& initialElements) + : queue_(initialElements) + { } + // standard copy operations allowed + + template + IterQueue + usingSequence (IT src) + { + for ( ; src; ++src ) + queue_.feed (*src); + return queue_; + } + + private: + IterQueue queue_; + }; + + /** Extension point to be picked up by ADL. + * The exposed Builder object allows client code + * to prepare and pre-fill the queue, without + * being tied to the implementation type + * @param initial the initial contents of the queue + * to start with, maybe empty. This parameter + * also acts as type tag to pick this \c build() + * function by ADL + */ + friend IterQueue::Builder + build (IterQueue const& initial) + { + return Builder(initial); + } + }; + + + /** convenience free function to build an iterable sequence */ + template + inline IterQueue + elements (T const& elm) + { + return IterQueue().feed(elm); + } + + template + inline IterQueue + elements (T const& e0, T const& e1) + { + return IterQueue().feed(e0).feed(e1); + } + + template + inline IterQueue + elements (T const& e0, T const& e1, T const& e2) + { + return IterQueue().feed(e0).feed(e1).feed(e2); + } + + template + inline IterQueue + elements (T const& e0, T const& e1, T const& e2, T const& e3) + { + return IterQueue().feed(e0).feed(e1).feed(e2).feed(e3); + } + + template + inline IterQueue + elements (T const& e0, T const& e1, T const& e2, T const& e3, T const& e4) + { + return IterQueue().feed(e0).feed(e1).feed(e2).feed(e3).feed(e4); + } + + + } // namespace lib #endif diff --git a/tests/40components.tests b/tests/40components.tests index 3b901d8d3..856084940 100644 --- a/tests/40components.tests +++ b/tests/40components.tests @@ -512,6 +512,10 @@ TEST "Stack-like iterator" IterStack_test END +TEST "Queue-like iterator" IterQueue_test +END + + TEST "Iterable type detection" IterableClassification_test < : Yes out: can_STL_ForEach : Yes diff --git a/tests/lib/iter-queue-test.cpp b/tests/lib/iter-queue-test.cpp new file mode 100644 index 000000000..157a8b2c8 --- /dev/null +++ b/tests/lib/iter-queue-test.cpp @@ -0,0 +1,115 @@ +/* + IterQueue(Test) - verify queue-like iterator and builder function + + Copyright (C) Lumiera.org + 2012, Hermann Vosseler + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + + + +#include "lib/test/run.hpp" +#include "lib/test/test-helper.hpp" +#include "lib/util.hpp" + +#include "lib/iter-stack.hpp" + + + +namespace lib { +namespace test{ + + using ::Test; + using util::isnil; + using lumiera::error::LUMIERA_ERROR_ITER_EXHAUST; + + + + + + + /********************************************************************* + * @test cover an easy to use queue, built as convenience wrapper + * on top of \c std::deque and allowing element retrieval + * by iteration. + * - iterable according to the Lumiera Forward Iterator concept + * - can enqueue and dequeue elements repeatedly + * - can be loaded through an generic builder API. + * + * @see IterExplorer + * @see IterAdapter + */ + class IterQueue_test : public Test + { + + virtual void + run (Arg) + { + IterQueue queue; + CHECK (isnil (queue)); + + VERIFY_ERROR (ITER_EXHAUST, *queue ); + VERIFY_ERROR (ITER_EXHAUST, ++queue ); + + queue.feed (1); + queue.feed (3); + queue.feed (5); + + CHECK (!isnil (queue)); + CHECK (1 == *queue); + + ++queue; + CHECK (3 == *queue); + + CHECK (3 == queue.pop()); + CHECK (5 == *queue); + + ++queue; + CHECK (isnil (queue)); + VERIFY_ERROR (ITER_EXHAUST, *queue ); + VERIFY_ERROR (ITER_EXHAUST, ++queue ); + VERIFY_ERROR (ITER_EXHAUST, queue.pop() ); + + + // use the generic builder API to feed + // the contents of another iterator into the queue + queue = build(queue).usingSequence (elements (23,45)); + + int i = queue.pop(); + CHECK (i == 23); + CHECK (45 == *queue); + + // feeding new elements and pulling / iteration can be mixed + queue.feed(67); + CHECK (45 == *queue); + ++queue; + CHECK (67 == *queue); + ++queue; + CHECK (isnil (queue)); + queue.feed(89); + CHECK (89 == *queue); + queue.pop(); + VERIFY_ERROR (ITER_EXHAUST, *queue ); + } + + }; + + + LAUNCHER (IterQueue_test, "unit common"); + + +}} // namespace lib::test diff --git a/tests/lib/iter-stack-test.cpp b/tests/lib/iter-stack-test.cpp index 82d93b4b5..9802b84f8 100644 --- a/tests/lib/iter-stack-test.cpp +++ b/tests/lib/iter-stack-test.cpp @@ -47,7 +47,7 @@ namespace test{ /********************************************************************* * @test cover behaviour of a convenience class built by using a - * std::stack as "state core" of an Lumiera IterStateWrapper. + * \c std::deque as "state core" of an Lumiera IterStateWrapper. * - iterable according to the Lumiera Forward Iterator concept * - can push and pop values repeatedly * - iterating and push / pop can be mixed