From f10e66e4ae311166d00947a6a297acf7f84d2da8 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 20 Nov 2017 01:02:30 +0100 Subject: [PATCH] TreeExplorer: design a solution to handle expansion of children this solution makes me feel somewhat queasy.. stacking several adaptors and wrappers and traits on top of each other. Well, it type checks and passes the test, so let's trust functional programming --- src/lib/iter-tree-explorer.hpp | 187 ++++++++++++++++++++-- tests/library/iter-tree-explorer-test.cpp | 20 +-- 2 files changed, 184 insertions(+), 23 deletions(-) diff --git a/src/lib/iter-tree-explorer.hpp b/src/lib/iter-tree-explorer.hpp index d49c24b33..5a8cc558a 100644 --- a/src/lib/iter-tree-explorer.hpp +++ b/src/lib/iter-tree-explorer.hpp @@ -93,7 +93,7 @@ namespace lib { using std::forward; using std::function; - namespace iter_explorer { + namespace iter_source { template using iterator = typename meta::Strip::TypeReferred::iterator; @@ -122,6 +122,107 @@ namespace lib { // standard copy operations acceptable }; + + /** + * 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 + class IterableDecorator + : public COR + { + COR & _core() { return static_cast (*this); } + COR const& _core() const { return static_cast (*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; + + template + IterableDecorator (ARGS&& ...init) + : COR(std::forward(init)...) + { } + + IterableDecorator() =default; + IterableDecorator (IterableDecorator&&) =default; + IterableDecorator (IterableDecorator const&) =default; + IterableDecorator& operator= (IterableDecorator&&) =default; + IterableDecorator& operator= (IterableDecorator const&) =default; + + operator bool() const { return isValid(); } + + + /* === lumiera forward iterator concept === */ + + reference + operator*() const + { + __throw_if_empty(); + return yield (_core()); // extension point: yield + } + + pointer + operator->() const + { + __throw_if_empty(); + return & yield(_core()); // extension point: yield + } + + IterableDecorator& + operator++() + { + __throw_if_empty(); + iterNext (_core()); // extension point: iterNext + return *this; + } + + bool + isValid () const + { + return checkPoint(_core()); // extension point: checkPoint + } + + bool + empty () const + { + return not isValid(); + } + + + + ENABLE_USE_IN_STD_RANGE_FOR_LOOPS (IterableDecorator); + + + /// Supporting equality comparisons of equivalent iterators (same state core)... + template + friend bool + operator== (IterableDecorator const& il, IterableDecorator const& ir) + { + return (il.empty() and ir.empty()) + or (il.isValid() and ir.isValid() and il._core() == ir._core()); + } + + template + friend bool + operator!= (IterableDecorator const& il, IterableDecorator const& ir) + { + return not (il == ir); + } + }; + + }//(End) namespace iter_explorer : predefined policies and configurations @@ -130,6 +231,7 @@ namespace lib { using meta::enable_if; using meta::Yes_t; using meta::No_t; + using meta::_Fun; using std::__and_; using std::__not_; using std::is_constructible; @@ -180,7 +282,16 @@ namespace lib { { static_assert (not std::is_rvalue_reference::value, "container needs to exist elsewhere during the lifetime of the iteration"); - using SrcIter = iter_explorer::StlRange; + using SrcIter = iter_source::StlRange; + }; + + + template + struct _ExpansionTraits + { + using ExpandedChildren = typename _Fun::Ret; + + using Core = typename _TreeExplorerTraits::SrcIter; }; }//(End) TreeExplorer traits @@ -189,20 +300,29 @@ namespace lib { namespace iter_source { - template + template class Expander - : public EXP + : public SRC { + using Core = typename _ExpansionTraits::Core; + + static_assert (std::is_convertible::value, + "the iterator from the expansion must yield compatible values"); + static_assert (std::is_convertible::Args::List::Head, typename SRC::value_type>::value, + "the expansion functor must accept a parameter compatible to the source iterator value type"); + function expandChildren_; + IterStack expansions_; public: Expander() =default; // inherited default copy operations template - Expander (EXP&& parentExplorer, FUN&& expandFunctor) - : EXP{move (parentExplorer)} + Expander (SRC&& parentExplorer, FUN&& expandFunctor) + : SRC{move (parentExplorer)} , expandChildren_{forward (expandFunctor)} + , expansions_{} { } @@ -210,15 +330,52 @@ namespace lib { Expander& expand() { - UNIMPLEMENTED ("expand-children core operation"); + REQUIRE (checkPoint(*this), "attempt to expand an empty explorer"); + + Core expanded = expandChildren_ (yield(*this)); + iterNext (*this); // consume current head element + if (expanded.isValid()) + expansions_.push (move(expanded)); + + return *this; } /** diagnostics: current level of nested child expansion */ size_t depth() const { - UNIMPLEMENTED ("implementation of the expand mechanics"); + return expansions_.size(); } + + + protected: /* === Iteration control API for IterableDecorator === */ + + friend bool + checkPoint (Expander const& tx) + { + return 0 < tx.depth() + or tx.isValid(); + } + + friend typename SRC::reference + yield (Expander const& tx) + { + return 0 < tx.depth()? **tx.expansions_ + : *tx; + } + + friend void + iterNext (Expander & tx) + { + if (0 < tx.depth()) + { + ++(*tx.expansions_); + while (0 < tx.depth() and not *tx.expansions_) + ++tx.expansions_; + } + else + ++tx; + } }; } @@ -241,9 +398,9 @@ namespace lib { public: - typedef typename SRC::value_type value_type; - typedef typename SRC::reference reference; - typedef typename SRC::pointer pointer; + using value_type = typename SRC::value_type; + using reference = typename SRC::reference; + using pointer = typename SRC::pointer; /** by default create an empty iterator */ @@ -271,8 +428,12 @@ namespace lib { { using FunSig = typename meta::_Fun::Sig; using This = typename meta::Strip::TypeReferred; + using Value = typename This::value_type; + using Core = iter_source::Expander; - return iter_source::Expander {move(*this), forward(expandFunctor)}; + using ExpandableExplorer = iter_source::IterableDecorator; + + return ExpandableExplorer{move(*this), forward(expandFunctor)}; } private: }; @@ -295,7 +456,7 @@ namespace lib { // using lib::meta::disable_if; // using std::function; // using meta::_Fun; - } + } diff --git a/tests/library/iter-tree-explorer-test.cpp b/tests/library/iter-tree-explorer-test.cpp index b075b02b9..a34c0ba88 100644 --- a/tests/library/iter-tree-explorer-test.cpp +++ b/tests/library/iter-tree-explorer-test.cpp @@ -125,15 +125,15 @@ namespace test{ class NumberSequence : public IterStateWrapper { - - public: - explicit - NumberSequence(uint end = 0) - : IterStateWrapper (CountDown(0,end)) - { } - NumberSequence(uint start, uint end) - : IterStateWrapper (CountDown(start,end)) - { } + + public: + explicit + NumberSequence(uint end = 0) + : IterStateWrapper (CountDown(0,end)) + { } + NumberSequence(uint start, uint end) + : IterStateWrapper (CountDown(start,end)) + { } }; @@ -297,7 +297,7 @@ namespace test{ * and consequently this result needs to be again an iterable with compatible value type. * Conceptually, the evaluation _forks into the children of the expanded element_, before * continuing with the successor of the expansion point. Obviously, expansion can be applied - * again on the result of the expansion, possibly leading to a tree of side evaluations. + * again on the result of the expansion, possibly leading to a tree of side evaluations. */ void verify_expandOperation()