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:
parent
41626c363e
commit
68dd8a9e03
4 changed files with 80 additions and 29 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue