2017-11-17 21:43:50 +01:00
/*
ITER - TREE - EXPLORER . hpp - building blocks for iterator evaluation strategies
Copyright ( C ) Lumiera . org
2017 , 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 0213 9 , USA .
*/
/** @file iter-tree-explorer.hpp
* * Building tree expanding and backtracking evaluations within hierarchical scopes .
2017-12-06 02:02:22 +01:00
* * Based on the * Lumiera Forward Iterator * concept and using the basic IterAdaptor templates ,
* * these components allow to implement typical evaluation strategies , like conditional expanding
* * or depth - first exploration of a hierarchical structure . Since the access to this structure is
2017-11-17 21:43:50 +01:00
* * abstracted through the underlying iterator , what we effectively get is a functional datastructure .
2017-12-06 02:02:22 +01:00
* * The implementation is based on the idea of a " state core " , which is wrapped right into the iterator
* * itself ( value semantics ) - - similar to the IterStateWrapper , which is one of the basic helper templates
2017-11-17 21:43:50 +01:00
* * provided by iter - adapter . hpp .
* *
* * @ remark as of 2017 , this template , as well as the initial IterExplorer ( draft from 2012 ) can be
* * seen as steps towards designing a framework of building blocks for tree expanding and
* * backtracking algorithms . Due to the nature of Lumiera ' s design , we repeatedly encounter
* * this kind of computation pattern , when it comes to matching flexible configuration against
* * a likewise hierarchical and rules based model . To keep the code base maintainable ,
* * we deem it crucial to reduce the inherent complexity in such algorithms by clearly
* * separate the _mechanics of evaluation_ from the actual logic of the target domain .
* *
* * # Iterators as Monad
2017-12-06 02:02:22 +01:00
* * The fundamental idea behind the implementation technique used here is the _Monad pattern_
* * known from functional programming . A Monad is a container holding some arbitrarily typed base value ; the monad can
* * be seen as " amplifying " and enhancing the contained base value by attaching additional properties or capabilities
* * This is a rather detached concept with a wide variety of applications ( things like IO state , parsers , combinators ,
2017-11-17 21:43:50 +01:00
* * calculations with exception handling but also simple data structures like lists or trees ) . The key point with any
2017-12-06 02:02:22 +01:00
* * monad is the ability to _bind a function_ into the monad ; this function will work on the _contained base values_
* * 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 .
2017-11-17 21:43:50 +01:00
* *
2017-11-18 03:00:59 +01:00
* * # # Rationale
2017-11-17 21:43:50 +01:00
* * 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
2017-11-18 03:00:59 +01:00
* * an abstracted data sequence . While the transformation to apply can be selected at runtime ( as a functor ) ,
* * the monad pattern defines a sane way to represent partial evaluation state without requiring a container
* * for intermediary results . This is especially helpful when
2017-11-17 21:43:50 +01:00
* * - 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 )
2017-11-18 03:00:59 +01:00
* * - and a partial evaluation needs to be stored as continuation ( not relying on the stack for partial results )
2017-11-17 21:43:50 +01:00
* *
2017-12-06 02:02:22 +01:00
* * # A Pipeline builder
* * Based on such concepts , structures and evaluation patterns , the TreeExplorer serves the purpose to provide
* * building blocks to assemble a _processing pipeline_ , where processing will happen _on demand , _ while iterating .
* * TreeExplorer itself is both a Lumiera Forward Iterator based on some wrapped data source , and at the same time
* * it is a builder to chain up processing steps to work on the data pulled from that source . These processing steps
* * are attached as _decorators_ wrapping the source , in the order the corresponding builder functions were invoked .
* * - the _expand operation_ installs a function to consume one element and replace it by the sequence of elements
* * ( ` ` children ' ' ) produced by that _ » expansion functor « _ . But this expansion does not happen automatically and
* * on each element , rather it is triggered by issuing a dedicated ` expandChildren ( ) ` call on the processing
* * pipeline . Thus , binding the expansion functor has augmented the data source with the ability to explore
* * some part in more detail _when required .
* * - the _transform operation_ installs a function to be mapped onto each element retrieved from the underlying source
* * - in a similar vein , the _filter operation_ binds a predicate to decide about using or discarding data
* * - in concert , expand - and transform operation allow to build hierarchy evaluation algorithms without exposing
* * any knowledge regarding the concrete hierarchy used and explored as data source .
* *
2017-11-18 03:00:59 +01:00
* * @ todo WIP - WIP - WIP initial draft as of 11 / 2017
2017-11-17 21:43:50 +01:00
* *
2017-11-18 03:00:59 +01:00
* * @ see IterTreeExplorer_test
2017-11-17 21:43:50 +01:00
* * @ see iter - adapter . hpp
* * @ see itertools . hpp
* * @ see IterSource ( completely opaque iterator )
* *
*/
# ifndef LIB_ITER_TREE_EXPLORER_H
# define LIB_ITER_TREE_EXPLORER_H
# include "lib/error.hpp"
2017-11-19 02:28:48 +01:00
# include "lib/meta/duck-detector.hpp"
2017-11-19 20:36:19 +01:00
# include "lib/meta/function.hpp"
2017-12-03 05:52:26 +01:00
# include "lib/meta/trait.hpp"
2017-11-30 03:52:32 +01:00
# include "lib/wrapper.hpp" ////////////TODO : could be more lightweight by splitting FunctionResult into separate header. Relevant?
2017-11-17 21:43:50 +01:00
# include "lib/iter-adapter.hpp"
2017-12-07 05:48:36 +01:00
# include "lib/iter-source.hpp" /////////////TICKET #493 : only using the IterSource base feature / interface here. Should really split the iter-source.hpp
2017-11-17 21:43:50 +01:00
# include "lib/iter-stack.hpp"
# include "lib/util.hpp"
2017-12-03 01:10:58 +01:00
# include "lib/test/test-helper.hpp" ///////////TODO Bug-o
2017-11-17 21:43:50 +01:00
2017-11-19 20:36:19 +01:00
# include <functional>
2017-12-03 05:52:26 +01:00
# include <utility>
2017-11-17 21:43:50 +01:00
namespace lib {
2017-11-19 20:36:19 +01:00
using std : : move ;
using std : : forward ;
using std : : function ;
2017-11-25 02:43:53 +01:00
using util : : isnil ;
2017-11-17 21:43:50 +01:00
2017-11-24 19:45:16 +01:00
namespace iter_explorer {
2017-11-18 03:00:59 +01:00
2017-11-19 17:35:00 +01:00
template < class CON >
using iterator = typename meta : : Strip < CON > : : TypeReferred : : iterator ;
template < class CON >
using const_iterator = typename meta : : Strip < CON > : : TypeReferred : : const_iterator ;
2017-11-18 18:26:59 +01:00
2017-11-19 17:35:00 +01:00
template < class CON >
struct StlRange
: RangeIter < iterator < CON > >
{
StlRange ( ) = default ;
StlRange ( CON & container )
: RangeIter < iterator < CON > > { begin ( container ) , end ( container ) }
{ }
// standard copy operations acceptable
} ;
template < class CON >
struct StlRange < const CON >
: RangeIter < const_iterator < CON > >
{
StlRange ( ) = default ;
StlRange ( CON const & container )
: RangeIter < const_iterator < CON > > { begin ( container ) , end ( container ) }
{ }
// standard copy operations acceptable
} ;
2017-11-18 18:26:59 +01:00
2017-11-20 01:02:30 +01:00
/**
* Decorate a state or logic core to treat it as Lumiera Forward Iterator .
* This Adapter does essentially the same as \ ref IterStateWrapper , but here
* the state core is not encapsulated opaque , but rather inherited , and thus
* the full interface of the core remains publicly accessible .
*/
template < typename T , class COR >
class IterableDecorator
: public COR
{
COR & _core ( ) { return static_cast < COR & > ( * this ) ; }
COR const & _core ( ) const { return static_cast < COR const & > ( * this ) ; }
void
__throw_if_empty ( ) const
{
if ( not isValid ( ) )
throw lumiera : : error : : Invalid ( " Can't iterate further " ,
lumiera : : error : : LUMIERA_ERROR_ITER_EXHAUST ) ;
}
public :
typedef T * pointer ;
typedef T & reference ;
typedef T value_type ;
2017-12-06 00:43:43 +01:00
/** by default, pass anything down for initialisation of the core.
* @ note especially this allows move - initialisation from an existing core .
* @ remarks to prevent this rule from " eating " the standard copy operations ,
* and the no - op default ctor , we need to declare them explicitly below .
*/
2017-11-20 01:02:30 +01:00
template < typename . . . ARGS >
IterableDecorator ( ARGS & & . . . init )
: COR ( std : : forward < ARGS > ( init ) . . . )
{ }
IterableDecorator ( ) = default ;
IterableDecorator ( IterableDecorator & & ) = default ;
IterableDecorator ( IterableDecorator const & ) = default ;
IterableDecorator & operator = ( IterableDecorator & & ) = default ;
IterableDecorator & operator = ( IterableDecorator const & ) = default ;
2017-12-03 04:24:02 +01:00
explicit operator bool ( ) const { return isValid ( ) ; }
2017-11-20 01:02:30 +01:00
/* === lumiera forward iterator concept === */
reference
operator * ( ) const
{
__throw_if_empty ( ) ;
2017-12-05 03:28:00 +01:00
return _core ( ) . yield ( ) ; // core interface: yield
2017-11-20 01:02:30 +01:00
}
pointer
operator - > ( ) const
{
__throw_if_empty ( ) ;
2017-12-05 03:28:00 +01:00
return & _core ( ) . yield ( ) ; // core interface: yield
2017-11-20 01:02:30 +01:00
}
IterableDecorator &
operator + + ( )
{
__throw_if_empty ( ) ;
2017-12-05 03:28:00 +01:00
_core ( ) . iterNext ( ) ; // core interface: iterNext
2017-11-20 01:02:30 +01:00
return * this ;
}
bool
isValid ( ) const
{
2017-12-05 03:28:00 +01:00
return _core ( ) . checkPoint ( ) ; // core interface: checkPoint
2017-11-20 01:02:30 +01:00
}
bool
empty ( ) const
{
return not isValid ( ) ;
}
ENABLE_USE_IN_STD_RANGE_FOR_LOOPS ( IterableDecorator ) ;
/// Supporting equality comparisons of equivalent iterators (same state core)...
template < class T1 , class T2 >
friend bool
operator = = ( IterableDecorator < T1 , COR > const & il , IterableDecorator < T2 , COR > const & ir )
{
return ( il . empty ( ) and ir . empty ( ) )
or ( il . isValid ( ) and ir . isValid ( ) and il . _core ( ) = = ir . _core ( ) ) ;
}
template < class T1 , class T2 >
friend bool
operator ! = ( IterableDecorator < T1 , COR > const & il , IterableDecorator < T2 , COR > const & ir )
{
return not ( il = = ir ) ;
}
} ;
2017-11-17 21:43:50 +01:00
} //(End) namespace iter_explorer : predefined policies and configurations
2017-11-19 17:35:00 +01:00
2017-11-19 20:36:19 +01:00
2017-12-06 02:02:22 +01:00
2017-11-19 17:35:00 +01:00
namespace { // TreeExplorer traits
2017-11-18 18:26:59 +01:00
2017-11-18 19:28:57 +01:00
using meta : : enable_if ;
2017-11-24 19:45:16 +01:00
using meta : : disable_if ;
2017-11-19 02:28:48 +01:00
using meta : : Yes_t ;
using meta : : No_t ;
2017-11-20 01:02:30 +01:00
using meta : : _Fun ;
2017-11-18 19:28:57 +01:00
using std : : __and_ ;
using std : : __not_ ;
2017-12-03 01:10:58 +01:00
using std : : is_convertible ;
using std : : remove_reference_t ;
2017-11-18 19:28:57 +01:00
using meta : : can_IterForEach ;
using meta : : can_STL_ForEach ;
2017-12-05 04:34:24 +01:00
2017-12-05 03:28:00 +01:00
META_DETECT_FUNCTION_ARGLESS ( checkPoint ) ;
META_DETECT_FUNCTION_ARGLESS ( iterNext ) ;
META_DETECT_FUNCTION_ARGLESS ( yield ) ;
2017-11-19 02:28:48 +01:00
2017-11-18 19:28:57 +01:00
template < class SRC >
struct is_StateCore
2017-12-05 03:28:00 +01:00
: __and_ < HasArglessFun_checkPoint < SRC >
, HasArglessFun_iterNext < SRC >
, HasArglessFun_yield < SRC >
2017-11-19 02:28:48 +01:00
>
2017-11-18 19:28:57 +01:00
{ } ;
template < class SRC >
struct shall_wrap_STL_Iter
: __and_ < can_STL_ForEach < SRC >
, __not_ < can_IterForEach < SRC > >
>
{ } ;
2017-12-03 01:10:58 +01:00
template < class SRC >
struct shall_use_StateCore
: __and_ < __not_ < can_IterForEach < SRC > >
, is_StateCore < SRC >
>
{ } ;
2017-11-27 04:59:52 +01:00
template < class SRC >
struct shall_use_Lumiera_Iter
: __and_ < can_IterForEach < SRC >
, __not_ < is_StateCore < SRC > >
>
{ } ;
2017-11-18 19:28:57 +01:00
2017-12-03 01:10:58 +01:00
/** the _value type_ yielded by a »state core« */
template < class COR >
struct CoreYield
{
2017-12-05 03:28:00 +01:00
using Res = remove_reference_t < decltype ( std : : declval < COR > ( ) . yield ( ) ) > ;
2017-12-03 01:10:58 +01:00
using value_type = typename meta : : TypeBinding < Res > : : value_type ;
using reference = typename meta : : TypeBinding < Res > : : reference ;
using pointer = typename meta : : TypeBinding < Res > : : pointer ;
} ;
2017-11-27 05:39:47 +01:00
/** decide how to adapt and embed the source sequence into the resulting TreeExplorer */
2017-11-18 18:26:59 +01:00
template < class SRC , typename SEL = void >
2017-11-30 03:52:32 +01:00
struct _DecoratorTraits
2017-11-18 18:26:59 +01:00
{
2017-11-18 19:28:57 +01:00
static_assert ( ! sizeof ( SRC ) , " Can not build TreeExplorer: Unable to figure out how to iterate the given SRC type. " ) ;
} ;
template < class SRC >
2017-11-30 03:52:32 +01:00
struct _DecoratorTraits < SRC , enable_if < is_StateCore < SRC > > >
2017-11-18 19:28:57 +01:00
{
2017-12-03 01:10:58 +01:00
using SrcVal = typename CoreYield < SRC > : : value_type ;
2017-11-24 19:45:16 +01:00
using SrcIter = iter_explorer : : IterableDecorator < SrcVal , SRC > ;
2017-11-18 19:28:57 +01:00
} ;
template < class SRC >
2017-11-30 03:52:32 +01:00
struct _DecoratorTraits < SRC , enable_if < shall_use_Lumiera_Iter < SRC > > >
2017-11-18 19:28:57 +01:00
{
2017-12-03 01:10:58 +01:00
using SrcIter = remove_reference_t < SRC > ;
2017-11-30 03:52:32 +01:00
using SrcVal = typename SrcIter : : value_type ;
2017-11-18 19:28:57 +01:00
} ;
template < class SRC >
2017-11-30 03:52:32 +01:00
struct _DecoratorTraits < SRC , enable_if < shall_wrap_STL_Iter < SRC > > >
2017-11-18 19:28:57 +01:00
{
2017-11-19 17:35:00 +01:00
static_assert ( not std : : is_rvalue_reference < SRC > : : value ,
" container needs to exist elsewhere during the lifetime of the iteration " ) ;
2017-11-24 19:45:16 +01:00
using SrcIter = iter_explorer : : StlRange < SRC > ;
2017-11-30 03:52:32 +01:00
using SrcVal = typename SrcIter : : value_type ;
2017-11-20 01:02:30 +01:00
} ;
2017-11-23 03:06:02 +01:00
2017-11-25 03:56:44 +01:00
/**
2017-12-03 05:52:26 +01:00
* @ internal technical details of binding a functor into the TreeExplorer .
* Notably , this happens when adapting an _ " expansion functor " _ to allow expanding a given element
* from the TreeExploer ( iterator ) into a sequence of child elements . A quite similar situation
* arises when binding a _transformation function_ to be mapped onto each result element .
*
2017-11-25 03:56:44 +01:00
* The TreeExplorer : : expand ( ) operation accepts various flavours of functors , and depending on
2017-12-03 05:52:26 +01:00
* the signature of such a functor , an appropriate adapter will be constructed here , allowing to
* write a generic Expander : : expandChildren ( ) operation . The following details are handled here :
2017-11-25 03:56:44 +01:00
* - detect if the passed functor is generic , or a regular " function-like " entity .
* - in case it is generic ( generic lambda ) , we assume it actually accepts a reference to
* the source iterator type ` SRC ` . Thus we instantiate a templated functor with this
* argument type to find out about its result type ( and this instantiation may fail )
* - moreover , we try to determine , if an explicitly typed functor accepts a value of the
* embedded source iterator ( this is the " monadic " usage pattern ) , or if it rather accepts
* the iterator or state core itself ( the " opaque state manipulation " usage pattern ) .
* - we generate a suitable argument accessor function and build the function composition
* of this accessor and the provided _expansion functor_ .
* - the resulting , combined functor is stored into a std : : function , but wired in a way to
* keep the argument - accepting front - end still generic ( templated ` operator ( ) ` ) . This
* special adapter supports the case when the _expansion functor_ yields a child sequence
* type different but compatible to the original source sequence embedded in TreeExplorer .
2017-11-30 03:52:32 +01:00
* @ tparam FUN something _ " function-like " _ passed as functor to be bound
2017-12-03 05:52:26 +01:00
* @ tparam SRC the source iterator type to apply when attempting to use a generic lambda as functor
2017-11-25 03:56:44 +01:00
*/
2017-11-25 02:16:21 +01:00
template < class FUN , typename SRC >
2017-12-03 05:52:26 +01:00
struct _BoundFunctorTraits
2017-11-20 01:02:30 +01:00
{
2017-11-25 03:56:44 +01:00
/** handle all regular "function-like" entities */
2017-11-24 19:45:16 +01:00
template < typename F , typename SEL = void >
struct FunDetector
{
using Sig = typename _Fun < F > : : Sig ;
} ;
2017-11-25 03:56:44 +01:00
/** handle a generic lambda, accepting a reference to the `SRC` iterator */
2017-11-24 19:45:16 +01:00
template < typename F >
struct FunDetector < F , disable_if < _Fun < F > > >
{
2017-11-25 02:16:21 +01:00
using Arg = typename std : : add_lvalue_reference < SRC > : : type ;
using Ret = decltype ( std : : declval < F > ( ) ( std : : declval < Arg > ( ) ) ) ;
using Sig = Ret ( Arg ) ;
2017-11-24 19:45:16 +01:00
} ;
2017-11-25 03:56:44 +01:00
2017-11-24 19:45:16 +01:00
using Sig = typename FunDetector < FUN > : : Sig ;
2017-12-03 01:10:58 +01:00
using Arg = typename _Fun < Sig > : : Args : : List : : Head ; // assuming function with a single argument
2017-11-25 02:43:53 +01:00
using Res = typename _Fun < Sig > : : Ret ;
2017-11-24 19:45:16 +01:00
2017-11-25 02:16:21 +01:00
2017-11-25 03:56:44 +01:00
/** adapt to a functor, which accesses the source iterator or embedded "state core" */
2017-11-25 02:43:53 +01:00
template < class ARG , class SEL = void >
struct ArgAccessor
{
2017-12-03 01:10:58 +01:00
using FunArgType = remove_reference_t < Arg > ;
2017-11-25 02:43:53 +01:00
static_assert ( std : : is_convertible < ARG , FunArgType > : : value ,
" the expansion functor must accept the source iterator or state core as parameter " ) ;
static auto build ( ) { return [ ] ( ARG & arg ) - > ARG & { return arg ; } ; }
} ;
2017-11-23 03:06:02 +01:00
2017-11-25 03:56:44 +01:00
/** adapt to a functor, which accepts the value type of the source sequence ("monadic" usage pattern) */
2017-11-25 02:43:53 +01:00
template < class IT >
2017-12-03 04:24:02 +01:00
struct ArgAccessor < IT , enable_if < is_convertible < typename IT : : value_type , Arg > > >
2017-11-25 02:43:53 +01:00
{
2017-11-26 22:29:51 +01:00
static auto build ( ) { return [ ] ( auto & iter ) { return * iter ; } ; }
2017-11-25 02:43:53 +01:00
} ;
2017-11-25 03:56:44 +01:00
/** holder for the suitably adapted _expansion functor_ */
2017-11-25 02:43:53 +01:00
struct Functor
{
2017-12-03 01:10:58 +01:00
function < Sig > boundFunction ;
2017-11-25 02:43:53 +01:00
template < typename ARG >
2017-11-27 02:07:04 +01:00
Res
2017-11-25 02:43:53 +01:00
operator ( ) ( ARG & arg )
{
auto accessArg = ArgAccessor < ARG > : : build ( ) ;
2017-12-03 01:10:58 +01:00
return boundFunction ( accessArg ( arg ) ) ;
2017-11-25 02:43:53 +01:00
}
} ;
2017-11-18 18:26:59 +01:00
} ;
} //(End) TreeExplorer traits
2017-11-17 21:43:50 +01:00
2017-12-03 05:52:26 +01:00
2017-11-24 19:45:16 +01:00
namespace iter_explorer {
2017-11-19 20:36:19 +01:00
2017-12-06 00:43:43 +01:00
/**
* @ internal Base of pipe processing decorator chain .
* TreeExplorer allows to create a stack out of various decorating processors
* - each decorator is itself a _ " state core " _ , adding some on - demand processing
* - each wraps and adapts a source iterator , attaching to and passing - on the iteration logic
* Typically each such layer is configured with actual functionality provided as lambda or functor .
* Yet in addition to forming an iteration pipeline , there is kind of an internal interconnection
* protocol , allowing the layers to collaborate ; notably this allows to handle an expandChildren ( )
* call , where some " expansion layer " consumes the current element and replaces it by an expanded
* series of new elements . Other layers might need to sync to this operation , and thus it is passed
* down the chain . For that reason , we need a dedicated BaseAdapter to adsorb such chained calls .
*/
template < class SRC >
struct BaseAdapter
: SRC
{
using SRC : : SRC ;
2017-12-07 05:48:36 +01:00
BaseAdapter ( SRC const & src ) : SRC ( src ) { } ////////////////////////TODO why the hell do we need to redeclare all those ctor variants????
BaseAdapter ( SRC & & src ) : SRC ( src ) { }
BaseAdapter ( SRC & src ) : SRC ( src ) { }
BaseAdapter ( ) = default ;
2017-12-06 00:43:43 +01:00
void expandChildren ( ) { }
} ;
2017-11-25 03:56:44 +01:00
/**
* @ internal Decorator for TreeExplorer adding the ability to " expand children " .
2017-12-03 05:52:26 +01:00
* The expandChildren ( ) operation is the key element of a depth - first evaluation : it consumes
2017-11-25 03:56:44 +01:00
* one element and performs a preconfigured _expansion functor_ on that element to yield
* its " children " . These are given in the form of another iterator , which needs to be
* compatible to the source iterator ( " compatibility " boils down to both iterators
* yielding a compatible value type ) . Now , this _sequence of children_ effectively
* replaces the expanded source element in the overall resulting sequence ; which
* means , the nested sequence was _flattened_ into the results . Since this expand ( )
* operation can again invoked on the results , the implementation of such an evaluation
* requires a stack datastructure , so the nested iterator from each expand ( ) invocation
* can be pushed to become the new active source for iteration . Thus the primary purpose
* of this Expander ( decorator ) is to integrate those " nested child iterators " seamlessly
* into the overall iteration process ; once a child iterator is exhausted , it will be
* popped and iteration continues with the previous child iterator or finally with
* the source iterator wrapped by this decorator .
* @ remark since we allow a lot of leeway regarding the actual form and definition of the
* _expansion functor_ , there is a lot of minute technical details , mostly confined
2017-12-03 05:52:26 +01:00
* within the _BoundFunctorTraits .
2017-11-25 03:56:44 +01:00
* @ tparam SRC the wrapped source iterator , typically a TreeExplorer or nested decorator .
* @ tparam FUN the concrete type of the functor passed . Will be dissected to find the signature
*/
2017-11-24 19:45:16 +01:00
template < class SRC , class FUN >
2017-11-19 20:36:19 +01:00
class Expander
2017-11-20 01:02:30 +01:00
: public SRC
2017-11-19 20:36:19 +01:00
{
2017-12-03 04:24:02 +01:00
static_assert ( can_IterForEach < SRC > : : value , " Lumiera Iterator required as source " ) ;
2017-12-03 05:52:26 +01:00
using _Traits = _BoundFunctorTraits < FUN , SRC > ;
2017-11-25 02:43:53 +01:00
using ExpandFunctor = typename _Traits : : Functor ;
2017-11-20 01:02:30 +01:00
2017-11-30 03:52:32 +01:00
using ResIter = typename _DecoratorTraits < typename _Traits : : Res > : : SrcIter ;
static_assert ( std : : is_convertible < typename ResIter : : value_type , typename SRC : : value_type > : : value ,
" the iterator from the expansion must yield compatible values " ) ;
2017-11-23 03:06:02 +01:00
ExpandFunctor expandChildren_ ;
2017-11-25 02:43:53 +01:00
IterStack < ResIter > expansions_ ;
2017-11-19 20:36:19 +01:00
public :
Expander ( ) = default ;
// inherited default copy operations
2017-11-20 01:02:30 +01:00
Expander ( SRC & & parentExplorer , FUN & & expandFunctor )
2017-12-03 04:24:02 +01:00
: SRC { move ( parentExplorer ) } // NOTE: slicing move to strip TreeExplorer (Builder)
2017-11-19 20:36:19 +01:00
, expandChildren_ { forward < FUN > ( expandFunctor ) }
2017-11-20 01:02:30 +01:00
, expansions_ { }
2017-11-19 20:36:19 +01:00
{ }
/** core operation: expand current head element */
2017-12-06 00:43:43 +01:00
void
2017-12-03 04:24:02 +01:00
expandChildren ( )
2017-11-19 20:36:19 +01:00
{
2017-12-05 03:28:00 +01:00
REQUIRE ( this - > checkPoint ( ) , " attempt to expand an empty explorer " ) ;
2017-11-20 01:02:30 +01:00
2017-11-27 03:22:34 +01:00
ResIter expanded { 0 < depth ( ) ? expandChildren_ ( * expansions_ )
: expandChildren_ ( * this ) } ;
2017-12-05 03:28:00 +01:00
iterNext ( ) ; // consume current head element
2017-11-25 02:43:53 +01:00
if ( not isnil ( expanded ) )
2017-11-20 01:02:30 +01:00
expansions_ . push ( move ( expanded ) ) ;
2017-12-06 02:02:22 +01:00
SRC : : expandChildren ( ) ;
2017-11-19 20:36:19 +01:00
}
/** diagnostics: current level of nested child expansion */
size_t
depth ( ) const
{
2017-11-20 01:02:30 +01:00
return expansions_ . size ( ) ;
2017-11-19 20:36:19 +01:00
}
2017-11-20 01:02:30 +01:00
2017-11-25 03:56:44 +01:00
2017-12-05 03:28:00 +01:00
public : /* === Iteration control API for IterableDecorator === */
2017-11-20 01:02:30 +01:00
2017-12-05 03:28:00 +01:00
bool
checkPoint ( ) const
{
return 0 < depth ( )
or this - > isValid ( ) ;
}
2017-11-20 01:02:30 +01:00
2017-12-05 03:28:00 +01:00
typename SRC : : reference
yield ( ) const
{
return 0 < depth ( ) ? * * expansions_
: * * this ;
}
2017-11-20 01:02:30 +01:00
2017-12-05 03:28:00 +01:00
void
iterNext ( )
{
if ( 0 < depth ( ) )
{
+ + ( * expansions_ ) ;
while ( 0 < depth ( ) and not * expansions_ )
+ + expansions_ ;
}
else
+ + ( * this ) ;
}
2017-11-19 20:36:19 +01:00
} ;
2017-11-30 03:52:32 +01:00
/**
2017-12-03 05:52:26 +01:00
* @ internal Decorator for TreeExplorer to map a transformation function on all results .
* The transformation function is invoked on demand , and only once per item to be treated ,
* storing the treated result into an universal value holder buffer . The given functor
* is adapted in a similar way as the " expand functor " , so to detect and convert the
* expected input on invocation .
2017-11-30 03:52:32 +01:00
*/
2017-12-03 04:24:02 +01:00
template < class SRC , class FUN >
2017-11-30 03:52:32 +01:00
class Transformer
2017-12-03 04:24:02 +01:00
: public SRC
2017-11-30 03:52:32 +01:00
{
2017-12-03 04:24:02 +01:00
static_assert ( can_IterForEach < SRC > : : value , " Lumiera Iterator required as source " ) ;
2017-12-03 05:52:26 +01:00
using _Traits = _BoundFunctorTraits < FUN , SRC > ;
2017-11-30 03:52:32 +01:00
using Res = typename _Traits : : Res ;
using TransformFunctor = typename _Traits : : Functor ;
using TransformedItem = wrapper : : ItemWrapper < Res > ;
TransformFunctor trafo_ ;
TransformedItem treated_ ;
public :
2017-12-02 02:41:27 +01:00
using value_type = typename meta : : TypeBinding < Res > : : value_type ;
using reference = typename meta : : TypeBinding < Res > : : reference ;
2017-12-03 01:10:58 +01:00
using pointer = typename meta : : TypeBinding < Res > : : pointer ;
2017-11-30 03:52:32 +01:00
Transformer ( ) = default ;
// inherited default copy operations
2017-12-03 04:24:02 +01:00
Transformer ( SRC & & dataSrc , FUN & & transformFunctor )
: SRC { move ( dataSrc ) }
2017-11-30 03:52:32 +01:00
, trafo_ { forward < FUN > ( transformFunctor ) }
{ }
2017-12-06 00:43:43 +01:00
/** refresh state when other layers manipulate the source sequence
* @ remark expansion replaces the current element by a sequence of
* " child " elements . Since we cache our transformation , we
* need to ensure possibly new source elements get processed
*/
void
expandChildren ( )
{
treated_ . reset ( ) ;
SRC : : expandChildren ( ) ;
}
2017-12-05 03:28:00 +01:00
public : /* === Iteration control API for IterableDecorator === */
2017-11-30 03:52:32 +01:00
2017-12-05 03:28:00 +01:00
bool
checkPoint ( ) const
{
return bool ( srcIter ( ) ) ;
}
2017-11-30 03:52:32 +01:00
2017-12-05 03:28:00 +01:00
reference
yield ( ) const
{
return unConst ( this ) - > invokeTransformation ( ) ;
}
2017-11-30 03:52:32 +01:00
2017-12-05 03:28:00 +01:00
void
iterNext ( )
{
+ + srcIter ( ) ;
treated_ . reset ( ) ;
}
2017-11-30 03:52:32 +01:00
private :
2017-12-03 04:24:02 +01:00
SRC &
srcIter ( ) const
2017-11-30 03:52:32 +01:00
{
return unConst ( * this ) ;
}
reference
2017-12-03 01:10:58 +01:00
invokeTransformation ( )
2017-11-30 03:52:32 +01:00
{
2017-12-03 04:24:02 +01:00
if ( not treated_ ) // invoke transform function once per src item
treated_ = trafo_ ( srcIter ( ) ) ;
2017-11-30 03:52:32 +01:00
return * treated_ ;
}
} ;
2017-12-06 02:33:32 +01:00
/**
* @ internal Decorator for TreeExplorer to filter elements based on a predicate .
2017-12-07 02:18:47 +01:00
* Similar to the Transformer , the given functor is adapted as appropriate . However ,
* we require the functor ' s result type to be convertible to bool , to serve as approval test .
* The filter predicate and thus the source iterator is evaluated _eagerly_ , to establish the
* * invariant * of this class : _if a " current element " exists , it has already been approved . _
2017-12-06 02:33:32 +01:00
*/
template < class SRC , class FUN >
class Filter
: public SRC
{
static_assert ( can_IterForEach < SRC > : : value , " Lumiera Iterator required as source " ) ;
using _Traits = _BoundFunctorTraits < FUN , SRC > ;
using Res = typename _Traits : : Res ;
static_assert ( std : : is_constructible < bool , Res > : : value , " Functor must be a predicate " ) ;
using FilterPredicate = typename _Traits : : Functor ;
2017-12-07 01:37:03 +01:00
FilterPredicate predicate_ ;
2017-12-06 02:33:32 +01:00
public :
Filter ( ) = default ;
// inherited default copy operations
Filter ( SRC & & dataSrc , FUN & & filterFun )
: SRC { move ( dataSrc ) }
2017-12-07 01:37:03 +01:00
, predicate_ { forward < FUN > ( filterFun ) }
{
pullFilter ( ) ; // initially pull to establish the invariant
}
2017-12-06 02:33:32 +01:00
2017-12-07 01:37:03 +01:00
/** refresh state when other layers manipulate the source sequence.
* @ note possibly pulls to re - establish the invariant */
2017-12-06 02:33:32 +01:00
void
expandChildren ( )
{
2017-12-07 01:37:03 +01:00
pullFilter ( ) ;
2017-12-06 02:33:32 +01:00
SRC : : expandChildren ( ) ;
}
public : /* === Iteration control API for IterableDecorator === */
bool
checkPoint ( ) const
{
2017-12-07 01:37:03 +01:00
return bool ( srcIter ( ) ) ;
2017-12-06 02:33:32 +01:00
}
typename SRC : : reference
yield ( ) const
{
2017-12-07 01:37:03 +01:00
return * srcIter ( ) ;
2017-12-06 02:33:32 +01:00
}
void
iterNext ( )
{
+ + srcIter ( ) ;
2017-12-07 01:37:03 +01:00
pullFilter ( ) ;
2017-12-06 02:33:32 +01:00
}
private :
SRC &
srcIter ( ) const
{
return unConst ( * this ) ;
}
2017-12-07 01:37:03 +01:00
/** @note establishes the invariant:
* whatever the source yields as current element ,
* has already been approved by our predicate */
void
2017-12-06 02:33:32 +01:00
pullFilter ( )
{
2017-12-07 01:37:03 +01:00
while ( srcIter ( ) and not predicate_ ( srcIter ( ) ) )
+ + srcIter ( ) ;
2017-12-06 02:33:32 +01:00
}
} ;
2017-11-19 20:36:19 +01:00
}
2017-12-07 05:48:36 +01:00
/**
* @ todo WIP 12 / 2017
*/
template < typename VAL >
struct IterExploreSource
: IterSource < VAL > : : iterator
{
//////TODO clarify allowed ctors
void
expandChildren ( )
{
UNIMPLEMENTED ( " how to phone home... " ) ;
}
private :
} ;
2017-11-19 20:36:19 +01:00
/**
* Adapter to build a demand - driven tree expanding and exploring computation
* based on a custom opaque _state core_ . TreeExploer adheres to the _Monad_
* pattern known from functional programming , insofar the _expansion step_ is
* tied into the basic template by means of a function provided at usage site .
2017-12-03 05:52:26 +01:00
* This allows to separate the mechanics of evaluation and result combination
* from the actual processing and thus to define tree structured computations
* based on a not further disclosed , opaque source data structure .
2017-11-19 20:36:19 +01:00
*
* @ todo WIP - - preliminary draft as of 11 / 2017
*/
2017-11-25 02:43:53 +01:00
template < class SRC >
2017-11-19 20:36:19 +01:00
class TreeExplorer
: public SRC
{
2017-12-03 04:24:02 +01:00
static_assert ( can_IterForEach < SRC > : : value , " Lumiera Iterator required as source " ) ;
2017-11-19 20:36:19 +01:00
public :
2017-12-03 04:24:02 +01:00
using value_type = typename meta : : TypeBinding < SRC > : : value_type ;
using reference = typename meta : : TypeBinding < SRC > : : reference ;
using pointer = typename meta : : TypeBinding < SRC > : : pointer ;
2017-11-19 20:36:19 +01:00
2017-12-06 00:43:43 +01:00
/** pass-through ctor */
using SRC : : SRC ;
2017-12-07 05:48:36 +01:00
TreeExplorer ( SRC const & src ) : SRC ( src ) { } ////////////////////////TODO why the hell do we need to redeclare all those ctor variants???? why isn't copy initialisation propagated from the base ctors?
TreeExplorer ( SRC & & src ) : SRC ( src ) { }
TreeExplorer ( SRC & src ) : SRC ( src ) { }
TreeExplorer ( ) = default ;
2017-11-19 20:36:19 +01:00
2017-11-25 03:56:44 +01:00
2017-11-19 20:36:19 +01:00
/* ==== Builder functions ==== */
2017-11-25 03:56:44 +01:00
/** preconfigure this TreeExplorer to allow for _"expansion of children"_.
* The resulting iterator exposes an ` expand ( ) ` function , which consumes
* the current head element of this iterator and feeds it through the
* _expansion functor_ , which was provided to this builder function here .
* The _expansion functor_ is expected to yield a sequence of " child " elements ,
* which will be integrated into the overall result sequence instead of the
* consumed source element . Thus , repeatedly invoking ` expand ( ) ` until exhaustion
* generates a _depth - first evaluation_ , since every child will be expanded until
* reaching the leaf nodes of a tree like structure .
*
* @ param expandFunctor a " function-like " entity to perform the actual " expansion " .
* There are two distinct usage patterns , as determined by the signature
* of the provided function or functor :
* - _ " monad style " _ : the functor takes a _value_ from the sequence and
* produces a new sequence , iterator or collection of compatible values
* - _ " opaque state manipulation " _ : the functor accepts the concrete source
* iterator type , or even a " state core " type embedded therein . It yields
* a new sequence , state core or collection representing the " children " .
* Obviously , the intention here is to allow hidden collaboration between
* the expansion functor and the embedded opaque " data source " . For that
* reason , the functor may take its argument by reference , and a produced
* new " child state core " may likewise collaborate with that original
* data source or state core behind the scenes ; the latter is guaranteed
* to exist during the whole lifetime of this TreeExplorer .
* @ note there is limited support for generic lambdas , but only for the second case .
* The reason is , we can not " probe " a template or generic lambda for possible
* argument and result types . Thus , if you provide a generic lambda , TreeExplorer
* tries to pass it a ` SrcIter & ` ( reference to the embedded original iterator ) .
* For any other cases , please provide a lambda or functor with a single , explicitly
* typed argument . Obviously , argument and result type should also make sense for
* the desired evaluation pattern , otherwise you ' ll get all kinds of nasty
* template compilation failures ( have fun ! )
*/
2017-11-19 20:36:19 +01:00
template < class FUN >
auto
expand ( FUN & & expandFunctor )
{
2017-12-03 04:24:02 +01:00
using ResCore = iter_explorer : : Expander < SRC , FUN > ;
using ResIter = typename _DecoratorTraits < ResCore > : : SrcIter ;
2017-11-19 20:36:19 +01:00
2017-12-03 04:24:02 +01:00
return TreeExplorer < ResIter > ( ResCore { move ( * this ) , forward < FUN > ( expandFunctor ) } ) ;
2017-11-19 20:36:19 +01:00
}
2017-11-25 02:43:53 +01:00
2017-11-28 03:37:34 +01:00
2017-12-03 05:52:26 +01:00
/** adapt this TreeExplorer to pipe each result value through a transformation function.
* Several " layers " of mapping can be piled on top of each other , possibly mixed with the
* other types of adaptation , like the child - expanding operation , or a filter . Obviously ,
* when building such processing pipelines , the input and output types of the functors
* bound into the pipeline need to be compatible or convertible . The transformation
* functor supports the same definition styles as described for # expand
* - it can be pure functional , src - > res
* - it can accept the underlying source iterator and exploit side - effects
2017-11-28 03:37:34 +01:00
*/
template < class FUN >
auto
transform ( FUN & & transformFunctor )
{
2017-12-03 04:24:02 +01:00
using ResCore = iter_explorer : : Transformer < SRC , FUN > ;
using ResIter = typename _DecoratorTraits < ResCore > : : SrcIter ;
2017-11-30 03:52:32 +01:00
2017-12-03 04:24:02 +01:00
return TreeExplorer < ResIter > ( ResCore { move ( * this ) , forward < FUN > ( transformFunctor ) } ) ;
2017-11-28 03:37:34 +01:00
}
2017-12-06 02:33:32 +01:00
2017-12-07 02:18:47 +01:00
/** adapt this TreeExplorer to filter results, by invoking the given functor to approve them.
* The previously created source layers will be " pulled " to fast - forward immediately to the
* next element confirmed this way by the bound functor . If none of the source elements
* is acceptable , the iterator will transition to exhausted state immediately .
2017-12-06 02:33:32 +01:00
*/
template < class FUN >
auto
filter ( FUN & & filterPredicate )
{
using ResCore = iter_explorer : : Filter < SRC , FUN > ;
using ResIter = typename _DecoratorTraits < ResCore > : : SrcIter ;
return TreeExplorer < ResIter > ( ResCore { move ( * this ) , forward < FUN > ( filterPredicate ) } ) ;
}
2017-12-07 05:48:36 +01:00
/** @todo package as IterSource
*/
IterExploreSource < value_type >
asIterSource ( )
{
UNIMPLEMENTED ( " wrap into IterSource " ) ;
}
2017-11-19 20:36:19 +01:00
private :
} ;
2017-11-17 21:43:50 +01:00
/* ==== convenient builder free functions ==== */
2017-11-27 05:39:47 +01:00
/** start building a TreeExplorer
* by suitably wrapping the given iterable source .
* @ return a TreeEplorer , which is an Iterator to yield all the source elements ,
* but may also be used to build an processing pipeline .
*/
2017-11-17 21:43:50 +01:00
template < class IT >
2017-11-18 18:26:59 +01:00
inline auto
treeExplore ( IT & & srcSeq )
2017-11-17 21:43:50 +01:00
{
2017-11-30 03:52:32 +01:00
using SrcIter = typename _DecoratorTraits < IT > : : SrcIter ;
2017-12-06 00:43:43 +01:00
using Base = iter_explorer : : BaseAdapter < SrcIter > ;
2017-11-18 19:28:57 +01:00
2017-12-07 05:48:36 +01:00
return TreeExplorer < Base > ( std : : forward < IT > ( srcSeq ) ) ;
2017-11-17 21:43:50 +01:00
}
} // namespace lib
# endif /* LIB_ITER_TREE_EXPLORER_H */