lumiera_/src/lib/iter-explorer.hpp
Ichthyostega 9e35ed0e49 IterExplorer: remove the hard wired safeguard value copy
Since C++ is not a real functional programming language and
has unsafe unmanaged pointers, it is not difficult to produce
dangling references within an extended evaluation pipeline
involving transient objects and pass-by-reference.

In the initial implementation, I built in a safeguard copy
into the signature of the Explorer function, to make sure even
a transiently dressed-up input value gets materialised before
proceeding with the source sequence. Unfortunately this safeguard
turns out as a roadblock now; we might as well take the input
by reference and return an "expanded" state by value. We might
even want to do the full "expansion" on referred state, when
we're able to ensure the source values remain in memory
until consumption.

Thus now the full power of decision is placed on the signature
of the explorer function. The expansion strategies of IterExplorer
will no longer attempt to "sanitise" the signature of the passed-in
function to prevent desaster; I've added some warnings into the
documentation to highlight that danger. Basically, if you want
to be clever, then you're bound to read and understand inticacies
of the implementation.

If in doubt, use values and copying. C++ is optimised for that.
2015-09-04 17:55:34 +02:00

1031 lines
41 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.
**
** @warning preferably use value semantics for the elements to be processed. Recall, C++ is not
** really a functional programming language, and there is no garbage collector. It might be
** tempting just to pass pointers through a whole evaluation chain. Indeed, you can do so,
** but make sure you understand the precise timing of the evaluation, expansion and
** re-integration steps with regards to memory management; an "explorer function"
** may pass a reference or pointer to some transient source, which is gone after
** incrementing the source iterator.
** @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
*
* @warning beware, when tempted to pass elements by reference (or pointer)
* through the explorer function, make sure you really understand the
* working mode of the #iterate function with regards to memory management.
* When ResultIter attempts just to store a pointer, after incrementing
* \c ++feed(), depending on the internals of the actual src iterator,
* this pointer might end up dangling. Recommendation is to let the
* Explorer either take arguments or return results by value (copy).
*/
template<class SRC, class FUN
,template<class> class _BUF_
>
class RecursiveExhaustingEvaluation
{
typedef typename _Fun<FUN>::Ret ResultIter;
typedef typename _Fun<FUN>::Sig Sig;
typedef function<Sig> 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 patterns, 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 DepthFirstEvaluationCombinator
: RecursiveExhaustingEvaluation<SRC, FUN, DepthFirstEvaluationBuffer>
{
DepthFirstEvaluationCombinator() { }
DepthFirstEvaluationCombinator(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 BreadthFirstEvaluationCombinator
: RecursiveExhaustingEvaluation<SRC, FUN, BreadthFirstEvaluationBuffer>
{
BreadthFirstEvaluationCombinator() { }
BreadthFirstEvaluationCombinator(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
* @warning beware of dangling references; make sure you never pass a
* reference or pointer through the Explorer function unaltered.
* Because some source iterator might expose a reference to a
* transient object just for the purpose of expanding it.
* The very reason of building iterator pipelines is to
* avoid heap allocation and to produce intermediaries
* on demand. So make sure in such a case that there is
* at least one real copy operation in the pipeline.
*/
template<class SRC, class FUN>
class RecursiveSelfIntegration
{
typedef typename _Fun<FUN>::Ret ResultIter;
typedef typename _Fun<FUN>::Sig Sig;
typedef typename SRC::value_type Val;
typedef function<Sig> 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:
/** ensure 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>, DepthFirstEvaluationCombinator>
{
DepthFirst() { };
DepthFirst(SRC const& srcSeq)
: IterExplorer<WrappedSequence<SRC>, DepthFirstEvaluationCombinator> (srcSeq)
{ }
};
template<class SRC>
struct BreadthFirst
: IterExplorer<WrappedSequence<SRC>, BreadthFirstEvaluationCombinator>
{
BreadthFirst() { };
BreadthFirst(SRC const& srcSeq)
: IterExplorer<WrappedSequence<SRC>, BreadthFirstEvaluationCombinator> (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