/* ITERTOOLS.hpp - collection of tools for building and combining iterators Copyright (C) Lumiera.org 2009, Hermann Vosseler 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 itertools.hpp ** Helpers for working with iterators based on the pipeline model. ** Iterators abstract from the underlying data container and provide ** the contained data as a source to pull values from. Based on this ** model, we can build pipelines, with filters, valves, junction points ** and transforming facilities. The templates in this header enable such, ** 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 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 ** the filtering or processing functionality, while the base class ** (IterTool) exposes the operations necessary to comply to the ** Forward Iterator Concept. ** ** \par filtering Iterator ** The FilterIter template can be used to build a filter into a pipeline, ** as it forwards only those elements from its source iterator, which pass ** the predicate evaluation. Anything acceptable as ctor parameter for a ** tr1::function object can be passed in as predicate, but of course the ** signature must be sensible. Please note, that -- depending on the ** predicate -- already the ctor or even a simple \c bool test might ** pull and exhaust the source iterator completely, in an attempt ** to find the first element passing the predicate test. ** ** \par processing Iterator ** the TransformIter template can be used as processing (or transforming) ** step within the pipeline. It is created with a functor, which, when ** pulling elements, is invoked for each element pulled from the ** source iterator. The signature of the functor must match the ** desired value (output) type. ** ** @todo some more building blocks are planned, see Ticket #347 ** ** @see iter-adapter.hpp ** @see itertools-test.cpp ** @see contents-query.hpp */ #ifndef LIB_ITERTOOLS_H #define LIB_ITERTOOLS_H #include "lib/bool-checkable.hpp" #include "lib/iter-adapter.hpp" #include "lib/meta/function.hpp" #include "lib/meta/trait.hpp" #include "lib/wrapper.hpp" #include "lib/util.hpp" #include namespace lib { using std::function; using util::unConst; using lib::meta::RefTraits; /** * A neutral \em identity-function core, * also serving as point-of reference how any * core is intended to work. Any core is intended * to serve as inner part of an iterator tool template. * - it provides the trait typedefs * - it abstracts the "source" * - it abstracts the local operation to be performed * - the ctor of the core sets up the configuration. * @note cores should be copyable without much overhead */ template struct IdentityCore { IT source_; IdentityCore (IT const& orig) : source_(orig) { } IT& pipe () { return source_; } IT const& pipe () const { return source_; } void advance () { ++source_; } bool evaluate () const { return bool(source_); } typedef typename IT::pointer pointer; typedef typename IT::reference reference; typedef typename IT::value_type value_type; }; /** * 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 _iteration control_ * building blocks to drive the processing/filter logic, * which is implemented in the specific core for each tool. */ template class IterTool : public lib::BoolCheckable > { protected: /* == iteration control == */ CORE core_; bool hasData() const { return core_.evaluate() || unConst(this)->iterate(); } // to skip irrelevant results doesn't count as "mutation" bool iterate () { if (!core_.pipe()) return false; do core_.advance(); while (core_.pipe() && !core_.evaluate()); return core_.pipe(); } void _maybe_throw() const { if (!isValid()) _throwIterExhausted(); } public: typedef typename CORE::pointer pointer; typedef typename CORE::reference reference; typedef typename CORE::value_type value_type; IterTool (CORE const& setup) : core_(setup) { hasData(); } /* === lumiera forward iterator concept === */ reference operator*() const { _maybe_throw(); return *core_.pipe(); } pointer operator->() const { _maybe_throw(); return & *core_.pipe(); } IterTool& operator++() { _maybe_throw(); iterate(); return *this; } bool isValid () const { return hasData(); } bool empty () const { return not isValid(); } }; template inline bool operator== (IterTool const& it1, IterTool const& it2) { return (!it1 && !it2 ) || ( it1 && it2 && (*it1) == (*it2) ) ; } template inline bool operator!= (IterTool const& ito1, IterTool const& ito2) { return not (ito1 == ito2); } /** * Implementation of the filter logic. * This core stores a function object instance, * passing each pulled source element to this * predicate function for evaluation. * @note predicate is evaluated at most once * for each value yielded by the source */ template struct FilterCore : IdentityCore { typedef IdentityCore Raw; typedef typename IT::reference Val; function predicate_; bool evaluate () const { return Raw::pipe() && currVal_isOK(); } mutable bool cached_; mutable bool isOK_; bool currVal_isOK () const ///< @return (maybe cached) result of filter predicate { return (cached_ && isOK_) || (cached_ = true &&(isOK_ = predicate_(*Raw::pipe()))); } void advance () { cached_ = false; Raw::advance(); } template FilterCore (IT const& source, PRED prediDef) : Raw(source) , predicate_(prediDef) // induces a signature check , cached_(false) // not yet cached , isOK_() // some value { } }; /** * Iterator tool filtering pulled data according to a predicate */ template class FilterIter : public IterTool > { typedef FilterCore _Filter; typedef IterTool<_Filter> _Impl; public: static bool acceptAll(typename _Filter::Val) { return true; } FilterIter () : _Impl(FilterCore(IT(), acceptAll)) { } template FilterIter (IT const& src, PRED filterPredicate) : _Impl(_Filter(src,filterPredicate)) { } ENABLE_USE_IN_STD_RANGE_FOR_LOOPS (FilterIter) }; /** Build a FilterIter: convenience free function shortcut, * picking up the involved types automatically. * @param filterPredicate to be invoked for each source element * @return Iterator filtering contents of the source */ template inline FilterIter filterIterator (IT const& src, PRED filterPredicate) { return FilterIter(src,filterPredicate); } /** * 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. * @note changing the condition modifies a given iterator in place. * Superficially this might look as if the storage remains * the same, but in fact we're adding a lambda closure, * which the runtime usually allocates on the heap, * holding the previous functor and a second functor * for the added clause. * @warning the addition of disjunctive and negated clauses might * actually weaken the filter condition. Yet still there is * \em no reset of the source iterator, i.e. we don't * re-evaluate from start, but just from current head. * Which means we might miss elements in the already consumed * part of the source sequence, which theoretically would * pass the now altered filter condition. * @see IterTools_test::verify_filterExtension */ template class ExtensibleFilterIter : public FilterIter { typedef FilterCore _Filter; typedef typename _Filter::Val Val; void reEvaluate() { this->core_.cached_ = false; this->hasData(); // re-evaluate head element } public: ExtensibleFilterIter() { } template ExtensibleFilterIter (IT const& src, PRED initialFilterPredicate) : FilterIter(src, initialFilterPredicate) { } ExtensibleFilterIter (IT const& src) : ExtensibleFilterIter(src, FilterIter::acceptAll) { } // standard copy operations acceptable /** access the unfiltered source iterator * in current state */ IT& underlying() { return this->core_.source_; } template ExtensibleFilterIter& andFilter (COND conjunctiveClause) { function& filter = this->core_.predicate_; filter = [=](Val val) { return filter(val) and conjunctiveClause(val); }; reEvaluate(); return *this; } template ExtensibleFilterIter& andNotFilter (COND conjunctiveClause) { function& filter = this->core_.predicate_; filter = [=](Val val) { return filter(val) and not conjunctiveClause(val); }; reEvaluate(); return *this; } template ExtensibleFilterIter& orFilter (COND disjunctiveClause) { function& filter = this->core_.predicate_; filter = [=](Val val) { return filter(val) or disjunctiveClause(val); }; reEvaluate(); return *this; } template ExtensibleFilterIter& orNotFilter (COND disjunctiveClause) { function& filter = this->core_.predicate_; filter = [=](Val val) { return filter(val) or not disjunctiveClause(val); }; reEvaluate(); return *this; } template ExtensibleFilterIter& setNewFilter (COND entirelyDifferentPredicate) { this->core_.predicate_ = entirelyDifferentPredicate; reEvaluate(); return *this; } ExtensibleFilterIter& flipFilter () { function& filter = this->core_.predicate_; filter = [=](Val val) { return not filter(val); }; reEvaluate(); return *this; } }; /** * Helper: predicate returning `true` * whenever the argument value changes * during a sequence of invocations. */ template class SkipRepetition { typedef wrapper::ItemWrapper Item; Item prev_; public: bool operator() (VAL const& elm) { if (prev_ && (*prev_ == elm)) return false; // element differs from predecessor prev_ = elm; return true; } typedef bool result_type; }; /** * Implementation of custom processing logic. * This core stores a function object instance * to treat each source element pulled. */ template class TransformingCore { typedef typename IT::reference InType; typedef wrapper::ItemWrapper Item; function trafo_; IT source_; Item treated_; void processItem () { if (source_) treated_ = trafo_(*source_); else treated_.reset(); } public: TransformingCore () ///< deactivated core : trafo_() , source_() , treated_() { } template TransformingCore (IT const& orig, FUN processor) : trafo_(processor) // induces a signature check , source_(orig) { processItem(); } Item const& pipe () const { return treated_; } void advance () { ++source_; processItem(); } bool evaluate () const { return bool(source_); } typedef typename RefTraits::pointer pointer; typedef typename RefTraits::reference reference; typedef typename RefTraits::value_type value_type; }; /** * Iterator tool treating pulled data by a custom transformation (function) * @param IT source iterator * @param VAL result (output) type */ template class TransformIter : public IterTool > { typedef TransformingCore _Trafo; typedef IterTool<_Trafo> _IteratorImpl; public: TransformIter () : _IteratorImpl(_Trafo()) { } template TransformIter (IT const& src, FUN trafoFunc) : _IteratorImpl(_Trafo(src,trafoFunc)) { } ENABLE_USE_IN_STD_RANGE_FOR_LOOPS (TransformIter) }; namespace { // Helper to pick up the produced value type automatically using lib::meta::FunctionSignature; template struct _ProducedOutput { typedef typename FunctionSignature >::Ret Type; }; template struct _ProducedOutput > { typedef typename FunctionSignature >::Ret Type; }; template struct _ProducedOutput { typedef typename FunctionSignature >::Ret Type; }; } /** Build a TransformIter: convenience free function shortcut, * picking up the involved types automatically. * @param processingFunc to be invoked for each source element * @return Iterator processing the source feed */ template inline TransformIter::Type> transformIterator (IT const& src, FUN processingFunc) { typedef typename _ProducedOutput::Type OutVal; return TransformIter(src,processingFunc); } /* === utility functions === */ template inline void append_all (IT iter, CON& container) { for ( ; iter; ++iter ) container.push_back (*iter); } template inline typename IT::value_type pull_last (IT iter) { typedef typename IT::value_type Val; typedef wrapper::ItemWrapper Item; Item lastElm; while (iter) { lastElm = *iter; ++iter; } if (lastElm) return *lastElm; else throw lumiera::error::State ("attempt to retrieve the last element " "of an exhausted or empty iterator" ,lumiera::error::LUMIERA_ERROR_ITER_EXHAUST); } /** filters away repeated values * emitted by source iterator */ template inline FilterIter filterRepetitions (IT const& source) { typedef typename IT::value_type Val; return filterIterator(source, SkipRepetition() ); } } // namespace lib #endif