From d58f8c853ab9f62b477245c5f41bd37015a1a16c Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 3 Oct 2016 19:31:59 +0200 Subject: [PATCH] TreeMutator binding: extend collection binding to support std::map actually this is a pragmatic extension for some special use cases, and in general rather discurraged, since it contradicts the established diff semantics. Yet with some precaution, it should be possible to transport information via an intermediary ETD Map -> ETD -> Map --- .../diff/tree-mutator-collection-binding.hpp | 80 +++++++++++-- wiki/renderengine.html | 12 +- wiki/thinkPad.ichthyo.mm | 106 +++++++++++++----- 3 files changed, 157 insertions(+), 41 deletions(-) diff --git a/src/lib/diff/tree-mutator-collection-binding.hpp b/src/lib/diff/tree-mutator-collection-binding.hpp index f4dc84156..f778ae48c 100644 --- a/src/lib/diff/tree-mutator-collection-binding.hpp +++ b/src/lib/diff/tree-mutator-collection-binding.hpp @@ -35,6 +35,9 @@ ** a building block for one such layer, especially for binding to a representation ** of "child objects" managed within a typical STL container. ** + ** As a _special case_, binding to a STL map is supported, while this usage is rather + ** discurraged, since it contradicts the diff semantics due to intrinsic ordering. + ** ** @note the header tree-mutator-collection-binding.hpp was split off for sake of readability ** and is included automatically from bottom of tree-mutator.hpp ** @@ -55,6 +58,8 @@ #include "lib/iter-adapter-stl.hpp" #include +#include +#include namespace lib { @@ -63,7 +68,7 @@ namespace diff{ namespace { // Mutator-Builder decorator components... - + using std::forward; using lib::meta::Strip; using lib::diff::GenNode; using lib::iter_stl::eachElm; @@ -139,7 +144,7 @@ namespace diff{ void inject (Elm&& elm) { - collection.emplace_back (forward(elm)); + emplace (collection, forward(elm)); } iterator @@ -154,23 +159,82 @@ namespace diff{ locate (GenNode const& targetSpec) { if (not collection.empty() - and matches (targetSpec, collection.back())) - return lastElm(); + and matches (targetSpec, recentElm(collection))) + return recentElmIter(); else return search (targetSpec, eachElm(collection)); } - private: + + private: /* === technicallities of container access === */ + /** @internal technicality * Our iterator is actually a Lumiera RangeIter, and thus we need * to construct a raw collection iterator pointing to the aftmost element * and then create a range from this iterator and the `end()` iterator. */ iterator - lastElm() + recentElmIter() { - using raw_iter = typename CollectionBinding::Coll::iterator; - return iterator (raw_iter(&collection.back()), collection.end()); + return iterator (recentElmRawIter(collection), collection.end()); + } + + template + static auto + recentElmRawIter (C& coll) ///< fallback: use first element of container + { + return coll.begin(); + } + + template + using Map = std::map; + + template + static auto + recentElmRawIter (Map& map) ///< workaround for `std::Map`: lookup via reverse iterator + { + return map.find (recentElm(map).first); + } + + static auto + recentElmRawIter (std::vector& vec) + { + return typename std::vector::iterator (&vec.back()); + } + + + template + static Elm& + recentElm (C& coll) + { + return *coll.begin(); + } + + template + static E& + recentElm (Map& map) + { + return *++map.rend(); + } + + static Elm& + recentElm (std::vector& vec) + { + return vec.back(); + } + + + template + static void + emplace (C& coll, Elm&& elm) + { + coll.emplace (forward (elm)); + } + + static void + emplace (std::vector& coll, Elm&& elm) + { + coll.emplace_back (forward (elm)); } }; diff --git a/wiki/renderengine.html b/wiki/renderengine.html index 838930432..d14463dbe 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -8231,7 +8231,7 @@ On receiving a stream of tokens of this "diff language", it is possibl i.e. a ''unified diff'' or the ''predicate notation'' used above to describe the list diffing algorithm, just by accumulating changes. -
+
The TreeMutator is an intermediary to translate a generic structure pattern into heterogeneous local invocation sequences.
 within the [[diff framework|TreeDiffModel]], this is a crucial joint, since here the abstract, generic, ~DOM-like ExternalTreeDescription meeds opaque, local and undisclosed data structures.
 
