/* ITER-EXPLORER.hpp - building blocks for iterator evaluation strategies 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. */ /** @file iter-explorer.hpp ** Helper template(s) for establishing various evaluation strategies for hierarchical data structures. ** Based on the Lumiera Forward Iterator concept and using the basic IterAdaptor templates, ** these components allow to implement typical evaluation strategies, like e.g. depth-first or ** breadth-first exploration of a hierarchical structure. Since the access to this structure is ** abstracted through the underlying iterator, what we effectively get is a functional datastructure. ** The implementation is based on the IterStateWrapper, which is one of the basic helper templates ** provided by iter-adapter.hpp. ** ** \par Iterators as Monad ** The fundamental idea behind the implementation technique used here is the \em Monad pattern ** known from functional programming. A Monad is a (abstract) container created by using some specific functions. ** This is an rather abstract concept with a wide variety of applications (things like IO state, parsers, combinators, ** calculations with exception handling but also simple data structures like lists or trees). The key point with any ** monad is the ability to \em bind a function into the monad; this function will work on the \em internals of the ** monad and produce a modified new monad instance. In the simple case of a list, "binding" a function ** basically means to map the function onto the elements in the list. ** ** \par Rationale ** The primary benefit of using the monad pattern is to separate the transforming operation completely from ** the mechanics of applying that operation and combining the results. More specifically, we rely on an iterator ** to represent an abstracted source of data and we expose the combined and transformed results again as such ** an abstracted data sequence. The transformation to apply can be selected at runtime (as a functor), and ** also the logic how to combine elements can be implemented elsewhere. The monad pattern defines a sane ** way of representing this partial evaluation state without requiring a container for intermediary ** results. This is especially helpful when ** - a flexible and unspecific source data structure needs to be processed ** - and this evaluation needs to be done asynchronously and in parallel (no locking, immutable data) ** - and a partial evaluation needs to be stored as continuation (not relying on the stack for partial results) ** ** \par preconfigured solutions ** This header provides some preconfigured applications of this pattern ** - the DefaultCombinator processes the source elements on demand, feeding them through ** the given functor and using the resulting iterator to deliver the result elements ** - Chained iterator uses similar building blocks just to get the "flattening" of ** a sequence of iterators into a single result iterator ** - the RecursiveExhaustingEvaluation is another kind of combination strategy, ** which recursively evaluates the given function and combines the results ** such as to produce classical depth-first and breadth-first search orders. ** - the more low-level RecursiveSelfIntegration combinator strategy actually ** delegates to the result set iterator implementation to perform the collecting ** and re-integrating of intermediary results. This approach is what we actually ** use in the proc::engine::Dispatcher ** ** Alternatively, just the basic IterExplorer template can be used together with a custom ** "combinator strategy" and typically even a specific iterator or sequence to implement very specific ** and optimised data structure evaluation patterns. This strategy needs to define some way to hold onto ** the original source elements, feed them through the functor on demand and recombine the result sets ** into a new sequence to be delivered on demand. ** Actually this is what we utilise for the continuous render job generation within the scheduler. ** All the other preconfigured variants defined here where created as proof-of-concept, to document ** and verify this implementation technique as such. ** ** @see IterExplorer_test ** @see iter-adapter.hpp ** @see itertools.hpp ** @see IterSource (completely opaque iterator) ** */ #ifndef LIB_ITER_EXPLORER_H #define LIB_ITER_EXPLORER_H #include "lib/error.hpp" #include "lib/meta/function.hpp" #include "lib/iter-adapter.hpp" #include "lib/iter-stack.hpp" #include "lib/meta/trait.hpp" #include "lib/null-value.hpp" #include "lib/util.hpp" #include #include namespace lib { namespace iter_explorer { template class DefaultCombinator; } /** * Adapter for using an Iterator in the way of a Monad * This allows to "bind" or "flatMap" a functor, thereby creating * a derived version of the iterator, yielding the (flattened) combination * of all the results generated by this bound functor. The rationale is * to apply some exploration or expansion pattern on the elements of the * source sequence (source iterator) -- while completely separating out * the \em mechanics how to treat and combine individual elements. * * \par implementation approach * IterExplorer is a thin wrapper, based on IterStateWrapper; thus the assumption * is for the actual elements to be generated by a "state core", which is embedded * right into each instance. To provide the monad bind operator \c >>= , we need * the help of a strategy template, the so called \em Combinator. This strategy * contains the details how to combine the results of various iterators and * to join them together into a single new IterExplorer instance. Thus, obviously * this Combinator strategy template relies on additional knowledge about the * source iterator's implementation. Such is unavoidable, since C++ isn't a * functional programming language. * * When invoking the bind (flat map) operator, a suitably represented \em functor * is embedded into an instance of the Combinator template. Moreover, a copy of * the current IterExplorer is embedded alongside. The resulting object becomes * the state core of the new IterExplorer object returned as result. * So the result \em is an iterator, but -- when "pulled" -- it will in turn * pull from the source iterator and feed the provided elements through the * \em exploration functor, which this way has been \em bound into the resulting * monad. The purpose of the Combinator strategy is to join together various * resulting iterators, thereby "flattening" one level of "monad packaging" * (hence the name "flat map operator"). * * @remarks figuring out what happens here just from the code might lead to confusion. * You really need to grasp the monad concept first, which is a design pattern * commonly used in functional programming. For the practical purpose in question * here, you might consider it a "programmable semicolon": it links together a * chain of operations detailed elsewhere, and provided in a dynamic fashion * (as functor at runtime). More specifically, these operations will expand * and explore a dynamic source data set on the fly. Notably this allows * us to handle such an "exploration" in a flexible way, without the need * to allocate heap memory to store intermediary results, but also without * using the stack and recursive programming. * @warning be sure to consider the effects of copying iterators on any hidden state * referenced by the source iterator(s). Basically any iterator must behave * sane when copied while in iteration: IterExplorer first evaluates the * head element of the source (the explorer function should build an * independent, new result sequence based on this first element). * Afterwards, the source is \em advanced and then \em copied * into the result iterator. * @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 * IterExplorer. This instantiation of the strategy gets as type parameters: * - this IterExplorer's instance type * - the type of the function bound with \c >>= */ template class _COM_ = iter_explorer::DefaultCombinator > class IterExplorer : public IterStateWrapper { public: typedef typename SRC::value_type value_type; typedef typename SRC::reference reference; typedef typename SRC::pointer pointer; /** Metafunction: the resulting type when binding a functor of type FUN */ template struct Binding { typedef IterExplorer<_COM_, _COM_> Type; }; /** by default create an empty iterator */ IterExplorer() { } /** wrap an iterator-like state representation * to build it into a monad. The resulting entity * is both an iterator yielding the elements generated * by the core, and it provides the (monad) bind operator. */ explicit IterExplorer (SRC const& iterStateCore) : IterStateWrapper (iterStateCore) { } /** monad bind ("flat map") operator. * Using a specific function to explore and work * on the "contents" of this IterExplorer, with the goal * to build a new IterExplorer combining the results of this * function application. The enclosing IterExplorer instance * provides a Strategy template _COM_, which defines how those * results are actually to be combined. An instantiation of * this "Combinator" strategy becomes the state core * of the result iterator. */ template IterExplorer<_COM_, _COM_> operator >>= (FUN explorer) { typedef _COM_ Combinator; // instantiation of the combinator strategy typedef IterExplorer ResultsIter; // result IterExplorer using that instance as state core return ResultsIter ( Combinator (explorer // build a new iteration state core ,accessRemainingElements())); // based on a copy of this iterator / sequence } private: IterExplorer const& accessRemainingElements() { return *this; } }; namespace iter_explorer { ///< predefined policies and configurations using util::unConst; using boost::enable_if; using boost::disable_if; using std::tr1::function; using meta::_Fun; /** * Building block: evaluating source elements. * This strategy will be tied into a "Combinator" * to hold the actual functor bound into the enclosing * IterExplorer monad to work on the contained elements. */ template struct ExploreByFunction : function { template ExploreByFunction(FUN explorationFunctionDefinition) : function(explorationFunctionDefinition) { } ExploreByFunction() { } ///< by default initialised to bottom function }; /** * Support for a special use case: an Iterator of Iterators, joining results. * In this case, already the source produces a sequence of Iterators, which * just need to be passed through to the output buffer unaltered. Using this * within the DefaultCombinator strategy creates a combined, flattened iterator * of all the source iterator's contents. */ template struct UnalteredPassThrough; template struct UnalteredPassThrough { IT operator() (IT elm) const { return elm; } bool operator! () const { return false; } ///< identity function is always valid }; /** * Building block: evaluate and combine a sequence of iterators. * This implementation helper provides two kinds of "buffers" (actually implemented * as iterators): A result buffer (iterator) which holds a sequence of already prepared * result elements, which can be retrieved through iteration right away. And a supply buffer * (iterator) holding raw source elements. When the result buffer is exhausted, the next source * element will be pulled from there and fed through the "evaluation strategy", which typically * is a function processing the source element and producing a new result buffer (iterator). */ template class _EXP_ = ExploreByFunction ///< Strategy: how to store and evaluate the function to apply on each element > class CombinedIteratorEvaluation { typedef typename _Fun::Ret ResultIter; typedef typename SRC::value_type SrcElement; typedef _EXP_ Explorer; SRC srcSeq_; ResultIter results_; Explorer explorer_; public: typedef typename ResultIter::value_type value_type; typedef typename ResultIter::reference reference; typedef typename ResultIter::pointer pointer; CombinedIteratorEvaluation() { } CombinedIteratorEvaluation(FUN explorerFunction) : srcSeq_() , results_() , explorer_(explorerFunction) { } // using standard copy operations void setSourceSequence (SRC const& followUpSourceElements) { REQUIRE (explorer_); srcSeq_ = followUpSourceElements; } private: bool findNextResultElement() { while (!results_ && srcSeq_) { results_ = explorer_(*srcSeq_); ++srcSeq_; } return bool(results_); } /* === Iteration control API for IterStateWrapper== */ friend bool checkPoint (CombinedIteratorEvaluation const& seq) { return unConst(seq).findNextResultElement(); } friend reference yield (CombinedIteratorEvaluation const& seq) { return *(seq.results_); } friend void iterNext (CombinedIteratorEvaluation & seq) { ++(seq.results_); } }; /** * a generic "Combinator strategy" for IterExplorer. * This default / fallback solution doesn't assume anything beyond the * source and the intermediary result(s) being Lumiera Forward Iterators. * @note the implementation stores the functor into a std::function object, * which might cause heap allocations, depending on given function. * Besides, the implementation holds one instance of the (intermediary) * result iterator (yielded by invoking the function) and a copy of the * original IterExplorer source sequence, to get the further elements * when the initial results are exhausted. */ template class DefaultCombinator : public CombinedIteratorEvaluation { typedef typename _Fun::Ret ResultIter; public: DefaultCombinator() { } DefaultCombinator(FUN explorerFunction, SRC const& sourceElements) : CombinedIteratorEvaluation(explorerFunction) { this->setSourceSequence (sourceElements); } }; /** Metafunction to detect an iterator yielding an iterator sequence */ template struct _is_iterator_of_iterators { typedef typename IT::value_type IteratorElementType; enum{ value = meta::can_IterForEach::value }; }; template class ChainedIteratorImpl : public CombinedIteratorEvaluation { }; /** * Special iterator configuration for combining / flattening the * results of a sequence of iterators. This sequence of source iterators * is assumed to be available as "Iterator yielding Iterators". * The resulting class is a Lumiera Forward Iterator, delivering all the * elements of all source iterators in sequence. * @remarks this is quite similar to the IterExplorer monad, but without * binding an exploration function to produce the result sequences. * Rather, the result sequences are directly pulled from the source * sequence, which thus needs to be an "Iterator of Iterators". * Beyond that, the implementation relies on the same building * blocks as used for the full-blown IterExplorer. * @param ITI iterator of iterators * @param SEQ type of the individual sequence (iterator). * The value_type of this sequence will be the overall * resulting value type of the flattened sequence */ template class ChainedIters; template class ChainedIters >::type > : public IterStateWrapper > { public: ChainedIters(ITI const& iteratorOfIterators) { // note: default ctor on parent -> empty sequences this->stateCore().setSourceSequence (iteratorOfIterators); } }; /** * Convenience specialisation: manage the sequence of iterators automatically. * @note in this case the \em first template parameter denotes the \em element sequence type; * we use a IterStack to hold the sequence-of-iterators in heap storage. * @warning this specialisation will not be picked, if the \em value-type * of the given iterator is itself an iterator */ template class ChainedIters >::type > : public IterStateWrapper, SEQ> > { public: typedef IterStack IteratorIterator; ChainedIters(IteratorIterator const& iteratorOfIterators) { this->stateCore().setSourceSequence (iteratorOfIterators); } /** empty result sequence by default */ ChainedIters() { } }; /** * A "Combinator strategy" allowing to expand and evaluate a * (functional) data structure successively and recursively. * Contrary to the DefaultCombinator, here the explorer is evaluated * repeatedly, feeding back the results until exhaustion. The concrete * exploration function needs to embody some kind of termination condition, * e.g. by returning an empty sequence at some point, otherwise infinite * recursion might happen. Another consequence of this repeated re-evaluation * is the requirement of the source sequence's element type to be compatible * to the result sequence's element type -- we can't \em transform the contents * of the source sequence into another data type, just explore and expand those * contents into sub-sequences based on the same data type. (While this contradicts * the full requirements for building a Monad, we can always work around that kind * of restriction by producing the element type of the target sequence by implicit * type conversion) * * \par strategy requirements * To build a concrete combinator a special strategy template is required to define * the actual implementation logic how to proceed with the evaluation (i.e. how to * find the feed of the "next elements" and how to re-integrate the results of an * evaluation step into the already expanded sequence of intermediary results. * Moreover, these implementation strategy pattern is used as a data buffer * to hold those intermediary results. Together, this allows to create * various expansion patterns, e.g. depth-first or breadth-first. * - \c Strategy::getFeed() accesses the point from where * to pull the next element to be expanded. This function must not * yield an empty sequence, \em unless the overall exploration is exhausted * - \c Strategy::feedBack() re-integrates the results of an expansion step */ template class _BUF_ > class RecursiveExhaustingEvaluation { typedef typename _Fun::Ret ResultIter; typedef typename SRC::value_type Val; typedef function Explorer; typedef _BUF_ Buffer; Buffer resultBuf_; Explorer explore_; public: typedef typename ResultIter::value_type value_type; typedef typename ResultIter::reference reference; typedef typename ResultIter::pointer pointer; RecursiveExhaustingEvaluation (Explorer fun, SRC const& src) : resultBuf_() , explore_(fun) { resultBuf_.feedBack( initEvaluation (src)); } RecursiveExhaustingEvaluation() { }; // standard copy operations private: /** Extension point: build the initial evaluation state * based on the source sequence (typically an IterExplorer). * This is a tricky problem, since the source sequence is not * necessarily assignment compatible to the ResultIter type * and there is no general method to build a ResultIter. * The solution is to rely on a "builder trait", which * needs to be defined alongside with the concrete * ResultIter type. The actual builder trait will * be picked up through a free function (ADL). */ ResultIter initEvaluation (SRC const& initialElements) { ResultIter startSet; return build(startSet).usingSequence(initialElements); } // extension point: free function build (...) /** @note \c _BUF_::getFeed is required to yield a non-empty sequence, * until everything is exhausted. Basically the buffer- and re-integrated * result sequences can be expected to be pulled until finding the next * non-empty supply. This is considered hidden internal state and thus * concealed within this \em const and \em idempotent function. */ ResultIter & feed() const { return unConst(this)->resultBuf_.getFeed(); } void iterate () { REQUIRE (feed()); ResultIter nextStep = explore_(*feed()); ++ feed(); resultBuf_.feedBack (nextStep); } /* === Iteration control API for IterStateWrapper== */ friend bool checkPoint (RecursiveExhaustingEvaluation const& seq) { return bool(seq.feed()); } friend reference yield (RecursiveExhaustingEvaluation const& seq) { return *(seq.feed()); } friend void iterNext (RecursiveExhaustingEvaluation & seq) { seq.iterate(); } }; /** * Strategy building block for recursive exhausting evaluation. * Allows to create depth-fist or breadth-first evaluation patters, just * by using a suitably intermediary storage container to hold the partially * evaluated iterators created at each evaluation step. Using a stack and * pushing results will create a depth-first pattern, while using a queue * will evaluate in layers (breadth-first). In both cases, the next * evaluation step will happen at the iterator returned by #getFeed. * @warning uses an empty-iterator marker object to signal exhaustion * - this marker \c IT() may be re-initialised concurrently * - accessing this marker during app shutdown might access * an already defunct object */ template< class IT , template class _QUEUE_ ///< the actual container to use for storage of intermediary results > class EvaluationBufferStrategy { _QUEUE_ intermediaryResults_; /** @return default constructed (=empty) iterator * @remarks casting away const is safe here, since all * you can do with an NIL iterator is to test for emptiness. */ IT & emptySequence() { return unConst(NullValue::get()); } // unsafe during shutdown public: IT & getFeed () { // fast forward to find the next non-empty result sequence while (intermediaryResults_ && ! *intermediaryResults_) ++intermediaryResults_; if (intermediaryResults_) return *intermediaryResults_; else return emptySequence(); } void feedBack (IT const& newEvaluationResults) { intermediaryResults_.insert (newEvaluationResults); } }; /** * concrete strategy for recursive \em depth-first evaluation. * Using heap allocated storage in a STL Deque (used stack-like) */ template struct DepthFirstEvaluationBuffer : EvaluationBufferStrategy { }; /** * concrete strategy for recursive \em breadth-first evaluation. * Using heap allocated storage in a STL Deque (used queue-like) */ template struct BreadthFirstEvaluationBuffer : EvaluationBufferStrategy { }; /** * preconfigured IterExplorer "state core" resulting in * depth-first exhaustive evaluation */ template struct DepthFirstEvaluationConbinator : RecursiveExhaustingEvaluation { DepthFirstEvaluationConbinator() { } DepthFirstEvaluationConbinator(FUN explorerFunction, SRC const& sourceElements) : RecursiveExhaustingEvaluation (explorerFunction,sourceElements) { } }; /** * preconfigured IterExplorer "state core" resulting in * breadth-first exhaustive evaluation */ template struct BreadthFirstEvaluationConbinator : RecursiveExhaustingEvaluation { BreadthFirstEvaluationConbinator() { } BreadthFirstEvaluationConbinator(FUN explorerFunction, SRC const& sourceElements) : RecursiveExhaustingEvaluation (explorerFunction,sourceElements) { } }; /** * IterExplorer "state core" for progressively expanding * an initial result set. This is a partially reduced and hard-coded * variation on the #RecursiveExhaustingEvaluation in depth-first configuration. * This setup works in conjunction with a special result sequence type, * with the ability to re-integrate results yielded by partial evaluation. * But the working pattern is more similar to the #CombinedIteratorEvaluation, * where the focal point of further expansion is always at the front end of * further items yet to be consumed and expanded; thus the evaluation is * bound to proceed depth-first (i.e. it is \em not possible to "integrate" * any intermediary result with the \em whole result set, only with the part * reachable immediately at the evaluation front) * * \par usage considerations * There needs to be a specific sequence or iterator type, which is used to * hold the result set(s). This custom type together with the Explorer function * are performing the actual expansion and re-integration steps. The latter is * accessed through the free function \c build(sequence) -- which is expected * to yield a "builder trait" for manipulating the element set yielded by * the custom iterator type returned by the Explorer function. * * @param SRC the initial result set sequence; this iterator needs to yield * values of the special ResultIter type, which are then expanded * until exhaustion by repeated calls to the Explorer function * @param FUN the Explorer function of type ResultIter -> ResultIter * @note the ResultIter type is defined implicitly through the result type * of the Explorer function. Similarly the result value type * is defined through the typedef \c ResultIter::value_type */ template class RecursiveSelfIntegration { typedef typename _Fun::Ret ResultIter; typedef typename SRC::value_type Val; typedef function Explorer; SRC srcSeq_; ResultIter outSeq_; Explorer explore_; public: typedef typename ResultIter::value_type value_type; typedef typename ResultIter::reference reference; typedef typename ResultIter::pointer pointer; RecursiveSelfIntegration (Explorer fun, SRC const& src) : srcSeq_(src) , outSeq_() , explore_(fun) { } RecursiveSelfIntegration() { }; // standard copy operations private: /** assure the next elements to be processed * will appear at outSeq_ head. When outSeq_ * is still empty after this function, * we're done. * @note \em not calling this function after * construction, because the typical user * of this template will do that implicitly * through invocation of #checkPoint */ bool findNextResultElement() { while (!outSeq_ && srcSeq_) { Val& nextElement(*srcSeq_); build(outSeq_).wrapping(nextElement); // extension point: free function build (...).wrapping(...) ++srcSeq_; } return bool(outSeq_); } void iterate () { REQUIRE (outSeq_); ResultIter nextStep = explore_(*outSeq_); ++ outSeq_; build(outSeq_).usingSequence(nextStep); // extension point: free function build (...).usingSequence(...) } /* === Iteration control API for IterStateWrapper== */ friend bool checkPoint (RecursiveSelfIntegration const& seq) { return unConst(seq).findNextResultElement(); } friend reference yield (RecursiveSelfIntegration const& seq) { return *(seq.outSeq_); } friend void iterNext (RecursiveSelfIntegration & seq) { seq.iterate(); } }; /** * Helper template to bootstrap a chain of IterExplorers. * This is a "state core", which basically just wraps a given * source iterator and provides the necessary free functions * (iteration control API) to use this as iteration state * within IterExplorer. * @note to ease building such an initial version of the Iterator Monad, * use the free function \link #exploreIter \endlink */ template class WrappedSequence : public IT { public: WrappedSequence() : IT() { } WrappedSequence(IT const& srcIter) : IT(srcIter) { } /* === Iteration control API for IterStateWrapper == */ friend bool checkPoint (WrappedSequence const& sequence) { return bool(sequence); } friend typename IT::reference yield (WrappedSequence const& sequence) { return *sequence; } friend void iterNext (WrappedSequence & sequence) { ++sequence; } }; template struct DepthFirst : IterExplorer, DepthFirstEvaluationConbinator> { DepthFirst() { }; DepthFirst(SRC const& srcSeq) : IterExplorer, DepthFirstEvaluationConbinator> (srcSeq) { } }; template struct BreadthFirst : IterExplorer, BreadthFirstEvaluationConbinator> { BreadthFirst() { }; BreadthFirst(SRC const& srcSeq) : IterExplorer, BreadthFirstEvaluationConbinator> (srcSeq) { } }; }//(End) namespace iter_explorer : predefined policies and configurations /* ==== convenient builder free functions ==== */ template inline IterExplorer > exploreIter (IT const& srcSeq) { return IterExplorer > (srcSeq); } template inline iter_explorer::DepthFirst depthFirst (IT const& srcSeq) { return iter_explorer::DepthFirst (srcSeq); } template inline iter_explorer::BreadthFirst breadthFirst (IT const& srcSeq) { return iter_explorer::BreadthFirst (srcSeq); } template inline iter_explorer::ChainedIters iterChain(IT const& seq) { typename iter_explorer::ChainedIters::IteratorIterator sequenceOfIterators; sequenceOfIterators.push (seq); return iter_explorer::ChainedIters(sequenceOfIterators); } template inline iter_explorer::ChainedIters iterChain(IT const& seq1, IT const& seq2) { typename iter_explorer::ChainedIters::IteratorIterator sequenceOfIterators; sequenceOfIterators.push (seq2); sequenceOfIterators.push (seq1); return iter_explorer::ChainedIters(sequenceOfIterators); } template inline iter_explorer::ChainedIters iterChain(IT const& seq1, IT const& seq2, IT const& seq3) { typename iter_explorer::ChainedIters::IteratorIterator sequenceOfIterators; sequenceOfIterators.push (seq3); sequenceOfIterators.push (seq2); sequenceOfIterators.push (seq1); return iter_explorer::ChainedIters(sequenceOfIterators); } template inline iter_explorer::ChainedIters iterChain(IT const& seq1, IT const& seq2, IT const& seq3, IT const& seq4) { typename iter_explorer::ChainedIters::IteratorIterator sequenceOfIterators; sequenceOfIterators.push (seq4); sequenceOfIterators.push (seq3); sequenceOfIterators.push (seq2); sequenceOfIterators.push (seq1); return iter_explorer::ChainedIters(sequenceOfIterators); } template inline iter_explorer::ChainedIters iterChain(IT const& seq1, IT const& seq2, IT const& seq3, IT const& seq4, IT const& seq5) { typename iter_explorer::ChainedIters::IteratorIterator sequenceOfIterators; sequenceOfIterators.push (seq5); sequenceOfIterators.push (seq4); sequenceOfIterators.push (seq3); sequenceOfIterators.push (seq2); sequenceOfIterators.push (seq1); return iter_explorer::ChainedIters(sequenceOfIterators); } } // namespace lib #endif