lumiera_/src/lib/iter-explorer.hpp

335 lines
11 KiB
C++
Raw Normal View History

/*
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 Iterators</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.
**
** @see IterExplorer_test.cpp
** @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/bool-checkable.hpp"
#include "lib/meta/function.hpp"
#include "lib/iter-adapter.hpp"
//#include <boost/type_traits/remove_const.hpp>
namespace lib {
namespace { // internal helpers
using std::tr1::function;
using meta::FunctionSignature;
/**
* Helper to dissect an arbitrary function signature,
* irrespective if the parameter is given as function reference,
* function pointer, member function pointer or functor object.
* The base case assumes a (language) function reference.
*/
template<typename SIG>
struct _Fun
{
typedef typename FunctionSignature<function<SIG> >::Ret Ret;
typedef typename FunctionSignature<function<SIG> >::Args Args;
};
/** Specialisation for using a function pointer */
template<typename SIG>
struct _Fun<SIG*>
{
typedef typename FunctionSignature<function<SIG> >::Ret Ret;
typedef typename FunctionSignature<function<SIG> >::Args Args;
};
/** Specialisation for passing a functor */
template<typename SIG>
struct _Fun<function<SIG> >
{
typedef typename FunctionSignature<function<SIG> >::Ret Ret;
typedef typename FunctionSignature<function<SIG> >::Args Args;
};
}
namespace iter_explorer {
template<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. 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" on-the-fly, without the need to
* allocate heap memory to store intermediary results, but also without
* using the stack and recursive programming.
*/
template<class SRC
,template<class> class _COM_ = iter_explorer::DefaultCombinator
>
class IterExplorer
: public IterStateWrapper<typename SRC::value_type, SRC>
{
typedef typename SRC::value_type value_type;
typedef typename SRC::reference reference;
public:
/** 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 */
template<class FUN>
IterExplorer<_COM_<FUN>, _COM_>
operator >>= (FUN explorer)
{
typedef _COM_<FUN> Combinator;
return IterExplorer(
Combinator(explorer)
.startWith( explorer(accessFirstElement()))
.followUp (accessRemainingElements()));
}
private:
reference
accessFirstElement()
{
return this->operator* ();
}
IterExplorer&
accessRemainingElements()
{
this->operator++ ();
return *this;
}
};
namespace iter_explorer { ///< predefined policies and configurations
/**
* a generic "Combinator strategy" for IterExplorer.
* This fallback solution just assumes that the source is a
* Lumiera Forward Iterator -- consequently we need to do heap
* allocations behind the scenes, as the size and structure of
* the expansion results is determined at runtime.
* @note the whole purpose of IterExplorer is to be more efficient
* then this fallback, thus requiring more specific Combinator
* strategies, able to exploit specific knowledge of the
* source iterator's implementation.
*/
template<class FUN>
class DefaultCombinator
{
typedef typename _Fun<FUN>::Ret ResultIter;
public:
typedef typename ResultIter::value_type value_type;
typedef typename ResultIter::reference reference;
typedef typename ResultIter::pointer pointer;
DefaultCombinator(FUN explorer)
{
UNIMPLEMENTED ("representation of the sequence of partial results");
}
DefaultCombinator &
startWith (ResultIter firstExplorationResult)
{
UNIMPLEMENTED ("DefaultCombinator: yield first result");
return *this;
}
template<class SRC>
DefaultCombinator&
followUp (SRC followUpSourceElements)
{
UNIMPLEMENTED ("DefaultCombinator: follow up iteration");
return *this;
}
/* === Iteration control API for IterStateWrapper== */
friend bool
checkPoint (DefaultCombinator const& sequence)
{
UNIMPLEMENTED ("how to determine exhausted state on the combined results");
return false;
}
friend reference
yield (DefaultCombinator const& sequence)
{
UNIMPLEMENTED ("how to yield a result from the combined iterator");
}
friend void
iterNext (DefaultCombinator & sequence)
{
UNIMPLEMENTED ("increment the source iterator and switch to the next partial result sequence if necessary");
}
};
/**
* Helper template to bootstrap a chain of IterExplorers.
* This is a "state corer", 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 == */
inline bool
checkPoint (WrappedSequence const& sequence)
{
return bool(sequence);
}
inline typename IT::reference
yield (WrappedSequence const& sequence)
{
return *sequence;
}
inline void
iterNext (WrappedSequence & sequence)
{
++sequence;
}
};
}//(End) namespace iter_explorer : predefined policies and configurations
template<class IT>
IterExplorer<iter_explorer::WrappedSequence<IT> >
exploreIter (IT const& srcSeq)
{
return IterExplorer<iter_explorer::WrappedSequence<IT> > (srcSeq);
}
} // namespace lib
#endif