1007 lines
39 KiB
C++
1007 lines
39 KiB
C++
/*
|
|
ITER-EXPLORER.hpp - building blocks for iterator evaluation strategies
|
|
|
|
Copyright (C) Lumiera.org
|
|
2012, Hermann Vosseler <Ichthyostega@web.de>
|
|
|
|
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 <b>Lumiera Forward Iterator</b> 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 <boost/utility/enable_if.hpp>
|
|
#include <stack>
|
|
|
|
|
|
namespace lib {
|
|
|
|
namespace iter_explorer {
|
|
|
|
template<class SRC, class FUN>
|
|
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.
|
|
* @warning IterExplorer and all the provided combination strategies are deliberately
|
|
* designed to work on sequences <b>of values</b>. 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
|
|
* 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 SRC
|
|
,template<class,class> class _COM_ = iter_explorer::DefaultCombinator
|
|
>
|
|
class IterExplorer
|
|
: public IterStateWrapper<typename SRC::value_type, SRC>
|
|
{
|
|
|
|
|
|
public:
|
|
typedef typename SRC::value_type value_type;
|
|
typedef typename SRC::reference reference;
|
|
typedef typename SRC::pointer pointer;
|
|
|
|
/** Metafunction: the resulting type when binding ("flat mapping")
|
|
* a functor of type FUN. Basically the result of binding a function
|
|
* is again an IterExplorer (with an "expanded" state core type) */
|
|
template<class FUN>
|
|
struct FlatMapped
|
|
{
|
|
typedef IterExplorer<_COM_<IterExplorer,FUN>, _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<value_type, SRC> (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<class FUN>
|
|
typename FlatMapped<FUN>::Type
|
|
operator >>= (FUN explorer)
|
|
{
|
|
typedef _COM_<IterExplorer,FUN> Combinator; // instantiation of the combinator strategy
|
|
typedef typename FlatMapped<FUN>::Type 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::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<class SIG>
|
|
struct ExploreByFunction
|
|
: function<SIG>
|
|
{
|
|
template<typename FUN>
|
|
ExploreByFunction(FUN explorationFunctionDefinition)
|
|
: function<SIG>(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<class SIG>
|
|
struct UnalteredPassThrough;
|
|
|
|
template<class IT>
|
|
struct UnalteredPassThrough<IT(IT)>
|
|
{
|
|
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 SRC, class FUN
|
|
,template<class> class _EXP_ = ExploreByFunction ///< Strategy: how to store and evaluate the function to apply on each element
|
|
>
|
|
class CombinedIteratorEvaluation
|
|
{
|
|
typedef typename _Fun<FUN>::Ret ResultIter;
|
|
typedef typename SRC::value_type SrcElement;
|
|
typedef _EXP_<ResultIter(SrcElement)> 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 SRC, class FUN>
|
|
class DefaultCombinator
|
|
: public CombinedIteratorEvaluation<SRC,FUN>
|
|
{
|
|
typedef typename _Fun<FUN>::Ret ResultIter;
|
|
|
|
public:
|
|
DefaultCombinator() { }
|
|
|
|
DefaultCombinator(FUN explorerFunction, SRC const& sourceElements)
|
|
: CombinedIteratorEvaluation<SRC,FUN>(explorerFunction)
|
|
{
|
|
this->setSourceSequence (sourceElements);
|
|
}
|
|
};
|
|
|
|
|
|
/** Metafunction to detect an iterator yielding an iterator sequence */
|
|
template<class IT>
|
|
struct _is_iterator_of_iterators
|
|
{
|
|
typedef typename IT::value_type IteratorElementType;
|
|
|
|
enum{ value = meta::can_IterForEach<IteratorElementType>::value };
|
|
};
|
|
|
|
|
|
template<class ITI, class SEQ>
|
|
class ChainedIteratorImpl
|
|
: public CombinedIteratorEvaluation<ITI, SEQ(SEQ)
|
|
, UnalteredPassThrough
|
|
>
|
|
{ };
|
|
|
|
|
|
/**
|
|
* 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 ITI, class SEL = void>
|
|
class ChainedIters;
|
|
|
|
template<class ITI>
|
|
class ChainedIters<ITI, typename enable_if< _is_iterator_of_iterators<ITI> >::type
|
|
>
|
|
: public IterStateWrapper<typename ITI::value_type::value_type
|
|
,ChainedIteratorImpl<ITI, typename ITI::value_type>
|
|
>
|
|
{
|
|
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 SEQ>
|
|
class ChainedIters<SEQ, typename disable_if< _is_iterator_of_iterators<SEQ> >::type
|
|
>
|
|
: public IterStateWrapper<typename SEQ::value_type
|
|
,ChainedIteratorImpl<IterStack<SEQ>, SEQ>
|
|
>
|
|
{
|
|
public:
|
|
typedef IterStack<SEQ> 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, this 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 <i>must not</i>
|
|
* yield an empty sequence, \em unless the overall exploration is exhausted
|
|
* - \c Strategy::feedBack() re-integrates the results of an expansion step
|
|
*/
|
|
template<class SRC, class FUN
|
|
,template<class> class _BUF_
|
|
>
|
|
class RecursiveExhaustingEvaluation
|
|
{
|
|
typedef typename _Fun<FUN>::Ret ResultIter;
|
|
typedef typename SRC::value_type Val; // note: deliberately using the value
|
|
typedef function<ResultIter(Val)> Explorer;
|
|
typedef _BUF_<ResultIter> 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)
|
|
{
|
|
reference result = *(seq.feed());
|
|
return result;
|
|
}
|
|
|
|
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> class _QUEUE_ ///< the actual container to use for storage of intermediary results
|
|
>
|
|
class EvaluationBufferStrategy
|
|
{
|
|
_QUEUE_<IT> 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<IT>::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<class IT>
|
|
struct DepthFirstEvaluationBuffer
|
|
: EvaluationBufferStrategy<IT, IterStack>
|
|
{ };
|
|
|
|
/**
|
|
* concrete strategy for recursive \em breadth-first evaluation.
|
|
* Using heap allocated storage in a STL Deque (used queue-like)
|
|
*/
|
|
template<class IT>
|
|
struct BreadthFirstEvaluationBuffer
|
|
: EvaluationBufferStrategy<IT, IterQueue>
|
|
{ };
|
|
|
|
|
|
|
|
/**
|
|
* preconfigured IterExplorer "state core" resulting in
|
|
* depth-first exhaustive evaluation
|
|
*/
|
|
template<class SRC, class FUN>
|
|
struct DepthFirstEvaluationConbinator
|
|
: RecursiveExhaustingEvaluation<SRC, FUN, DepthFirstEvaluationBuffer>
|
|
{
|
|
DepthFirstEvaluationConbinator() { }
|
|
|
|
DepthFirstEvaluationConbinator(FUN explorerFunction, SRC const& sourceElements)
|
|
: RecursiveExhaustingEvaluation<SRC, FUN, DepthFirstEvaluationBuffer> (explorerFunction,sourceElements)
|
|
{ }
|
|
};
|
|
|
|
/**
|
|
* preconfigured IterExplorer "state core" resulting in
|
|
* breadth-first exhaustive evaluation
|
|
*/
|
|
template<class SRC, class FUN>
|
|
struct BreadthFirstEvaluationConbinator
|
|
: RecursiveExhaustingEvaluation<SRC, FUN, BreadthFirstEvaluationBuffer>
|
|
{
|
|
BreadthFirstEvaluationConbinator() { }
|
|
|
|
BreadthFirstEvaluationConbinator(FUN explorerFunction, SRC const& sourceElements)
|
|
: RecursiveExhaustingEvaluation<SRC, FUN, BreadthFirstEvaluationBuffer> (explorerFunction,sourceElements)
|
|
{ }
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
* IterExplorer "state core" for progressively expanding
|
|
* an initial result set. This initial set can be conceived to hold the seed
|
|
* or starting points of evaluation. Elements are consumed by an iterator, at
|
|
* the front. Each element is fed to the "explorer function". This exploration
|
|
* returns an expanded result sequence, which is immediately integrated into the
|
|
* overall result sequence, followed by further exploration of the then-to-be first
|
|
* element of the result sequence. All this exploration is driven on-demand, by
|
|
* consuming the result sequence. Exploration will proceed until exhaustion,
|
|
* in which case the exploration function will yield an empty result set.
|
|
*
|
|
* This strategy is intended for use with the IterExplorer -- most prominently
|
|
* in use for discovering render prerequisites and creating new render jobs for
|
|
* the engine. The RecursiveSelfIntegration strategy is a partially reduced
|
|
* and hard-coded variation on the #RecursiveExhaustingEvaluation in depth-first
|
|
* configuration.
|
|
* This setup works in conjunction with a <i>special result sequence</i> 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 return 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 SRC, class FUN>
|
|
class RecursiveSelfIntegration
|
|
{
|
|
typedef typename _Fun<FUN>::Ret ResultIter;
|
|
typedef typename SRC::value_type Val;
|
|
typedef function<ResultIter(Val)> 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 nextSteps = explore_(*outSeq_);
|
|
++ outSeq_;
|
|
build(outSeq_).usingSequence(nextSteps); // 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 IT>
|
|
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<class SRC>
|
|
struct DepthFirst
|
|
: IterExplorer<WrappedSequence<SRC>, DepthFirstEvaluationConbinator>
|
|
{
|
|
DepthFirst() { };
|
|
DepthFirst(SRC const& srcSeq)
|
|
: IterExplorer<WrappedSequence<SRC>, DepthFirstEvaluationConbinator> (srcSeq)
|
|
{ }
|
|
};
|
|
|
|
template<class SRC>
|
|
struct BreadthFirst
|
|
: IterExplorer<WrappedSequence<SRC>, BreadthFirstEvaluationConbinator>
|
|
{
|
|
BreadthFirst() { };
|
|
BreadthFirst(SRC const& srcSeq)
|
|
: IterExplorer<WrappedSequence<SRC>, BreadthFirstEvaluationConbinator> (srcSeq)
|
|
{ }
|
|
};
|
|
|
|
|
|
}//(End) namespace iter_explorer : predefined policies and configurations
|
|
|
|
|
|
|
|
|
|
/* ==== convenient builder free functions ==== */
|
|
|
|
template<class IT>
|
|
inline IterExplorer<iter_explorer::WrappedSequence<IT> >
|
|
exploreIter (IT const& srcSeq)
|
|
{
|
|
return IterExplorer<iter_explorer::WrappedSequence<IT> > (srcSeq);
|
|
}
|
|
|
|
|
|
template<class IT>
|
|
inline iter_explorer::DepthFirst<IT>
|
|
depthFirst (IT const& srcSeq)
|
|
{
|
|
return iter_explorer::DepthFirst<IT> (srcSeq);
|
|
}
|
|
|
|
|
|
template<class IT>
|
|
inline iter_explorer::BreadthFirst<IT>
|
|
breadthFirst (IT const& srcSeq)
|
|
{
|
|
return iter_explorer::BreadthFirst<IT> (srcSeq);
|
|
}
|
|
|
|
|
|
|
|
template<class IT>
|
|
inline iter_explorer::ChainedIters<IT>
|
|
iterChain(IT const& seq)
|
|
{
|
|
typename iter_explorer::ChainedIters<IT>::IteratorIterator sequenceOfIterators;
|
|
|
|
sequenceOfIterators.push (seq);
|
|
return iter_explorer::ChainedIters<IT>(sequenceOfIterators);
|
|
}
|
|
|
|
template<class IT>
|
|
inline iter_explorer::ChainedIters<IT>
|
|
iterChain(IT const& seq1, IT const& seq2)
|
|
{
|
|
typename iter_explorer::ChainedIters<IT>::IteratorIterator sequenceOfIterators;
|
|
|
|
sequenceOfIterators.push (seq2);
|
|
sequenceOfIterators.push (seq1);
|
|
return iter_explorer::ChainedIters<IT>(sequenceOfIterators);
|
|
}
|
|
|
|
template<class IT>
|
|
inline iter_explorer::ChainedIters<IT>
|
|
iterChain(IT const& seq1, IT const& seq2, IT const& seq3)
|
|
{
|
|
typename iter_explorer::ChainedIters<IT>::IteratorIterator sequenceOfIterators;
|
|
|
|
sequenceOfIterators.push (seq3);
|
|
sequenceOfIterators.push (seq2);
|
|
sequenceOfIterators.push (seq1);
|
|
return iter_explorer::ChainedIters<IT>(sequenceOfIterators);
|
|
}
|
|
|
|
template<class IT>
|
|
inline iter_explorer::ChainedIters<IT>
|
|
iterChain(IT const& seq1, IT const& seq2, IT const& seq3, IT const& seq4)
|
|
{
|
|
typename iter_explorer::ChainedIters<IT>::IteratorIterator sequenceOfIterators;
|
|
|
|
sequenceOfIterators.push (seq4);
|
|
sequenceOfIterators.push (seq3);
|
|
sequenceOfIterators.push (seq2);
|
|
sequenceOfIterators.push (seq1);
|
|
return iter_explorer::ChainedIters<IT>(sequenceOfIterators);
|
|
}
|
|
|
|
template<class IT>
|
|
inline iter_explorer::ChainedIters<IT>
|
|
iterChain(IT const& seq1, IT const& seq2, IT const& seq3, IT const& seq4, IT const& seq5)
|
|
{
|
|
typename iter_explorer::ChainedIters<IT>::IteratorIterator sequenceOfIterators;
|
|
|
|
sequenceOfIterators.push (seq5);
|
|
sequenceOfIterators.push (seq4);
|
|
sequenceOfIterators.push (seq3);
|
|
sequenceOfIterators.push (seq2);
|
|
sequenceOfIterators.push (seq1);
|
|
return iter_explorer::ChainedIters<IT>(sequenceOfIterators);
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace lib
|
|
#endif
|