diff --git a/src/lib/diff/tree-diff-mutator-binding.hpp b/src/lib/diff/tree-diff-mutator-binding.hpp index 652e24389..87512dacb 100644 --- a/src/lib/diff/tree-diff-mutator-binding.hpp +++ b/src/lib/diff/tree-diff-mutator-binding.hpp @@ -23,7 +23,7 @@ /** @file tree-diff-mutator-binding.hpp ** Concrete implementation to apply structural changes to unspecific - ** private datea structures with hierarchical nature. This is a variation + ** private data structures with hierarchical nature. This is a variation ** of the generic [tree diff applicator](\ref tree-diff-application.hpp), ** using the same implementation concept, while relying on an abstract ** adapter type, the \ref TreeMutator. Similar to the generic case, when @@ -47,9 +47,9 @@ ** language in itself is necessarily a double dispatch (we have to abstract the ** verbs and we have to abstract the implementation side). And now we're decoupling ** the implementation side from a concrete data structure. Which means, that the - ** use will have to provide a set of closures (which might even partially generated + ** use will have to provide a set of closures (which might even partially be generated ** functors) to translate the _implementation actions_ underlying the language into - ** _concrete actions_ on local data. + ** _concrete actions_ working on local data. ** ** @todo this is WIP as of 2/2016 -- in the end it might be merged back or even ** replace the tree-diff-application.hpp @@ -87,7 +87,7 @@ namespace diff{ /** - * Interpreter for the tree-diff-language to work on arbitrary, undiclosed + * Interpreter for the tree-diff-language to work on arbitrary, undisclosed * local data structures. The key point to note is that this local data is * not required to implement any specific interface. The only requirement is * the ability somehow to support the basic operations of applying a structural @@ -329,13 +329,13 @@ namespace diff{ void assignElm (GenNode const& n) { - UNIMPLEMENTED("locate allready accepted element and assign given new payload"); + UNIMPLEMENTED("locate already accepted element and assign given new payload"); } void open_subScope (GenNode const& n) { - UNIMPLEMENTED("locate allready accepted element and open recursive sub-scope for mutation"); + UNIMPLEMENTED("locate already accepted element and open recursive sub-scope for mutation"); } void @@ -397,7 +397,7 @@ namespace diff{ __fail_not_found (n); } - /** assignement of changed value in one step */ + /** assignment of changed value in one step */ virtual void set (GenNode const& n) override { diff --git a/src/lib/diff/tree-mutator-attribute-binding.hpp b/src/lib/diff/tree-mutator-attribute-binding.hpp index f002fa2ef..5314b13d1 100644 --- a/src/lib/diff/tree-mutator-attribute-binding.hpp +++ b/src/lib/diff/tree-mutator-attribute-binding.hpp @@ -32,9 +32,58 @@ ** adaptation is done by implementing binding templates, in the way of building ** blocks, attached and customised through lambdas. It is possible to layer ** several bindings on top of a single TreeMutator -- and this header defines - ** a building block for one such layer, especially for binding to object fields - ** through getter / setter lambdas. - ** + ** a building block for one specific kind of layer, used to bind object fields + ** through "setter" lambdas. + ** + ** ## architecture considerations + ** Together with the (\ref tree-mutator-collection-binding.hpp), the attribute binding + ** is the most relevant building block -- yet it is special in several respects. There + ** is kind of a "impedance mismatch" between the concept of an "attribute", as used in + ** the context of diff messages and »External Tree Description«, and the nature of + ** data fields as used within the imperative or object oriented implementation: the + ** latter is rooted within a _class definition_ -- which is conceived as a _type_, + ** a conceptual entity used for construction of code, yet not really embodied into + ** the actual code at execution time. Thus, with respect to the _behaviour_ at execution, + ** the structure defined through typing and classes appears as static backdrop. This leads + ** to the consequence, that, on a generic (unspecific) level, we don't have any correlate + ** to the notion of _ordering_ and _sequence_, as found within the diff language. + ** + ** On the other hand, this very notion of _ordering_ and _sequence_ is essential to the + ** meaning of "diff", as far as collections of "children" are involved. This leaves us + ** with the decision, either to increase complexity of the diff language's definition + ** and concept, absorb this discrepancy within the complexity of implementation. + ** Deliberately, the whole concept of a "diff language" builds onto the notion of + ** _layered semantics,_ where the precise meaning of some terms remains a private + ** extension within specific usage context. There is a lot of leeway within the + ** language, and the _correct usage protocol_ is linked to the actual scope of + ** usage. We need the diff language to be a connecting medium, to link some + ** remote partners based on a locally shared common understanding of structure. + ** + ** And so we use the same approach when it comes to "attributes": We'll assume that + ** the partners connected through diff messages are _structurally compatible_ -- thus + ** any "change" message emitted at one side is assumed to basically make sense on the + ** receiving side. Consequently, the binding of an "attribute" to an object or data field + ** will either ignore or reject any specifics about field order. It will _reject_ an + ** explicit demand to re-order a field, and it will silently pass down other notions + ** related to ordering -- down to lower "onion layers" of the concrete binding. So + ** it depends on the concrete setup of the data binding (TreeMutator), if some + ** expression in diff language will be deemed incompatible -- which happens when + ** in the end no "onion layer" of the concrete binding was able to absorb and + ** comply to the diff message. + ** + ** Another architectural consideration is relevant to the way attribute bindings are + ** constructed: we rather construct a separate binding for each individual attribute, + ** instead of building a collective binding for all attributes of a given object. + ** This gives us the benefit of a simple and flexible solution plus it avoids the + ** overhead of managing a _collection of attribute definitions_ (which would likely + ** cause a heap allocation for storage). The downside is that we loose any coherence + ** between attributes of "the same object", we loose possible consistency checks and + ** we get a linear search for access to any attribute binding. Moreover, we cannot + ** protect against creation of a nonsensical binding, e.g. a binding which ties + ** the same attribute several times in contradictory fashion. The client code + ** constructing the concrete TreeMutator needs to have adequate understanding + ** regarding mode of operation and "mechanics" of such a binding. + ** ** @note the header tree-mutator-attribute-binding.hpp with specific builder templates ** is included way down, after the class definitions. This is done so for sake ** of readability. @@ -96,7 +145,7 @@ virtual bool hasSrc() override { - return true; + return true; // x or true == true } /** ensure the given spec is deemed appropriate at that point. @@ -203,7 +252,7 @@ return true; } - /** invoke the setter lambda, in case this binding layer is in charge */ + /** invoke the setter lambda, when this binding layer is in charge */ virtual bool assignElm (GenNode const& spec) override { diff --git a/src/lib/diff/tree-mutator.hpp b/src/lib/diff/tree-mutator.hpp index 0fd5ab3f0..242b96b5a 100644 --- a/src/lib/diff/tree-mutator.hpp +++ b/src/lib/diff/tree-mutator.hpp @@ -70,7 +70,7 @@ ** or reshape _all of the target's contents_. After that, you must not refer to the ** exhausted TreeMutator anymore, just let it fall out of scope. Incidentally, this ** also means that _any failure or exception encountered_ while applying a diff will - ** **corrupt the target data structure**. The basic assumption is that + ** leave a **corrupted target data structure**. The basic assumption is that ** - the target data structure will actually be built through diff messages solely ** - and that all received diff messages are sane, as being drawn from a ** semantically and structurally equivalent source structure @@ -82,6 +82,8 @@ ** is defined in separate headers and included towards the bottom of this header. ** ** @see tree-mutator-test.cpp + ** @see tree-mutator-binding-test.cpp + ** @see diff-language.hpp ** @see DiffDetector ** */ diff --git a/tests/library/diff/tree-mutator-binding-test.cpp b/tests/library/diff/tree-mutator-binding-test.cpp index 8a782a732..731556209 100644 --- a/tests/library/diff/tree-mutator-binding-test.cpp +++ b/tests/library/diff/tree-mutator-binding-test.cpp @@ -33,9 +33,8 @@ #include "lib/error.hpp" #include "lib/util.hpp" -//#include +#include #include -//#include using util::join; using util::isnil; @@ -45,9 +44,6 @@ using lib::iter_stl::eachElm; using lib::time::Time; using std::string; -//using std::vector; -//using std::swap; - using util::typeStr; @@ -91,7 +87,7 @@ namespace test{ * - use a dummy diagnostic implementation to verify the interface * - verify an adapter to apply structure modification to a generic collection * - use closures to translate mutation into manipulation of private attributes - * - integrate the standard case of tree diff application to `Rec` + * @todo - integrate the standard case of tree diff application to `Rec` * * @remark even while this is a very long and detail oriented test, it barely * scratches the surface of what is possible with _layering multiple bindings_ @@ -107,7 +103,7 @@ namespace test{ * diff application to an arbitrary hierarchical data structure. In this way, the * following test cases demonstrate the intermediary steps executed when applying * this test diff through the concrete binding exemplified in each case - * @remark the **test diff** referred here reads as follows + * @remark the **test diff** implied here reads as follows * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * ins(ATTRIB1) * ins(ATTRIB3) @@ -658,7 +654,14 @@ namespace test{ - /** @test translate generic mutation into attribute manipulation */ + /** @test translate generic mutation into attribute manipulation + * - here we bind directly to data fields local to this scope + * - we execute the same diff primitives used in the preceding tests + * - yet binding to data fields has certain intrinsic limits; due to the + * fixed non-dynamic nature of data fields, it is impossible to define an + * "ordering" and consequently there is no _sequence of diff application._ + * - so the only form of actually _applying_ a change is to invoke the given + * setter or use the given mechanism to construct a nested mutator. */ void mutateAttribute () { @@ -690,14 +693,10 @@ namespace test{ gamma = val; }); -#if false /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #992 - CHECK (sizeof(mutator1) <= sizeof(VecD) // the buffer for pending elements - + sizeof(VecD*) // the reference to the original collection - + sizeof(void*) // the reference from the ChildCollectionMutator to the CollectionBinding - + 2 * sizeof(VecD::iterator) // one Lumiera RangeIter (comprised of pos and end iterators) - + 4 * sizeof(void*) // the four unused default configured binding functions - + 1 * sizeof(void*)); // one back reference from the closure to this scope -#endif /////////////////////////////////////////////////////////////////////////////////////////////////////////////UNIMPLEMENTED :: TICKET #992 + CHECK (sizeof(mutator1) <= sizeof(void*) // the TreeMutator VTable + + 2 * sizeof(void*) // one closure reference for each lambda + + 2 * sizeof(GenNode::ID)); // one attribute-key for each binding + // --- first round: introduce new "attributes" --- @@ -706,8 +705,8 @@ namespace test{ CHECK (-1 == beta); CHECK (-1 == gamma); - CHECK (mutator1.hasSrc()); // NOTE: the attribute binding always has an implicit "source sequence" - // (which is in fact fixed, because it relies on a likewise fixed class definition) + CHECK (mutator1.hasSrc()); // NOTE: the attribute binding has no "reference source sequence" and thus no dynamic state. + // (in fact it is predetermined, because it relies on a likewise fixed class definition) CHECK (mutator1.completeScope()); // NOTE: this is always true and NOP, for the same reason: the structure of the binding is fixed mutator1.injectNew (ATTRIB1); @@ -728,7 +727,7 @@ namespace test{ CHECK (not mutator1.injectNew (ATTRIB2)); // ...because we didn't define a binding for ATTRIB2 (aka "beta") // any changes to something other than attributes are just delegated to the next "onion layer" - // since in this case here, there is only one layer (our attribute binding), these other changes will be silently ignored + // since in this case here, there is only one layer (our attribute binding), these other changes will be ignored silently mutator1.injectNew (CHILD_B); mutator1.injectNew (CHILD_B); mutator1.injectNew (CHILD_T); @@ -740,6 +739,7 @@ namespace test{ cout << "successfully 'injected' new attributes." < subMutatorBuffer; TreeMutator::MutatorBuffer placementHandle(subMutatorBuffer); diff --git a/wiki/renderengine.html b/wiki/renderengine.html index cf3da5509..555a51bea 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -8214,7 +8214,7 @@ On receiving the terms of this "diff language", it is possible to gene 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.
 
@@ -8311,7 +8311,7 @@ builder interface, several ways of binding can be //layered on top of each other
 each layer is responsible for a specific aspect of the binding. This is crucial to deal with "objects" comprised of a mixture of children, like e.g. the [[Pipe]]
 attached to a clip, or the mixture of clips, effects and labels found within a [[Fork]] ("track").
 ;the selector
-:this is a lambda {{{isApplicableIf : bool(GenNode const&)}}}, provided by each //onion layer//
+:this is a lambda {{{isApplicableIf : bool(GenNode const&)}}}, provided separately by the concrete //onion layer//
 :* the selector is //optional// and only necessary when there is more than one binding layer
 :* this predicate decides if a given //spec,// as taken from a //diff verb// to be applied, is of relevance for the given //onion layer//
 :* if this onion layer is not concerned or affected by this message, it will be passed down to further layers, possibly ignoring it at the end
@@ -8338,8 +8338,9 @@ attached to a clip, or the mixture of clips, effects and labels found within a [
 :** the lambda is expected to //place// the generated mutator into the given buffer smart handle, which takes ownership of this object
 ;object field binding
 :this maps the concept of "Attribute" onto concrete, mutable data fields
-:any re-ordering, inserting and deleting of fields is ''prohibited'', while defaultable optional fields are tolerated
-:there is only one common »binding layer« for object fields (i.e. we don't offer a customisable selector)
+:any re-ordering  and deletion of fields is ''prohibited'', while defaultable optional fields may be tolerated
+:there is only one common combined binding for object fields (i.e. we don't offer a customisable selector),
+:where each individual binding is implemented on its own, as a self contained, isolated binding layer
 :binding is created ''alternatively'' through the following closures
 :* ''change'': "key", setter lambda {{{void(T val)}}}
 :** binding for a regular setter to assign a new value