From e3ca8548a41e96cc49b40e6700f6e114f77758e9 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 8 Sep 2018 14:41:51 +0200 Subject: [PATCH] TreeExplorer: allow for a disabled filter ...considered as one of the building blocks to resolve Problems in the Design of ChainSearch --- src/lib/iter-tree-explorer.hpp | 21 +- tests/library/iter-tree-explorer-test.cpp | 25 +- wiki/thinkPad.ichthyo.mm | 798 ++++++++++++++-------- 3 files changed, 556 insertions(+), 288 deletions(-) diff --git a/src/lib/iter-tree-explorer.hpp b/src/lib/iter-tree-explorer.hpp index a7f5c5686..def63b5a2 100644 --- a/src/lib/iter-tree-explorer.hpp +++ b/src/lib/iter-tree-explorer.hpp @@ -445,6 +445,8 @@ namespace lib { namespace iter_explorer { // Implementation of Iterator decorating layers... + constexpr auto ACCEPT_ALL = [](auto){return true;}; + /** * @internal technical details of binding a functor into the TreeExplorer. * Notably, this happens when adapting an _"expansion functor"_ to allow expanding a given element @@ -927,12 +929,19 @@ namespace lib { return unConst(*this); } + bool + isDisabled() const + { + return not predicate_.boundFunction; + } + /** @note establishes the invariant: * whatever the source yields as current element, * has already been approved by our predicate */ void pullFilter () { + if (isDisabled()) return; while (srcIter() and not predicate_(srcIter())) ++srcIter(); } @@ -1082,6 +1091,13 @@ namespace lib { }); } + /** discard filter predicates and disable any filtering */ + void + disableFilter() + { + _Base::predicate_.boundFunction = nullptr; + } + private: /** @internal boilerplate to remould the filter predicate in-place @@ -1113,6 +1129,9 @@ namespace lib { WrappedPredicate& firstClause = _Base::predicate_; // pick up the existing filter predicate ChainPredicate chainClause{forward (additionalClause)}; // wrap the extension predicate in a similar way + if (_Base::isDisabled()) + firstClause.boundFunction = ACCEPT_ALL; + _Base::predicate_ = WrappedPredicate{buildCombinedClause (firstClause, chainClause)}; _Base::pullFilter(); // pull to re-establish the Invariant } @@ -1457,7 +1476,7 @@ namespace lib { auto mutableFilter() { - return mutableFilter ([](auto){ return true; }); + return mutableFilter (iter_explorer::ACCEPT_ALL); } diff --git a/tests/library/iter-tree-explorer-test.cpp b/tests/library/iter-tree-explorer-test.cpp index 8dc2cdc72..588be76c4 100644 --- a/tests/library/iter-tree-explorer-test.cpp +++ b/tests/library/iter-tree-explorer-test.cpp @@ -853,7 +853,7 @@ namespace test{ verify_FilterChanges() { auto seq = treeExplore(CountDown{20}) - .mutableFilter([](uint){ return true; }); + .mutableFilter(); auto takeEve = [](uint i){ return i%2 == 0; }; auto takeTrd = [](uint i){ return i%3 == 0; }; @@ -908,6 +908,29 @@ namespace test{ CHECK (0 == seq.p); // ...which he manipulated, so that core == 0 CHECK (isnil (seq)); // .....and thus iteration end is detected VERIFY_ERROR (ITER_EXHAUST, *seq ); + + + // verify enabling and disabling... + auto seq2 = treeExplore(CountDown{10}) + .mutableFilter(takeTrd); + + CHECK (9 == *seq2); + seq2.disableFilter(); + CHECK (9 == *seq2); + ++seq2; + CHECK (8 == *seq2); + seq2.andNotFilter (takeEve); + CHECK (7 == *seq2); + ++seq2; + CHECK (5 == *seq2); + seq2.disableFilter(); + CHECK (5 == *seq2); + ++seq2; + CHECK (4 == *seq2); + ++seq2; + CHECK (3 == *seq2); + seq2.flipFilter(); // everything rejected + CHECK (isnil (seq2)); } diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 5876d4158..fc46297ea 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -1,6 +1,6 @@ - + @@ -30746,6 +30746,8 @@ + + @@ -30783,6 +30785,400 @@ + + + + + + + + + + +

+ sofern längerfristig Itertools durch TreeExplorer abgelöst werden könnte +

+ +
+
+ + + + + + + + + + + + + + +

+ ...und das hängt an einem hauchdünnen Faden, +

+

+ und ist subtil bis zum geht nicht mehr.... +

+

+ +

+

+ Wenn man das Lambda einfach per [=] schreibt, und das Feld this->predicate_ verwendet, +

+

+ dann wird this gecaptured (und das ist effektiv per Referenz). Wenn ich dann den +

+

+ konstruierten Funktor an this->predicate_ zuweise, haben wir eine Endlos-Rekursion. +

+

+ +

+

+ Lösung: man muß im lokalen Frame eine Referenz auf this->predicate definieren und binden. +

+

+ Diese wird dann per Value gecaptured, was die gewünschte Kopie bewirkt. +

+ +
+
+ + + + + + +

+ ...muß den Chain-Funktor aus dem Template-Argument erzeugen, +

+

+ und ihn dann in die per-Value-Closure des erzeugten neuen Lambda binden (=Kopie). +

+

+ +

+

+ Erst ab C++17 kann man Lambda-Captures pre move machen +

+

+ (und auch dafür ist die Syntax grausam) +

+ +
+
+
+ + + + + + + +

+ ...es soll bloß einfach funktionieren!!!!!!!!!!! +

+ +
+ +
+ + + + + + + + + +

+ keine gute Idee. +

+

+ Dann verwenden wir im einen Funktor-Framework eine Filter-Komponente +

+

+ aus dem anderen Framework, obwohl es direkt hier auch eine Filter-Komponente gäbe. +

+

+ +

+

+ Außerdem habe ich immer noch die Hoffnung, irgendwann mal +

+

+ die Itertools komplett durch den TreeExplorer ablösen zu können +

+ +
+ +
+
+ + + + + + + + + + + + +

+ Name: mutableFilter() +

+ +
+ +
+
+ + + + + + + + + + + + + + + + +

+ ...sieht gut aus. +

+

+ +

+

+ Folgendes habe ich gesehen +

+
    +
  • + zu Beginn zeigt das eingebettete Funktor-Objekt auf eine Position auf dem Stack +
  • +
  • + beim Aufrufen der andFilter()-Funktion werden diverse Funktoren kopiert,
    wobei nacheinander die (zu erwartenden) Argumente als Quelle auftauchen
    +
  • +
  • + danach hat sich der Funktor geändert: er zeigt nun auf eine Position auf dem Heap
    +
  • +
  • + der bisherige Funktor wurde mit der Closure des zusammengesetzten Funktors kollabiert (hat gleiche Addresse)
    +
  • +
  • + der Chain-Funktor hat eine Closure bekommen, die ebenfalls Heap-alloziert ist.
    das deutet darauf hin, daß das capturen per copy funktioniert hat
    +
  • +
  • + Beim Aufruf steppen wir nacheinander erst in den kombinierten Funktor,
    und von dort wie erwartet in die beiden Lambdas. +
  • +
  • +
    +
  • +
+ +
+ +
+
+
+
+ + + + + + + + + + + + + +

+ ...für das dort hineingereichte Funktor-Objekt wird der Argument-Accessor ausgewählt (Metaprogrammierung). +

+

+ Er ist dann im Typ des Wrappers == _Traits::Functor codiert. +

+

+ +

+

+ Wir können zwar den im Wrapper enthaltenen Funktor neu zuweisen (in gewissen Grenzen), +

+

+ aber er wird stets den zu Beginn gewählten Argument-Accessor nehmen. +

+

+ Typischerweise wird dieser ja sogar beim Aufruf des getemplateteten Funtions-Operators geInlined +

+ +
+
+ + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ Problem: std::function in FilterPredicate +

+ +
+ + + + + + +

+ entweder Val -> bool oder Iter -> bool +

+ +
+ +
+
+
+
+
+
+ + + + + + + + + + + +

+ ...für das dort hineingereichte Funktor-Objekt wird der Argument-Accessor ausgewählt (Metaprogrammierung). +

+

+ Er ist dann im Typ des Wrappers == _Traits::Functor codiert. +

+

+ +

+

+ Wir können zwar den im Wrapper enthaltenen Funktor neu zuweisen (in gewissen Grenzen), +

+

+ aber er wird stets den zu Beginn gewählten Argument-Accessor nehmen. +

+

+ Typischerweise wird dieser ja sogar beim Aufruf des getemplateteten Funtions-Operators geInlined +

+ + +
+ +
+ + + + + + + + + + +

+ der Aufruf vom äußeren in den inneren Wrapper ist 1:1 und sollte vom Compiler wegoptimiert werden +

+ +
+
+ + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + +

+ ...ich verbrenne Stunden beim Debuggen von neuem Code +

+ + +
+ +
+ + + + + + + + + + + + + @@ -30837,290 +31233,9 @@ - + + - - - - - - - -

- sofern längerfristig Itertools durch TreeExplorer abgelöst werden könnte -

- -
-
- - - - - - - - - - - - - - -

- ...und das hängt an einem hauchdünnen Faden, -

-

- und ist subtil bis zum geht nicht mehr.... -

-

- -

-

- Wenn man das Lambda einfach per [=] schreibt, und das Feld this->predicate_ verwendet, -

-

- dann wird this gecaptured (und das ist effektiv per Referenz). Wenn ich dann den -

-

- konstruierten Funktor an this->predicate_ zuweise, haben wir eine Endlos-Rekursion. -

-

- -

-

- Lösung: man muß im lokalen Frame eine Referenz auf this->predicate definieren und binden. -

-

- Diese wird dann per Value gecaptured, was die gewünschte Kopie bewirkt. -

- -
-
- - - - - - -

- ...muß den Chain-Funktor aus dem Template-Argument erzeugen, -

-

- und ihn dann in die per-Value-Closure des erzeugten neuen Lambda binden (=Kopie). -

-

- -

-

- Erst ab C++17 kann man Lambda-Captures pre move machen -

-

- (und auch dafür ist die Syntax grausam) -

- -
-
-
- - - - - - - -

- ...es soll bloß einfach funktionieren!!!!!!!!!!! -

- -
- -
- - - - - - - - - -

- keine gute Idee. -

-

- Dann verwenden wir im einen Funktor-Framework eine Filter-Komponente -

-

- aus dem anderen Framework, obwohl es direkt hier auch eine Filter-Komponente gäbe. -

-

- -

-

- Außerdem habe ich immer noch die Hoffnung, irgendwann mal -

-

- die Itertools komplett durch den TreeExplorer ablösen zu können -

- -
- -
-
- - - - - - - - - - - - -

- Name: mutableFilter() -

- -
- -
-
- - - - - - - - - - - - - - - - -

- ...sieht gut aus. -

-

- -

-

- Folgendes habe ich gesehen -

-
    -
  • - zu Beginn zeigt das eingebettete Funktor-Objekt auf eine Position auf dem Stack -
  • -
  • - beim Aufrufen der andFilter()-Funktion werden diverse Funktoren kopiert,
    wobei nacheinander die (zu erwartenden) Argumente als Quelle auftauchen
    -
  • -
  • - danach hat sich der Funktor geändert: er zeigt nun auf eine Position auf dem Heap
    -
  • -
  • - der bisherige Funktor wurde mit der Closure des zusammengesetzten Funktors kollabiert (hat gleiche Addresse)
    -
  • -
  • - der Chain-Funktor hat eine Closure bekommen, die ebenfalls Heap-alloziert ist.
    das deutet darauf hin, daß das capturen per copy funktioniert hat
    -
  • -
  • - Beim Aufruf steppen wir nacheinander erst in den kombinierten Funktor,
    und von dort wie erwartet in die beiden Lambdas. -
  • -
  • -
    -
  • -
- -
- -
-
-
-
- - - - - - - - - - - - -

- ...für das dort hineingereichte Funktor-Objekt wird der Argument-Accessor ausgewählt (Metaprogrammierung). -

-

- Er ist dann im Typ des Wrappers == _Traits::Functor codiert. -

-

- -

-

- Wir können zwar den im Wrapper enthaltenen Funktor neu zuweisen (in gewissen Grenzen), -

-

- aber er wird stets den zu Beginn gewählten Argument-Accessor nehmen. -

-

- Typischerweise wird dieser ja sogar beim Aufruf des getemplateteten Funtions-Operators geInlined -

- -
-
- - - - - - - - - - -

- der Aufruf vom äußeren in den inneren Wrapper ist 1:1 und sollte vom Compiler wegoptimiert werden -

- -
-
- - - - - - - - - - - - - - - - - - -
-
-
- - - - - - - - -
@@ -31162,6 +31277,31 @@ + + + + + + + + +

+ die Search-Engine bläht die Debug-Infos gewaltig auf +

+ + +
+ + + + + + +
+ + + +
@@ -31525,7 +31665,8 @@ - + + @@ -31533,6 +31674,9 @@ + + + @@ -31545,7 +31689,7 @@ - + @@ -31560,6 +31704,7 @@ + @@ -31577,6 +31722,87 @@ + + + + + + + + + + + +

+ ...denn +

+
    +
  • + es ist praktisch, zunächst "leer" zu konstruieren +
  • +
  • + es ist verständlicher, wenn alle Bedingungen symmetrisch angegeben werden +
  • +
  • + es ist natürlich, daß ein "leerer" Filter alles durchläßt +
  • +
+ +
+
+ + + + + + +

+ Filter leer == alles durchlassen +

+ +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +