refine implementation draft: make FilterIter extensible on-the-fly

after looking into our various iterator tools,
it seems obvious that our filtering iterator implementation
has almost all of the required behaviour; we only need to
add a hook to rewrite and extend the filtering functor,
which can now nicely done with a lambda closure.

This means all memory management, if necessary, is
pushed into std::function and the automated memory
management for closures provided by the runtime.
This commit is contained in:
Fischlurch 2015-12-05 00:28:07 +01:00
parent 41626c363e
commit 68dd8a9e03
4 changed files with 80 additions and 29 deletions

View file

@ -257,7 +257,7 @@ namespace lib {
namespace iter_explorer { ///< predefined policies and configurations
namespace iter_explorer { ///< predefined "exploration strategies", policies and configurations
using util::unConst;
using boost::enable_if;
@ -267,7 +267,7 @@ namespace lib {
/**
* Building block: evaluating source elements.
* Building block: just evaluate 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.
@ -386,9 +386,9 @@ namespace lib {
/**
* 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.
* source and the intermediary result(s) to be Lumiera Forward Iterators.
* @note the implementation stores the functor into a std::function object,
* which might cause heap allocations, depending on given function.
* which might cause heap allocations, depending on the function given.
* 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

View file

@ -29,8 +29,8 @@
** based on the Lumiera Forward Iterator concept. They build on generic
** programming techniques, thus are intended to be combined at compile time
** using definitive type information. Contrast this to an iterator model
** as in Java's Commons-Collections, where Iterator is an Interface based
** on virtual functions. Thus, the basic problem to overcome is the lack
** as in Java's Collections, where Iterator is an Interface based on
** virtual functions. Thus, the basic problem to overcome is the lack
** of a single common interface, which could serve as foundation for
** type inference. As a solution, we use a "onion" approach, where a
** generic base gets configured with an active core, implementing
@ -55,10 +55,9 @@
** source iterator. The signature of the functor must match the
** desired value (output) type.
**
** @todo WIP WIP WIP
** @todo see Ticket #347
** @todo some more building blocks are planned, see Ticket #347
**
** @see IterAdapter
** @see iter-adapter.hpp
** @see itertools-test.cpp
** @see contents-query.hpp
*/
@ -142,15 +141,16 @@ namespace lib {
* Standard functionality to build up any iterator tool.
* IterTool exposes the frontend functions necessary to
* comply to the Lumiera Forward Iterator concept.
* The protected part provides the building blocks
* to implement the actual processing/filter logic.
* The protected part provides the _iteration control_
* building blocks to drive the processing/filter logic,
* which is implemented in the specific core for each tool.
*/
template<class CORE>
class IterTool
: public lib::BoolCheckable<IterTool<CORE> >
{
protected: /* iteration control */
protected: /* == iteration control == */
CORE core_;
bool
@ -158,7 +158,7 @@ namespace lib {
{
return core_.evaluate()
|| unConst(this)->iterate();
} // skipping irrelevant results doesn't count as "mutation"
} // to skip irrelevant results doesn't count as "mutation"
bool
iterate ()
@ -351,7 +351,41 @@ namespace lib {
/**
* Helper: predicate returning \c true
* Additional capabilities for FilterIter,
* allowing to extend the filter condition underway.
* This wrapper enables remoulding of the filer functor
* while in the middle of iteration. When the filter is
* modified, current head of iteration gets re-evaluated
* and possible fast-forwarded to the next element
* satisfying the now extended filter condition.
*/
template<class IT>
class ExtensibleFilterIter
: public FilterIter<IT>
{
typedef FilterCore<IT> _Filter;
typedef typename _Filter::Val Val;
public:
template<typename COND>
ExtensibleFilterIter&
andFilter (COND conjunctiveClause)
{
function<bool(Val)>& filter = this->core_.predicate_;
filter = [=](Val val)
{
return filter(val)
and conjunctiveClause(val);
};
return *this;
}
};
/**
* Helper: predicate returning `true`
* whenever the argument value changes
* during a sequence of invocations.
*/

View file

@ -73,19 +73,20 @@ namespace test{
{
friend class EventLog;
MegaPlonk solution_;
// MegaPlonk solution_;
bool
eval()
{
return !isnil (solution_);
UNIMPLEMENTED ("evaluate current filter condition");
// return !isnil (solution_);
}
void
enforce()
{
if (!eval())
throw error::State("jaleck", error::LUMIERA_ERROR_ASSERTION)
throw error::State("jaleck", error::LUMIERA_ERROR_ASSERTION);
}
public:
@ -117,7 +118,7 @@ namespace test{
EventMatch&
after (string match)
{
solution_ = solution_ >>= find(match);
// solution_ = solution_ >>= find(match);
enforce();
}

View file

@ -192,6 +192,13 @@ namespace test{
* @test use a simple source iterator yielding numbers
* to build various functional evaluation structures,
* based on the IterExplorer template.
* - the [state adapter][verifyStateAdapter] iterator
* construction pattern
* - helper to [chain iterators][verifyChainedIterators]
* - building [tree exploring structures][verifyDepthFirstExploration]
* - the [monadic nature][verifyMonadOperator] of IterExplorer
* - a [recursively self-integrating][verifyRecrusiveSelfIntegration]
* evaluation pattern
*
* \par Explanation
* Both this test and the IterExplorer template might be bewildering
@ -212,7 +219,7 @@ namespace test{
* In such a situation, it is beneficial to develop and test both
* in isolation. The IterExplorer template applies this pattern
* to the task of processing a source sequence. Typically we use
* this in situations where we can't effort building elaborate
* this in situations where we can't afford building elaborate
* data structures in (global) memory, but rather strive at
* doing everything on-the-fly. A typical example is the
* processing of a variably sized data set without
@ -240,13 +247,17 @@ namespace test{
/** @test all of the following IterExplorer flavours are built on top
* of a special iterator adapter, centred at the notion of an iterable
/** @test demonstrate the underlying solution approach of IterExplorer.
* All of the following IterExplorer flavours are built on top of
* a special iterator adapter, centred at the notion of an iterable
* state element type. The actual iterator just embodies one element
* of this state representation, and typically this element alone holds
* all the relevant state and information, without any hidden back-link
* to some kind of container in charge of the elements yielded.
* Essentially this means the iterator is self contained.
* all the relevant state and information. Essentially this means the
* iterator is self contained. Contrast this to the more conventional
* approach of iterator implementation, where the iterator entity actually
* maintains a hidden back-link to some kind of container, which in turn
* is the one in charge of the elements yielded by the iterator.
*
*/
void
verifyStateAdapter ()
@ -275,7 +286,9 @@ namespace test{
/** @test a convenient helper built using IterExplorer building blocks.
/** @test verify a helper to chain a series of iterators into a
* "flat" result sequence. This convenience helper is built using
* IterExplorer building blocks.
* The resulting iterator \em combines and \em flattens a sequence
* of source iterators, resulting in a simple sequence accessible
* as iterator again. Here we verify the convenience / default
@ -419,7 +432,7 @@ namespace test{
*
* @note technical detail: the result type of the exploration function (here \c exploreChildren() ) determines
* the iterator type used within IterExplorer and to drive the evaluation. The source sequence used to
* seed the evaluation process actually can be any iterator yielding assignment compatible values: The
* seed the evaluation process can actually be any iterator yielding assignment compatible values: The
* second example uses a NumberSequence with unsigned int values 0..6, while the actual expansion and
* evaluation is based on NumberSeries using signed int values.
*/
@ -454,8 +467,8 @@ namespace test{
/** @test a variation of recursive exploration, this time directly
* relying on the result set iterator type to provide the re-integration
/** @test verify a variation of recursive exploration, this time to rely
* directly on the result set iterator type to provide the re-integration
* of intermediary results. Since our \c exploreChildren() function returns
* a NumberSeries, which basically is a IterQueue, the re-integration of expanded
* elements will happen at the end, resulting in breadth-first visitation order --
@ -465,7 +478,9 @@ namespace test{
* \link #verifyBreadthFirstExploration \endlink, appears here at the end of the
* explorationResult sequence
* @remarks this "combinator strategy" is really intended for use with custom sequences,
* where the "Explorer" function works together with a specific implementation.
* where the "Explorer" function works together with a specific implementation
* and exploits knowledge about specifically tailored additional properties of
* the input sequence elements to yield the desired overall effect.
* Actually this is what we use in the proc::engine::Dispatcher to generate a
* series of frame render jobs, including all prerequisite jobs
*/
@ -534,6 +549,7 @@ namespace test{
// the ">>=" associates to the right, while the proper monad bind operator should associate to the left
}
/** @internal exploration function used in ::verifyMonadOperator */
static NumberSequence
explode (uint top)
{