@@ -8354,6 +8354,10 @@ attached to a clip, or the mixture of clips, effects and labels found within a [
 :** fabricate a recursive sub-TreeMutator to deal with the (implementation data) element passed as reference
 :** the ID, as taken from the (diff) spec, is also passed, for orientation
 :** the lambda is expected to //place// the generated mutator into the given buffer smart handle, which takes ownership of this object
+;STL map binding
+:...can be supported, as an extension to the ''collection binding'' --
+:* elements are (key, value) pairs
+:* //use is discurraged,// since it contradicts and breaks diff semantics (due to the inherent ordering of maps)
 ;object field binding
 :this maps the concept of "Attribute" onto concrete, mutable data fields
 :any re-ordering  and deletion of fields is ''prohibited'', while defaultable optional fields may be tolerated
@@ -8377,7 +8381,7 @@ attached to a clip, or the mixture of clips, effects and labels found within a [
 You should note that this entire design is recursive: only after understanding the part, where, for handling a sub-structure, a nested mutator is fabricated and placed into a given buffer, it becomes clear to what effect we're creating a customised mutator: we always need some (relative) parent scope, which happens to know more about the actual data to be treated with a TreeMutator. This scope assists with creating a suitable binding. Obviously, from within that binding, it is known what the sub-structures and children of that local data are all about and what semantics to give to the fundamental operations.
 
-
+
The TreeMutator is an intermediary to translate a generic structure pattern into heterogeneous local invocation sequences.
 
 !Motivation
@@ -8492,7 +8496,7 @@ So we get to choose between
 Since our goal is to represent changes to structured objects in the form of a diff message sequence, the ability to capture the essence of objects is crucial. We are not concerned with behaviour and state, yet we have to deal with the //object's visible properties.// This gets us into a design dilemma; the notion underlying our diff language is that of //structured data,// which indeed offers the concept of an //attribute// -- but visible properties are based on //object fields,// and thus have more limited capabilities, as being rooted in the class definition underlying each object. Consequently our diff language may express changes beyond the abilities of any ordinary object.
 * attributes may be reordered
 * attributes may be added and deleted
--- none of which has any tangible meaning for a regular (language) object. Contrast this with ExternalTreeDescription, which we crafted specifically to support diff language and diff messages, resulting in the ability to represent those changes explicitly. So, in theory, a given diff, when applied both to a GenNode and via TreeMutator to a native data structure, might lead to states not semantically equivalent. We can not reliably protect ourselves against that possibility, but, on the other hand, it is not clear if such poses an actual threat.
+-- none of which has any tangible meaning for a regular (language) object. Contrast this with ExternalTreeDescription, which we crafted specifically to support diff language and diff messages, resulting in the ability to represent those changes explicitly. So, in theory, a given diff, when applied both to a GenNode and via TreeMutator to a native data structure, might lead to states not semantically equivalent. We can not reliably protect ourselves against that possibility, but, on the other hand, it is not clear if such poses an actual threat. Thus we'll tolerate extensions to the diff binding, to acknowledge usefulness in specific situations
 
 ''A possible path to reconcile'' these inner contradictions is to support the mutation primitives ''as far as is sensible''.
 Through analysis of the semantics, we could distinguish several flavours of "attributes", especially...
@@ -8505,7 +8509,7 @@ Through analysis of the semantics, we could distinguish several flavours of &quo
 ;attribute map
 :an ordered collection of key-value associations, which can be enumerated, searched and extended
 Thus we //could define a sensible handling for each of those cases, and deal with combinations// -- but --
-it seems more adequate to limit ourselves just to object fields and to include the defaultable object fields through implementation leeway. Because, in the end we really do need object fields, and anything beyond is just a materialisation of special behaviour and can be kept out of the diff system altogether. Through diff messages, we want to express structured changes, not metadata, nor closures, nor strategies, nor prototypes. And the attribute map is really something different than an object, and should be implemented separately, when it comes to applying diff messages to GenNode elements; the latter are able to //emulate// or //represent// objects, but by their nature are rather key-value associations arranged in nested scopes.
+it seems more adequate to limit ourselves just to object fields and to include the defaultable object fields through implementation leeway. Because, in the end we really do need object fields, and anything beyond is just a materialisation of special behaviour and can be kept out of the diff system altogether. Through diff messages, we want to express structured changes, not metadata, nor closures, nor strategies, nor prototypes. And the attribute map is really something different than an object, and should be implemented separately, based on binding ot a //attribute collection,// when it comes to applying diff messages to GenNode elements; the latter are able to //emulate// or //represent// objects, but by their nature are rather key-value associations arranged in nested scopes.
 
 !!!mapping rules to handle object fields
 So we basically ''disallow'' anything related to ''order'', ''re-ordering'' and ''deletion'' of object fields.
diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm
index 434579397..59d5909ab 100644
--- a/wiki/thinkPad.ichthyo.mm
+++ b/wiki/thinkPad.ichthyo.mm
@@ -1883,7 +1883,7 @@
 
 
 
-
+
 
 
 
@@ -2833,7 +2833,7 @@
 
 
 
-
+
 
 
 
@@ -2892,7 +2892,7 @@
 
 
 
-
+
 
 
 
@@ -3007,7 +3007,7 @@
   
 
 
-
+
 
   
     
@@ -3030,6 +3030,7 @@
     

+
@@ -3129,7 +3130,7 @@ - + @@ -3205,6 +3206,17 @@ + + + + + + + + + + + @@ -3516,8 +3528,8 @@ - - + + @@ -3591,7 +3603,9 @@ - + + + @@ -5525,8 +5539,7 @@ - - + @@ -5547,8 +5560,7 @@ Marker-Typ MutationMessage

- - +
@@ -5618,8 +5630,7 @@ Und für eine reine Ref erzeugt C++ niemals eine anonyme Instanz!

- - +
@@ -5643,8 +5654,7 @@ MutationMessage(blaBlubb())

- -
+
@@ -5676,8 +5686,7 @@ Der Nexus speichert nämlich eine direkte Referenz in der Routingtabelle

- - +
@@ -5715,10 +5724,13 @@ Und es gibt nicht sowas wie das "zuletzt behandelte" Element

- - +
+ + + + @@ -5741,8 +5753,7 @@ erhalten!

- - + @@ -5764,8 +5775,7 @@ Das bricht mit unserem grundsätzlichen Konzept der kongruenten  Daten-Strukturen

- - +
@@ -5781,25 +5791,63 @@ läßt sich nicht auf eine Map-Implementierung aufspielen

- - +
- +

- Entscheidung: abgelehnt + Entscheidung +

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

+ ...zum Beispiel wie grade hier, beim MockElm +

+

+ das wird vermutlich niemals wirklich in einem vollen Diff-Zusammenhang gebraucht. +

+

+ +

+

+ Und dann ist unbestreitbar eine Map eine sehr einfache Implementierung +

+

+ und auch im Diff-Applikator nicht wirklich schwierig zu unterstützen

- + +
+ + + + + + + +