Diff-Framework: investigate simplification for the most common case
After this long break during the "Covid Year 2020", I pick this clean-up task as a means to fresh up my knowledge about the code base The point to note is, when looking at all the existing diff bindings, seemingly there is a lot of redundancy on some technical details, which do not cary much meaining or relevance at the usage site: - the most prominent case is binding to a collection of DiffMutables hold by smart-ptr - all these objects expose an object identity (getID() function), which can be used as »Matcher« - and all these objects can just delegate to the child's buildMutator() function for entering a recursive mutation.
This commit is contained in:
parent
657b94a4e3
commit
05b5ee9a7e
7 changed files with 412 additions and 26 deletions
|
|
@ -68,7 +68,7 @@
|
|||
**
|
||||
** #### State and nested scopes
|
||||
** For performance reasons, the diff is applied _in place_, directly mutating the
|
||||
** target data structure. This makes the diff application _stateful_ -- and in case of
|
||||
** target data structure. This makes the diff application _statefull_ -- and in case of
|
||||
** a *diff conflict*, the target *will be corrupted*.
|
||||
**
|
||||
** Our tree like data structures are conceived as a system of nested scopes. Within
|
||||
|
|
|
|||
|
|
@ -31,13 +31,44 @@
|
|||
** Each concrete TreeMutator instance will be configured differently, and this
|
||||
** 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 especially this header
|
||||
** defines a building block for one such layer, especially for binding to a
|
||||
** several bindings on top of a single TreeMutator -- and indeed this header
|
||||
** defines a building block for one such layer, specifically 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
|
||||
** discouraged, since it contradicts the diff semantics due to intrinsic ordering.
|
||||
**
|
||||
** ## Internal structure
|
||||
**
|
||||
** The task to set up a binding to a _generic STL collection_ has to face some
|
||||
** technical intricacies, leading to a rather involved implementation, which can
|
||||
** be hard to understand and maintain. We attempt to address this challenge through
|
||||
** a decomposition into several sub-tasks, organised into four levels of abstraction
|
||||
** - at the bottom we use an adaptation layer in the form of a traits template,
|
||||
** with two concrete specialisations of the ContainerTraits for vector-like
|
||||
** and map-like collections
|
||||
** - on top of this the CollectionBinding is established to establish a kind of
|
||||
** generic access protocol for consuming a collection guided by diff instructions
|
||||
** - the third level then holds the actual TreeMutator implementation, embodied into
|
||||
** the ChildCollectionMutator template, which in fact translates and delegates
|
||||
** any actual access to the underlying collection to its embedded CollectionBinding
|
||||
** instance...
|
||||
** - which in turn is assembled on the top level, the DSL level, from building blocks
|
||||
** provided by the client of this collection binding. The entrance point to this
|
||||
** DSL layer is the _DefaultBinding, which is established by wrapping the actual
|
||||
** collection into the concrete CollectionBinding at the point where the builder
|
||||
** is created. The further DSL verbs on the CollectionBindingBuilder just server
|
||||
** to provide or overlay some lambdas to fill in the flexible parts of the binding.
|
||||
**
|
||||
** And these flexible parts are mostly concerned with the _actual contents_ of the
|
||||
** STL collection to be bound. Because, at this point, we can not assume much without
|
||||
** loosing genericity. Thus, the user of this binding has to fill in the missing link
|
||||
** - to decide if a given diff specification is addressed at this collection binding (»Selector«)
|
||||
** - when to consider a concrete content element as a _match_ for the diff specification (»Matcher«)
|
||||
** - the way actually to construct a new content element in accordance to the given diff spec (»Constructor«)
|
||||
** - the actual implementation of value assignment (optional)
|
||||
** - and the recursive entrance into mutation of a specific element within that collection (optional)
|
||||
**
|
||||
** @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
|
||||
**
|
||||
|
|
|
|||
189
src/lib/diff/tree-mutator-diffmutable-binding.hpp
Normal file
189
src/lib/diff/tree-mutator-diffmutable-binding.hpp
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
TREE-MUTATOR-DIFFMUTABLE-BINDING.hpp - default configuration to attach a collection of DiffMutable objects
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2021, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
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 tree-mutator-Diffmutable-binding.hpp
|
||||
** Special supplement for TreeMutator and the STL collection binding,
|
||||
** to provide a shortcut and default wiring for a collection holding
|
||||
** [DiffMutable](\ref diff-mutable.hpp) objects -- either directly or
|
||||
** by smart-ptr. TreeMutator is a customisable intermediary, which
|
||||
** enables otherwise opaque implementation data structures to receive
|
||||
** and respond to generic structural change messages ("tree diff").
|
||||
**
|
||||
** Each concrete TreeMutator instance will be configured differently, and this
|
||||
** adaptation is done by combining various building blocks. One of the most relevant
|
||||
** binding cases is to attach to a collection of child objects, which are themselves
|
||||
** _recursively diff mutable_. This header is based on the
|
||||
** [generic STL collection binding](\ref tree-mutator-collection-binding.hpp)
|
||||
** and provides the most common default implementation for a »Matcher« and
|
||||
** for building a recursive TreeMutator for the child elements by means of
|
||||
** delegating to their DiffMutable::buildMutator() function. An additional
|
||||
** requirement for this standard setup to be used is that the objects in the
|
||||
** collection must expose a `getID()` function to determine the object identity.
|
||||
**
|
||||
** @note the header tree-mutator-collection-diffmutable-binding.hpp was split off
|
||||
** or sake of readability and is included automatically from bottom of
|
||||
** tree-mutator.hpp -- no need to include it explicitly
|
||||
**
|
||||
** @see tree-mutator-test.cpp
|
||||
** @see _DefaultBinding
|
||||
** @see TreeMutator::build()
|
||||
**
|
||||
*/
|
||||
|
||||
|
||||
#ifndef LIB_DIFF_TREE_MUTATOR_DIFFMUTABLE_BINDING_H
|
||||
#define LIB_DIFF_TREE_MUTATOR_DIFFMUTABLE_BINDING_H
|
||||
|
||||
|
||||
#include "lib/error.hpp"
|
||||
#include "lib/meta/trait.hpp"
|
||||
#include "lib/diff/gen-node.hpp"
|
||||
#include "lib/diff/tree-mutator.hpp"
|
||||
#include "lib/diff/tree-mutator-collection-binding.hpp"
|
||||
|
||||
namespace lib {
|
||||
namespace diff{
|
||||
|
||||
class DiffMutable;
|
||||
|
||||
namespace { // Mutator-Builder decorator components...
|
||||
|
||||
using lib::meta::enable_if;
|
||||
using lib::meta::Yes_t;
|
||||
using lib::meta::No_t;
|
||||
|
||||
META_DETECT_FUNCTION(typename X::iterator, begin,(void));
|
||||
|
||||
template<typename T>
|
||||
class can_recursively_bind_DiffMutable
|
||||
{
|
||||
// typedef typename Strip<T>::Type Type;
|
||||
|
||||
|
||||
|
||||
public:
|
||||
enum { value = false
|
||||
// is_iterable::value
|
||||
// or is_const_iterable::value
|
||||
// or is_noexcept_iterable::value
|
||||
// or is_const_noexcept_iterable::value
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
template<typename T>
|
||||
class Is_DiffMutable
|
||||
{ };
|
||||
|
||||
template<typename T>
|
||||
class Is_wrapped_DiffMutable
|
||||
{ };
|
||||
|
||||
template<typename T>
|
||||
class Can_access_ID
|
||||
{ };
|
||||
|
||||
|
||||
template<class TAR, typename SEL =void>
|
||||
struct _AccessID
|
||||
{
|
||||
GenNode::ID const&
|
||||
getID (TAR const& target)
|
||||
{
|
||||
throw error::Logic ("Unable to access the target element's object ID. "
|
||||
"Please define a »Matcher« explicitly by invoking the builder function \"matchElement\".");
|
||||
}
|
||||
};
|
||||
template<class TAR>
|
||||
struct _AccessID<TAR, enable_if<Can_access_ID<TAR>>>
|
||||
{
|
||||
GenNode::ID const&
|
||||
getID (TAR const& target)
|
||||
{
|
||||
return target.getID();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<class ELM, typename SEL =void>
|
||||
struct _AccessTarget
|
||||
{
|
||||
DiffMutable&
|
||||
access (ELM const& elm)
|
||||
{
|
||||
throw error::Logic ("Unable to determine if the target is DiffMutable, and how to access it. "
|
||||
"Please define a »Mutator« explicitly by invoking the builder function \"buildChildMutator\".");
|
||||
}
|
||||
};
|
||||
template<class ELM>
|
||||
struct _AccessTarget<ELM, enable_if<Is_DiffMutable<ELM>>>
|
||||
{
|
||||
DiffMutable&
|
||||
access (ELM const& elm)
|
||||
{
|
||||
return elm;
|
||||
}
|
||||
};
|
||||
template<class ELM>
|
||||
struct _AccessTarget<ELM, enable_if<Is_wrapped_DiffMutable<ELM>>>
|
||||
{
|
||||
DiffMutable&
|
||||
access (ELM const& elm)
|
||||
{
|
||||
return *elm;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
/** */
|
||||
template<class ELM>
|
||||
struct _DefaultBinding<ELM, enable_if<can_recursively_bind_DiffMutable<ELM>>>
|
||||
: private _AccessTarget<ELM>
|
||||
, private _AccessID<ELM>
|
||||
{
|
||||
template<class COLL>
|
||||
static auto
|
||||
attachTo (COLL& coll)
|
||||
{
|
||||
return _EmptyBinding<ELM>::attachTo(coll)
|
||||
.matchElement([](GenNode const& spec, ELM const& elm)
|
||||
{
|
||||
return spec.idi == getID (access (elm));
|
||||
})
|
||||
.buildChildMutator ([&](ELM& target, GenNode::ID const&, TreeMutator::Handle buff) -> bool
|
||||
{
|
||||
access(target).buildMutator (buff);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}//(END)Mutator-Builder decorator components...
|
||||
|
||||
}} // namespace lib::diff
|
||||
#endif /*LIB_DIFF_TREE_MUTATOR_DIFFMUTABLE_BINDING_H*/
|
||||
|
|
@ -61,8 +61,8 @@
|
|||
|
||||
|
||||
#include "lib/diff/gen-node.hpp"
|
||||
#include "lib/diff/tree-mutator-collection-binding.hpp"
|
||||
#include "lib/diff/tree-mutator.hpp"
|
||||
#include "lib/diff/tree-mutator-collection-binding.hpp"
|
||||
|
||||
#include <tuple>
|
||||
|
||||
|
|
|
|||
|
|
@ -136,8 +136,8 @@ namespace diff{
|
|||
* on arbitrary, hierarchical object-like data.
|
||||
* The TreeMutator exposes two distinct interfaces
|
||||
* - the \em operation API -- similar to what a container exposes --
|
||||
* is the entirety of abstract operations that can be done to the
|
||||
* subsumed, tree like target structure
|
||||
* is the entirety of abstract operations that can be performed
|
||||
* on the subsumed, tree like target structure
|
||||
* - the \em binding API allows to link some or all of these generic
|
||||
* activities to concrete manipulations known within target scope.
|
||||
*/
|
||||
|
|
@ -197,8 +197,8 @@ namespace diff{
|
|||
* to prevent a SEGFAULT. Thus `skipSrc` can not match
|
||||
* and thus can not return anything. Consequently the
|
||||
* `del` implementation has to use `matchSrc` explicitly,
|
||||
* and the latter must invoke the selector prior to
|
||||
* performing the local match. */
|
||||
* and the latter must invoke the "layer selector" prior
|
||||
* to performing the local match. */
|
||||
virtual void
|
||||
skipSrc (GenNode const&)
|
||||
{
|
||||
|
|
@ -412,9 +412,12 @@ namespace diff{
|
|||
* allows for recursive descent into nested child scopes. On invocation, it has
|
||||
* to build a suitable custom TreeMutator implementation into the provided buffer
|
||||
* (handle), and this nested TreeMutator should be wired with the internal
|
||||
* representation of the nested scope to enter. The code invoking this closure
|
||||
* typically pushes the buffer on some internal stack and switches then to use
|
||||
* this nested mutator until encountering the corresponding `EMU` bracket verb.
|
||||
* representation of the nested scope to enter. At this point, the implementation
|
||||
* can safely assume that the given `target` data element has already be checked
|
||||
* with the configured _matcher closure_, and thus can be considered equivalent
|
||||
* to the given ID. The code invoking this closure then typically pushes the
|
||||
* buffer onto some internal stack and switches then to use this nested mutator
|
||||
* until encountering the corresponding `EMU` bracket verb.
|
||||
* @note the `after(Ref::ATTRIBS)` verb can only processed if the selector responds
|
||||
* correct to a Ref::ATTRIBS spec. The implicit default selector does so, i.e.
|
||||
* it rejects `Ref::ATTRIBS`. Please be sure to accept this token _only_ if
|
||||
|
|
@ -484,7 +487,6 @@ namespace diff{
|
|||
|
||||
|
||||
}} // namespace lib::diff
|
||||
#endif /*LIB_DIFF_TREE_MUTATOR_H*/
|
||||
|
||||
|
||||
/* == implementation detail headers == */
|
||||
|
|
@ -492,6 +494,10 @@ namespace diff{
|
|||
#include "lib/diff/tree-mutator-gen-node-binding.hpp"
|
||||
#include "lib/diff/tree-mutator-attribute-binding.hpp"
|
||||
#include "lib/diff/tree-mutator-collection-binding.hpp"
|
||||
#include "lib/diff/tree-mutator-diffmutable-binding.hpp"
|
||||
#include "lib/diff/tree-mutator-listener-binding.hpp"
|
||||
#include "lib/diff/tree-mutator-noop-binding.hpp"
|
||||
|
||||
|
||||
|
||||
#endif /*LIB_DIFF_TREE_MUTATOR_H*/
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ namespace test{
|
|||
}
|
||||
|
||||
|
||||
/** @test mutate a Record<GenNode> by applying a the sample diff */
|
||||
/** @test mutate a Record<GenNode> by applying the [sample diff](\ref #someDiff) */
|
||||
void
|
||||
demo_one()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -37878,6 +37878,39 @@
|
|||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1573835265349" ID="ID_352943196" MODIFIED="1573835276049" TEXT="Diff-Anwendung einbinden">
|
||||
<icon BUILTIN="pencil"/>
|
||||
<node COLOR="#338800" CREATED="1611218580537" ID="ID_1387738684" MODIFIED="1611218916752" TEXT=""habituellen" Standard-Fall etablieren">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node CREATED="1611218653199" ID="ID_353919229" MODIFIED="1611218879611" TEXT="einzelne Objekt-Properties werden explizit gebunden">
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
<node CREATED="1611218673196" ID="ID_104829523" MODIFIED="1611218882295" TEXT="ansonsten nur Sub-Collections">
|
||||
<icon BUILTIN="idea"/>
|
||||
<node CREATED="1611218685874" ID="ID_503186465" MODIFIED="1611218887833" TEXT="STL-Collection von Smart-Pointern">
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
<node CREATED="1611218698241" ID="ID_766067661" MODIFIED="1611218890019" TEXT="das Pointee ist wieder DiffMutable">
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
<node COLOR="#435e98" CREATED="1611218724397" ID="ID_495328814" MODIFIED="1611218907878" TEXT="Bindungs-Schema">
|
||||
<font BOLD="true" NAME="SansSerif" SIZE="12"/>
|
||||
<icon BUILTIN="forward"/>
|
||||
<node CREATED="1611218730812" ID="ID_718558563" MODIFIED="1611218900241" TEXT="der Selector prüft ein Typfeld">
|
||||
<icon BUILTIN="yes"/>
|
||||
</node>
|
||||
<node CREATED="1611218788908" ID="ID_1503520124" MODIFIED="1611218900241" TEXT="der Matcher prüft die Objekt-ID">
|
||||
<icon BUILTIN="yes"/>
|
||||
</node>
|
||||
<node CREATED="1611218813169" ID="ID_1174818531" MODIFIED="1611218900241" TEXT="der Konstruktor enthält das relevante Closure-Binding">
|
||||
<icon BUILTIN="yes"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1611218846053" ID="ID_1626706964" MODIFIED="1611221187326" TEXT="Standard-Implementierung für den rekursiven Kind-Mutator">
|
||||
<arrowlink COLOR="#64b2d0" DESTINATION="ID_341242193" ENDARROW="Default" ENDINCLINATION="-1947;126;" ID="Arrow_ID_1779456185" STARTARROW="None" STARTINCLINATION="1123;53;"/>
|
||||
<icon BUILTIN="yes"/>
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1573835763482" ID="ID_1220310042" MODIFIED="1576441741737" TEXT="Problem: Slave-Collection">
|
||||
<arrowlink COLOR="#519b84" DESTINATION="ID_727500820" ENDARROW="Default" ENDINCLINATION="-67;7;" ID="Arrow_ID_115955006" STARTARROW="None" STARTINCLINATION="71;200;"/>
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
|
|
@ -42385,7 +42418,7 @@
|
|||
</node>
|
||||
<node CREATED="1465428839332" ID="ID_655341963" MODIFIED="1561827465587" TEXT="Diff-Anwendung" VSHIFT="36">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node CREATED="1465428850946" FOLDED="true" ID="ID_1546287157" MODIFIED="1576200627485">
|
||||
<node COLOR="#435e98" CREATED="1465428850946" FOLDED="true" MODIFIED="1611218290572">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
|
|
@ -42486,14 +42519,54 @@
|
|||
<icon BUILTIN="yes"/>
|
||||
<node CREATED="1465666541419" ID="ID_787956112" MODIFIED="1465666552293" TEXT="entweder ein festes Interface"/>
|
||||
<node CREATED="1465666552842" ID="ID_1360512685" MODIFIED="1465666574594" TEXT="oder über Metaprogrammierung / Spezialisierung"/>
|
||||
<node CREATED="1468761997202" FOLDED="true" ID="ID_206940182" MODIFIED="1561827483836" TEXT="kombinierte Lösung">
|
||||
<node CREATED="1468761997202" FOLDED="true" ID="ID_206940182" MODIFIED="1611219082511" TEXT="kombinierte Lösung">
|
||||
<icon BUILTIN="idea"/>
|
||||
<node CREATED="1468762009304" ID="ID_170152384" MODIFIED="1468762018314" TEXT="Interface DiffMutable"/>
|
||||
<node CREATED="1468762009304" ID="ID_170152384" MODIFIED="1611219253284" TEXT="Interface DiffMutable">
|
||||
<icon BUILTIN="forward"/>
|
||||
<node COLOR="#435e98" CREATED="1611219255178" ID="ID_790740828" MODIFIED="1611221058698" TEXT="dies entwickelt sich zum Standardfall...">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
...zumindest im GUI, wo parktisch alle Empfänger auch ein Tangible (Widget oder Controller) sind
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<font ITALIC="true" NAME="SansSerif" SIZE="12"/>
|
||||
</node>
|
||||
<node CREATED="1611221009482" ID="ID_1797198606" MODIFIED="1611226329184" TEXT="exponiert builder-Methode">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
...und genau diese virtuelle Builder-Methode ist üblicherweise der Ort, wo mit einer DSL und über Lambdas das Mapping auf die internen Strukturen des DiffMutable hergestellt wird; deshalb kann auch DiffMutable selber ein sehr schlankes Interface sein; der resultierende TreeMutator ist dann eine für den jeweiligen konkreten Typ aus Bausteinen generierte Hilfsklasse
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
<node CREATED="1611226844354" ID="ID_252206662" MODIFIED="1611227312132" TEXT="oft in einer Collection...">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
...und dort per smart-Ptr.<br />Für diesen Fall kann die Verdrahtung weitgehend automatisch konfiguiert werden, und man muß eigentlich nur noch den Konstruktor-Aufruf explizit (per Lambda) in das TreeMuator-Binding integrieren. Wenn es mehrere »onion layer« gibt, muß allerdings auch noch ein »Selector« definiert werden, um zu steuern, wo genau dieses Binding angewendet wird, und wo sonst auf einen anderen Layer delegiert wird, z.B. für explizit gebundene Objekt-Attribute.
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<arrowlink COLOR="#1ebca6" DESTINATION="ID_489519336" ENDARROW="Default" ENDINCLINATION="689;-28;" ID="Arrow_ID_1973768518" STARTARROW="None" STARTINCLINATION="490;52;"/>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1468762031701" FOLDED="true" ID="ID_1455212830" MODIFIED="1561827482931" TEXT="Meta-Adapter">
|
||||
<linktarget COLOR="#503382" DESTINATION="ID_1455212830" ENDARROW="Default" ENDINCLINATION="8;131;" ID="Arrow_ID_1591967504" SOURCE="ID_993360353" STARTARROW="None" STARTINCLINATION="535;0;"/>
|
||||
<linktarget COLOR="#503382" DESTINATION="ID_1455212830" ENDARROW="Default" ENDINCLINATION="32;58;" ID="Arrow_ID_1591967504" SOURCE="ID_993360353" STARTARROW="None" STARTINCLINATION="222;18;"/>
|
||||
<node CREATED="1468762052858" ID="ID_1870920189" MODIFIED="1468762066764" TEXT="synthetisiert dieses Interface"/>
|
||||
<node CREATED="1468762067383" ID="ID_1801995068" MODIFIED="1468762075059" TEXT="erkennt passende Methoden"/>
|
||||
<node CREATED="1468762075671" FOLDED="true" ID="ID_1201929549" MODIFIED="1561827469159" TEXT="Problem: Storage">
|
||||
<node CREATED="1468762075671" ID="ID_1201929549" MODIFIED="1611219223899" TEXT="Problem: Storage">
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
<node CREATED="1468762092660" ID="ID_1968257665" MODIFIED="1468762097264" TEXT="Adapter oder Lambda"/>
|
||||
<node CREATED="1468762097684" ID="ID_438037423" MODIFIED="1468762107374" TEXT="muß länger leben als der Adapter"/>
|
||||
|
|
@ -42530,7 +42603,7 @@
|
|||
<icon BUILTIN="messagebox_warning"/>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1469121877371" FOLDED="true" HGAP="38" ID="ID_1614796496" MODIFIED="1561827469159" TEXT="freie funktion" VSHIFT="16">
|
||||
<node CREATED="1469121877371" FOLDED="true" HGAP="38" ID="ID_1614796496" MODIFIED="1561827469159" TEXT="freie Funktion" VSHIFT="16">
|
||||
<icon BUILTIN="yes"/>
|
||||
<node CREATED="1469121882782" ID="ID_695291968" MODIFIED="1469121933463" TEXT="keine Spezialisierung notwendig">
|
||||
<icon BUILTIN="idea"/>
|
||||
|
|
@ -42574,7 +42647,7 @@
|
|||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<arrowlink COLOR="#503382" DESTINATION="ID_1455212830" ENDARROW="Default" ENDINCLINATION="8;131;" ID="Arrow_ID_1591967504" STARTARROW="None" STARTINCLINATION="535;0;"/>
|
||||
<arrowlink COLOR="#503382" DESTINATION="ID_1455212830" ENDARROW="Default" ENDINCLINATION="32;58;" ID="Arrow_ID_1591967504" STARTARROW="None" STARTINCLINATION="222;18;"/>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1469544264955" HGAP="35" ID="ID_1303383911" MODIFIED="1561827465596" VSHIFT="16">
|
||||
|
|
@ -42719,6 +42792,7 @@
|
|||
<node CREATED="1469545456549" ID="ID_300958644" MODIFIED="1469545459401" TEXT="ScopeManager"/>
|
||||
</node>
|
||||
<node CREATED="1469545522445" FOLDED="true" ID="ID_1403539911" MODIFIED="1561827482932" TEXT="ScopeManager">
|
||||
<icon BUILTIN="info"/>
|
||||
<node CREATED="1469545526380" ID="ID_1121286637" MODIFIED="1469545535550" TEXT="Imp. wird per Zieltyp generiert"/>
|
||||
<node CREATED="1469545536139" ID="ID_53324473" MODIFIED="1469545543750" TEXT="lebt in opaquem Buffer"/>
|
||||
<node CREATED="1469545546209" ID="ID_347304855" MODIFIED="1469545561067" TEXT="enthält Stack,"/>
|
||||
|
|
@ -42964,7 +43038,7 @@
|
|||
<node CREATED="1472172593710" ID="ID_539813118" MODIFIED="1472172608024" TEXT="geschachtelter Scope muß beim Verlassen komplett abgearbeitet sein"/>
|
||||
<node CREATED="1472172608636" ID="ID_904329399" MODIFIED="1472172634020" TEXT="wir steigen niemals über den root-Scope hinaus auf"/>
|
||||
</node>
|
||||
<node CREATED="1472172651974" ID="ID_717242686" MODIFIED="1512926192130" TEXT="ins-Problem">
|
||||
<node COLOR="#435e98" CREATED="1472172651974" FOLDED="true" ID="ID_717242686" MODIFIED="1611218246329" TEXT="ins-Problem beim Mutieren von Attributen">
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
<node CREATED="1472172658349" ID="ID_279579205" MODIFIED="1472172806077">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
|
|
@ -42986,18 +43060,104 @@
|
|||
<icon BUILTIN="messagebox_warning"/>
|
||||
</node>
|
||||
<node CREATED="1472172711846" ID="ID_67509104" MODIFIED="1472172809794" TEXT="wir haben es hier mit objektwertigen Attributen zu tun">
|
||||
<icon BUILTIN="idea"/>
|
||||
<icon BUILTIN="info"/>
|
||||
</node>
|
||||
<node CREATED="1472172723853" ID="ID_397776543" MODIFIED="1472172732503" TEXT="das ist ein sonderbarer Grenzfall"/>
|
||||
<node CREATED="1472172733307" ID="ID_1698922494" MODIFIED="1472172753564" TEXT="man wird die normalerweise auf Impl-Ebene erzeugen"/>
|
||||
<node CREATED="1472172723853" ID="ID_397776543" MODIFIED="1472172732503" TEXT="das ist ein sonderbarer Grenzfall">
|
||||
<icon BUILTIN="idea"/>
|
||||
<node CREATED="1611218058623" ID="ID_948845594" MODIFIED="1611218075752" TEXT="Attribute tendieren zur Wertsemantik"/>
|
||||
<node CREATED="1611218104017" ID="ID_1247405117" MODIFIED="1611218113067" TEXT="Attribute kann man eigentlich auch nicht umordnen"/>
|
||||
<node CREATED="1611218115055" ID="ID_1628532981" MODIFIED="1611218183029" TEXT="Objekte können zudem Ctor-Constraints haben"/>
|
||||
</node>
|
||||
<node CREATED="1472172733307" ID="ID_1698922494" MODIFIED="1611217973598" TEXT="Attribute werden normalerweise fest in der Objekt-Impl erzeugt"/>
|
||||
<node CREATED="1472172754136" ID="ID_205435085" MODIFIED="1472172765458" TEXT="ein INS hat dann nichts sinnvolles zu tun"/>
|
||||
<node CREATED="1472172772494" ID="ID_842638924" MODIFIED="1472172795138" TEXT="default-Implementierung absorbiert das INS stillschweigend">
|
||||
<node COLOR="#435e98" CREATED="1472172772494" ID="ID_842638924" MODIFIED="1472172795138" TEXT="default-Implementierung absorbiert das INS stillschweigend">
|
||||
<icon BUILTIN="forward"/>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1611218339864" ID="ID_341242193" MODIFIED="1611314480869" TEXT="Abkürzung für rekursives Standard-Binding">
|
||||
<linktarget COLOR="#64b2d0" DESTINATION="ID_341242193" ENDARROW="Default" ENDINCLINATION="-1947;126;" ID="Arrow_ID_1779456185" SOURCE="ID_1626706964" STARTARROW="None" STARTINCLINATION="1123;53;"/>
|
||||
<icon BUILTIN="pencil"/>
|
||||
<node CREATED="1611227233973" ID="ID_489519336" MODIFIED="1611227312132" TEXT="für den häufigsten Fall: STL-Collection von Smart-Ptr auf DiffMutable">
|
||||
<linktarget COLOR="#1ebca6" DESTINATION="ID_489519336" ENDARROW="Default" ENDINCLINATION="689;-28;" ID="Arrow_ID_1973768518" SOURCE="ID_252206662" STARTARROW="None" STARTINCLINATION="490;52;"/>
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
<node CREATED="1611227337956" ID="ID_284850401" MODIFIED="1611227346387" TEXT="wird per MetaPrädikat erkannt">
|
||||
<node CREATED="1611227487011" ID="ID_1701933719" MODIFIED="1611227500783" TEXT="wenn die Elemente entweder selber direkt DiffMutable sind"/>
|
||||
<node CREATED="1611227501552" ID="ID_752689541" MODIFIED="1611227529633" TEXT="oder alternativ ein (smart)Pointer/Wrapper auf ein DiffMutable"/>
|
||||
</node>
|
||||
<node CREATED="1611261538911" ID="ID_498748918" MODIFIED="1611261552068" TEXT="Standard-Imlementierung">
|
||||
<node CREATED="1611261602057" ID="ID_160823769" MODIFIED="1611261614523" TEXT="inzwischen hat sich ein Standard-Schema etabliert">
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
<node CREATED="1611261556183" ID="ID_236586609" MODIFIED="1611261601349" TEXT="Frage: warum machen wir einen ID-Match?">
|
||||
<node CREATED="1611261593546" ID="ID_1666826303" MODIFIED="1611261791971" TEXT="typischerweise prüfen wir zu Beginn, ob das zu mutierende Sub-Element auch matcht"/>
|
||||
<node CREATED="1611261792751" ID="ID_1188415737" MODIFIED="1611261815910" TEXT="man könnte hierfür den »Matcher« des Collection-Bindings verwenden"/>
|
||||
<node CREATED="1611261840272" ID="ID_922368010" MODIFIED="1611261871424" TEXT="mehr noch: wie kommt es zu diesem Match-Guard?">
|
||||
<node CREATED="1611261874508" ID="ID_1431589128" MODIFIED="1611261892215" TEXT="das Standard-Schema hat sich per Copy-n-Paste entwickelt">
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
<node CREATED="1611261893793" ID="ID_1503357970" MODIFIED="1611261910755" TEXT="könnte durchaus sein, daß dieser Schritt redundant ist"/>
|
||||
<node CREATED="1611270254702" ID="ID_781667464" MODIFIED="1611271568520" TEXT="und genau so ist es">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Das ist verständlich aus der Historie: Zunächst einmal war der TreeMutator gedacht als ein Interface, das der client des Diff-Frameworks zu implementieren hat. Nachdem ich diese Übung aber drei mal gemacht hatte, war mir klar, daß dies zu viel verlangt ist. Denn der TreeMutator ist notwendigerweise stark an den Implementierungs-Ansatz im Diff-Framework gebunden. Das heißt, man kann dieses Interface nur implementieren, wenn man diese interne Funktionsweise verstanden hat. Und das Diff-Framework würde seinen Zweck verfehlen, wenn der Nutzer dieses Wissen haben müßte. Also habe ich über den TreeMutator ein Baukastensystem errichtet, welches über Lambdas in den Anwendungskontext gebunden wird.
|
||||
</p>
|
||||
<p>
|
||||
|
||||
</p>
|
||||
<p>
|
||||
In einem zweiten Anlauf habe ich schließlich die schon bestehenden, explizitien Implementierungen des TreeMutator-Interfaces allesamt "eingefangen" und durch ein Meshup aus dem Bauskastensystem ersetzt. Daher gibt es jetzt nur noch eine einzige valide Implementierung, nämlich die im Baukasen. Und das soll auch so bleiben.
|
||||
</p>
|
||||
<p>
|
||||
|
||||
</p>
|
||||
<p>
|
||||
Daher ist es zulässig, sich diese Implementierung anzuschauen: in der Tat geht nämlich dort jedem Aufruf des Mutators ein Suchvorgang voraus, und dieser endet immer mit einer erfolgreichen Anwendung des Matchers. Daher ist es grundsätzlich nicht notwendig, den ID-Match nochmal zu prüfen, bevor man in die rekursive Mutation einsteigt.
|
||||
</p>
|
||||
<p>
|
||||
|
||||
</p>
|
||||
<p>
|
||||
Wie kommt nun aber dieser explizit ausprogrammierte ID-Match in die Standard-Implementierung? Die Antwort ist, letztlich aus Verlegenheit. Denn zunächst einmal hatte ich für den Unit-Test alles für ein sehr spezielles Setup ausprogrammiert. Das ist auch gut so, denn dieses Setup deckt auch Grenzfälle mit ab. Also im Besonderen auch Bindings, die sich sehr speziell in Implementierungsstrukturen einklinken, und eben grade nicht direkt an ein DiffMutable-Subobjekt delegieren. Und diese Offenheit ist der essentielle Grund, warum ich auf ein Diff-Framework setze, und nicht auf ein Datenmodell mit festen Interfaces. Diese Offenheit bedingt aber auch, daß man dem Client die letztendliche Übersetzung der IDs überlassen muß. Für die rekursive Kind-Mutation muß der Client sich ggfs ein internes Implementierungs-Objekt anhand der gegebenen ID heraussuchen.
|
||||
</p>
|
||||
<p>
|
||||
|
||||
</p>
|
||||
<p>
|
||||
Im einfachen Standardfall jedoch ist das nicht notwendig, denn in diesem häufigsten Standardfall haben wir mehr oder weniger direkt das Objekt aus der Collection in der Hand, auf welches dann der rekursive Mutator angewendet werden soll. Also stand ich beim ersten Schreiben einer Implementierung für diesen einfachen Standardfall vor dem Paradoxon, daß die Funktion einen ID-Parameter bekommt, den man anscheinend hier gar nicht braucht. Und, ohne diese Zusammenhänge damals zu verstehen, habe ich dann aus Verlegenheit noch eine Zeile Code eingebaut, die "was Sinnvolles mit dieser ID macht", denn es war ja zunächst auch erst mal ein Proof-of-Concept (und zu diesem Zeitpunkt gab es noch zwei weitere, alternative Implementierungen des TreeMutator-Interfaces). Letztlich hat sich dann dieses "anstandshalber" eingebaute Zeile, eben genau der ID-match, welcher mithin "etwas Sinnvolles" mit der ID macht, per Copy-n-Paste in alle konkreten Implementierungen fortgepflanzt. Wiewohl dieser Schritt im Stand der gegenwärtigen Implementierung stets redundant ist.
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="info"/>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1465860740971" ID="ID_1905457534" MODIFIED="1512926192131" TEXT="Unit-Test">
|
||||
<node BACKGROUND_COLOR="#ccb59b" COLOR="#6e2a38" CREATED="1611271855692" ID="ID_1449093831" MODIFIED="1611271873489" TEXT="Fazit: der Match-Guard kann aus allen Implementierungen entfernt werden">
|
||||
<font ITALIC="true" NAME="SansSerif" SIZE="14"/>
|
||||
<icon BUILTIN="yes"/>
|
||||
<node CREATED="1611271971948" ID="ID_1541118749" MODIFIED="1611271982750" TEXT="der ID-Parameter wird normalerweise nicht gebraucht"/>
|
||||
<node CREATED="1611271983506" ID="ID_1671925205" MODIFIED="1611272055176" TEXT="er ist nur für Grenzfälle gedacht">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
...falls die clientseitige Datenstruktur einen Index verwendet, um per ID die eigentliche Zieldatenstruktur zu betreten.
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1465860740971" ID="ID_1905457534" MODIFIED="1512926192131" TEXT="Unit-Test">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node CREATED="1472122365079" ID="ID_781825543" MODIFIED="1512926192131" TEXT="Aussagekräftiges Beispiel-Diff">
|
||||
<node CREATED="1472122379548" ID="ID_1273829051" MODIFIED="1472122384864" TEXT="sollte alle Features vorführen"/>
|
||||
|
|
|
|||
Loading…
Reference in a new issue