DiffFramework: simplify existing bindings

...by relying on the newly implemented automatic standard binding
Looks like a significant improvement for me, now the actual bindings
only details aspects, which are related to the target, and no longer
such technicalitis like how to place a Child-Mutator into a buffer handle
This commit is contained in:
Fischlurch 2021-01-23 11:54:19 +01:00
parent c52576ffc7
commit 06dbb9fad5
14 changed files with 209 additions and 168 deletions

View file

@ -44,42 +44,34 @@
// 04/19 - forwarding tuple element(s) to function invocation
// 06/19 - use a statefull counting filter in a treeExplorer pipeline
// 03/20 - investigate type deduction bug with PtrDerefIter
// 01/21 - look for ways to detect the presence of an (possibly inherited) getID() function
/** @file try.cpp
* Compiling a seemingly valid iterator pipeline failed, due to type deduction problems.
* As expected, they originate within PtrDerefIter, which I abused here to dereference
* an unique_ptr -- which might seem strange, yet is true to the spirit of generic programming.
* Since I consider this a valid usage, the fix is to add a further specialisation to my
* hand-written RemovePtr trait template in iter-adapter-ptr-deref.hpp (which also justifies
* in hindsight to use a hand-written trait right within this header, instead of some library).
* Verify a way to detect the presence of a specific implementation function,
* even when it is just inherited and thus not part of the concrete class definition.
* The trick is to _emulate the use_ of the object method in question within a `decltype( )`
* statement, which in turn is used to build the concrete template signature.
*/
typedef unsigned int uint;
namespace std {
template <typename _Tp, typename _Dp>
class unique_ptr;
}
#include "lib/format-cout.hpp"
#include "lib/test/test-helper.hpp"
#include "lib/util.hpp"
#include "lib/iter-adapter-ptr-deref.hpp"
#include "lib/iter-adapter-stl.hpp"
#include "lib/itertools.hpp"
#include "lib/util-coll.hpp"
#include "lib/idi/entry-id.hpp"
#include "lib/meta/duck-detector.hpp"
#include <utility>
#include <string>
#include <vector>
#include <memory>
#include <type_traits>
using std::string;
using std::make_unique;
using util::max;
using lib::idi::EntryID;
using lib::idi::BareEntryID;
using lib::meta::Yes_t;
using lib::meta::No_t;
#define SHOW_TYPE(_TY_) \
@ -87,35 +79,71 @@ using util::max;
#define SHOW_EXPR(_XX_) \
cout << "Probe " << STRINGIFY(_XX_) << " ? = " << _XX_ <<endl;
using PStr = std::unique_ptr<string>;
using Strs = std::vector<PStr>;
constexpr auto elems = [](auto& coll) { return lib::ptrDeref (lib::iter_stl::eachElm (coll)); };
META_DETECT_FUNCTION(BareEntryID const&, getID,(void) const);
META_DETECT_FUNCTION_ARGLESS(getID);
template<typename TY>
class Can_retrieve_and_compare_ID
{
template<typename X,
typename SEL = decltype(std::declval<BareEntryID>() == std::declval<X>().getID())>
struct Probe
{ }; \
\
template<class X> \
static Yes_t check(Probe<X> * ); \
template<class> \
static No_t check(...); \
\
public: \
static const bool value = (sizeof(Yes_t)==sizeof(check<TY>(0))); \
};
class Base
{
EntryID<Base> idi;
public:
BareEntryID const&
getID() const
{
return idi;
}
};
class Derived
: public Base
{ };
class Derailed
{ };
int
main (int, char**)
{
Strs ss;
ss.emplace_back(new string{"li"});
ss.emplace_back(new string{"la"});
ss.emplace_back(new string{"lutsch"});
SHOW_EXPR (ss);
SHOW_EXPR (elems(ss));
using ITS = decltype(elems(ss));
SHOW_TYPE (ITS);
Base b1;
Derived d1;
Derailed r1;
SHOW_EXPR( b1 );
SHOW_EXPR( b1.getID() );
SHOW_EXPR( HasFunSig_getID<Base>::value );
SHOW_EXPR( HasArglessFun_getID<Base>::value );
SHOW_EXPR( Can_retrieve_and_compare_ID<Base>::value );
// using ITSR = typename ITS::reference;
// lib::test::TypeDebugger<ITSR> buggy;
SHOW_EXPR( d1 );
SHOW_EXPR( d1.getID() );
SHOW_EXPR( HasFunSig_getID<Derived>::value );
SHOW_EXPR( HasArglessFun_getID<Derived>::value );
SHOW_EXPR( Can_retrieve_and_compare_ID<Derived>::value );
auto dings = elems(ss);
SHOW_EXPR( r1 );
SHOW_EXPR( HasFunSig_getID<Derailed>::value );
SHOW_EXPR( HasArglessFun_getID<Derailed>::value );
SHOW_EXPR( Can_retrieve_and_compare_ID<Derailed>::value );
int maxVSize = max (lib::transformIterator(dings,
[](string const& ding)
{
return ding.length();
}));
SHOW_EXPR (maxVSize);
// SHOW_TYPE( decltype( d1.getID() ))
cout << "\n.gulp.\n";
return 0;

View file

@ -89,4 +89,8 @@ namespace diff{
}} // namespace lib::diff
// enable an extension to the TreeMutator builder DSL for DiffMutable
#include "lib/diff/tree-mutator-diffmutable-binding.hpp"
#endif /*LIB_DIFF_DIFF_MUTABLE_H*/

View file

@ -397,7 +397,7 @@ namespace diff{
if (binding_.isApplicable(n))
{
bool isSrcMatch = pos_ and binding_.matches (n, *pos_);
if (isSrcMatch) //NOTE: crucial to perform only our own match check here
if (isSrcMatch) //NOTE: crucial to perform only our own match check here
{
binding_.inject (move(*pos_));
++pos_;

View file

@ -21,7 +21,7 @@
*/
/** @file tree-mutator-Diffmutable-binding.hpp
/** @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
@ -32,7 +32,7 @@
** 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
** _recursively diff mutable_. This header here 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
@ -40,9 +40,9 @@
** 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
** @note the header tree-mutator-diffmutable-binding.hpp was split off
** for sake of readability and is included automatically from the
** bottom of diff-mutable.hpp -- no need to include it explicitly
**
** @see tree-mutator-test.cpp
** @see _DefaultBinding
@ -65,8 +65,6 @@
namespace lib {
namespace diff{
class DiffMutable;
namespace { // Mutator-Builder decorator components...
using lib::meta::enable_if;
@ -75,20 +73,43 @@ namespace diff{
using std::__and_;
using std::__or_;
META_DETECT_FUNCTION(GenNode::ID const&, getID,(void));
/**
* Metaprogramming helper to detect if the given target class allows us
* to build a default »Matcher« automatically. (The »Matcher« is used to
* determine the applicability of a given diff verb to this target object)
* @note we directly probe the desired functionality: can we equality compare
* a given GenNode::ID (from the diff) with this type's object ID?
* @see duck-detector.hpp for explanation of this technique
*/
template<typename TY>
class Can_retrieve_and_compare_ID
{
template<typename X,
typename SEL = decltype(std::declval<GenNode::ID>() == std::declval<X>().getID())>
struct Probe
{ };
template<class X>
static Yes_t check(Probe<X> * );
template<class>
static No_t check(...);
public:
static const bool value = (sizeof(Yes_t)==sizeof(check<TY>(0)));
};
template<typename T>
struct Can_access_ID
: HasFunSig_getID<typename meta::Strip<T>::Type>
: Can_retrieve_and_compare_ID<typename meta::Strip<T>::Type>
{ };
template<typename T>
struct Is_DiffMutable
struct Is_DiffMutable ///< Metafunction: does the target implement the DiffMutable interface?
: meta::is_Subclass<T,DiffMutable>
{ };
template<typename T>
struct Is_wrapped_DiffMutable
struct Is_wrapped_DiffMutable ///< Metafunction: is this a DiffMutable wrapped into a smart-ptr?
: __and_< meta::Unwrap<T>
, Is_DiffMutable<typename meta::Unwrap<T>::Type>>
{ };
@ -98,22 +119,26 @@ namespace diff{
: __or_< Is_DiffMutable<T>
, Is_wrapped_DiffMutable<T>>
{ } ;
/**
* Metaprogramming helper to retrieve the object identity, whenever
* the target object for the diff defines a `getID()` function.
*/
template<class TAR, typename SEL =void>
struct _AccessID
{
static GenNode::ID const&
static idi::BareEntryID const&
getID (TAR const&)
{
throw error::Logic ("Unable to access the target element's object ID. "
throw error::Logic ("TreeMutator::build() .attach(collection...) : Unable to access the target element's object ID. "
"Please define a »Matcher« explicitly by invoking the builder function \"matchElement\".");
}
};
template<class ELM>
struct _AccessID<ELM, enable_if<Can_access_ID<ELM>>>
{
static GenNode::ID const&
static idi::BareEntryID const&
getID (ELM const& target)
{
return meta::unwrap(target).getID();
@ -122,7 +147,15 @@ namespace diff{
/** */
/**
* Entry Point: Specialisation of the collection binding to work on a collection of DiffMutable objects,
* either embedded directly, or attached via smart-ptr. Since the DiffMutable interface directly
* exposes a function to build a TreeMutator, a generic implementation for recursive child mutation
* can be supplied automatically. Moreover, if the target objects also offer a `getID()` function
* to reveal their object identity, the »Matcher« (to check applicability of some diff verb) can
* likewise be generated automatically.
* @note different than in the base case, recursive child mutation is thus enabled automatically.
*/
template<class ELM>
struct _DefaultBinding<ELM, enable_if<can_recursively_bind_DiffMutable<ELM>>>
{
@ -145,7 +178,7 @@ namespace diff{
};
}//(END)Mutator-Builder decorator components...

View file

@ -494,7 +494,6 @@ 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"

View file

@ -34,7 +34,7 @@
**
** While C++ certainly isn't a dynamic language and does not provide any kind of run time introspection,
** doing such check-and branch at compile time allows to combine flexibility as known from dynamic
** languages with static type safety, which is compelling. We can generate similar implementation
** languages with static type safety, which is compelling. We can generate similar implementations
** for types not further related by inheritance. Building on this, we're able to emulate some
** of the features enabled by type classes (or "concepts").
**
@ -62,8 +62,8 @@
** to determine if a member function of a type in question has the desired signature.
**
** All these detection building blocks are written such as to provide a bool member `::value`,
** which is in accordance to the conventions of C++11 metaprogramming. I.e. you can immediately
** use them within `std::enable_if`
** which is in accordance to the conventions of modern C++ metaprogramming. I.e. you can
** directly use them within `std::enable_if`
**
** # some pitfalls to consider
**
@ -72,7 +72,7 @@
** you'd be better off explicitly checking the detection result by an unit test.
**
** There are several *typical problems* to care about
** - none of these tests can detect any private members
** - none of these tests is able to detect any private members
** - the name-only detectors will fail if the name is ambiguous
** - a member can be both a variable or a function of that name
** - function signatures need to match precisely, including const modifiers
@ -115,7 +115,7 @@
* with the given name. To answer this question, instantiate
* resulting HasNested_XXX template with the type in question
* and check the static bool value field.
* @warning none of these checks can not detect private members
* @warning none of these checks can detect private members
*/
#define META_DETECT_NESTED(_TYPE_) \
template<typename TY> \
@ -138,7 +138,7 @@
* the presence of a member with the given name within
* a type in question.
* @note this check will likely fail if the name is ambiguous.
* @warning none of these checks can not detect private members
* @warning none of these checks can detect private members
*/
#define META_DETECT_MEMBER(_NAME_) \
template<typename TY> \

View file

@ -142,19 +142,9 @@ namespace interact {
{ // »Selector« : require object-like sub scope
return spec.data.isNested();
})
.matchElement ([&](GenNode const& spec, TimelineGui const& elm) -> bool
{ // »Matcher« : how to know we're dealing with the right timeline object
return spec.idi == elm.getID();
})
.constructFrom ([&](GenNode const& spec) -> TimelineGui
{ // »Constructor« : what to do when the diff mentions a new entity
return injectTimeline (spec);
})
.buildChildMutator ([&](TimelineGui& targetTimeline, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool
{ // »Mutator« : how to apply the diff recursively to a nested scope
if (subID != targetTimeline.getID()) return false;
targetTimeline.buildMutator (buff); // - delegate to child(Timeline) to build nested TreeMutator
return true;
}))
.mutateAttrib(ATTR_fork, [&](TreeMutator::Handle buff)
{ // »Attribute Mutator« : how enter an object field as nested scope

View file

@ -74,8 +74,9 @@ namespace panel {
namespace {
bool
isEmptyTimeline (auto& pages)
template<class P>
inline bool
isEmptyTimeline (P const& pages)
{
return 1 == pages.size()
and dynamic_cast<TimelineWidgetEmpty*> (pages[0].get());

View file

@ -103,62 +103,32 @@ namespace timeline {
{ // »Selector« : require object-like sub scope with type-field "Marker"
return TYPE_Marker == spec.data.recordType();
})
.matchElement ([&](GenNode const& spec, PMarker const& elm) -> bool
{
return spec.idi == elm->getID();
})
.constructFrom ([&](GenNode const& spec) -> PMarker
{
return make_unique<MarkerWidget> (spec.idi, this->uiBus_);
})
.buildChildMutator ([&](PMarker& target, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool
{
if (subID != target->getID()) return false;
target->buildMutator (buff);
return true;
}))
.attach (collection(effects_)
.isApplicableIf ([&](GenNode const& spec) -> bool
{ // »Selector« : require object-like sub scope with type-field "Effect"
return TYPE_Effect == spec.data.recordType();
})
.matchElement ([&](GenNode const& spec, PEffect const& elm) -> bool
{
return spec.idi == elm->getID();
})
.constructFrom ([&](GenNode const& spec) -> PEffect
{
std::optional<TimeSpan> timing = spec.retrieveAttribute<TimeSpan> (string{ATTR_timing});
return make_unique<ClipPresenter> (spec.idi, this->uiBus_
,getClipContentCanvas()
,timing);
})
.buildChildMutator ([&](PEffect& target, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool
{
if (subID != target->getID()) return false;
target->buildMutator (buff);
return true;
}))
.attach (collection(channels_)
.isApplicableIf ([&](GenNode const& spec) -> bool
{ // »Selector« : require object-like sub scope with type-field "Channel"
return TYPE_Channel == spec.data.recordType();
})
.matchElement ([&](GenNode const& spec, PChannel const& elm) -> bool
{
return spec.idi == elm->getID();
})
.constructFrom ([&](GenNode const& spec) -> PChannel
{
return make_unique<ClipPresenter> (spec.idi, this->uiBus_
,getClipContentCanvas()
,std::nullopt); /////////////////////////TICKET #1213 : time → horizontal extension : how to represent "always" / "the whole track"??
})
.buildChildMutator ([&](PChannel& target, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool
{
if (subID != target->getID()) return false;
target->buildMutator (buff);
return true;
}))
//-Diff-Change-Listener----------------
.onLocalChange ([this]()

View file

@ -143,6 +143,10 @@ namespace timeline {
* is simply achieved by relying on the common interface of all those "elements", which
* is stage::model::Tangible and just happens to require each such "tangible" to offer
* a mutation building method, just like this one here. Plain recursive programming.
* @remark in fact the `.buildChildMutator` binding could have been omitted here,
* since it is precisely the default, which will be provided for a DiffMutable
* target object automatically (the TimelineController is a stage::model::Tangible,
* and thus implements the DiffMutable interface, with abstract #buildMutator method.
* @see DiffComplexApplication_test
*/
void
@ -158,18 +162,13 @@ namespace timeline {
{ // »Selector« : require object-like sub scope
return spec.data.isNested();
})
.matchElement ([&](GenNode const& spec, PMarker const& elm) -> bool
{ // »Matcher« : how to know we're dealing with the right object
return spec.idi == elm->getID();
})
.constructFrom ([&](GenNode const& spec) -> PMarker
{ // »Constructor« : what to do when the diff mentions a new entity
return make_unique<MarkerWidget>(spec.idi, this->uiBus_);
})
.buildChildMutator ([&](PMarker& target, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool
.buildChildMutator ([&](PMarker& target, GenNode::ID const&, TreeMutator::Handle buff) -> bool
{ // »Mutator« : how to apply the diff recursively to a nested scope
if (subID != target->getID()) return false;// - require match on already existing child object
target->buildMutator (buff); // - delegate to child to build nested TreeMutator
target->buildMutator (buff); // : delegate to child for building a nested TreeMutator
return true;
}))
.mutateAttrib(rootForkID, [&](TreeMutator::Handle buff)

View file

@ -270,77 +270,37 @@ namespace timeline {
{ // »Selector« : require object-like sub scope with type-field "Ruler"
return TYPE_Ruler == spec.data.recordType();
})
.matchElement ([&](GenNode const& spec, PRuler const& elm) -> bool
{
return spec.idi == elm->getID();
})
.constructFrom ([&](GenNode const& spec) -> PRuler
{ // »Constructor« : how to attach a new ruler track
return make_unique<RulerTrack> (spec.idi, this->uiBus_, *this);
})
.buildChildMutator ([&](PRuler& target, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool
{
if (subID != target->getID()) return false;
target->buildMutator (buff);
return true;
}))
.attach (collection(markers_)
.isApplicableIf ([&](GenNode const& spec) -> bool
{ // »Selector« : require object-like sub scope with type-field "Marker"
return TYPE_Marker == spec.data.recordType();
})
.matchElement ([&](GenNode const& spec, PMark const& elm) -> bool
{
return spec.idi == elm->getID();
})
.constructFrom ([&](GenNode const& spec) -> PMark
{
return make_unique<MarkerWidget> (spec.idi, this->uiBus_);
})
.buildChildMutator ([&](PMark& target, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool
{
if (subID != target->getID()) return false;
target->buildMutator (buff);
return true;
}))
.attach (collection(clips_)
.isApplicableIf ([&](GenNode const& spec) -> bool
{ // »Selector« : require object-like sub scope with type-field "Clip"
return TYPE_Clip == spec.data.recordType();
})
.matchElement ([&](GenNode const& spec, PClip const& elm) -> bool
{
return spec.idi == elm->getID();
})
.constructFrom ([&](GenNode const& spec) -> PClip
{
std::optional<TimeSpan> timing = spec.retrieveAttribute<TimeSpan> (string{ATTR_timing});
return make_unique<ClipPresenter> (spec.idi, this->uiBus_, display_.getClipHook(), timing);
})
.buildChildMutator ([&](PClip& target, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool
{
if (subID != target->getID()) return false;
target->buildMutator (buff);
return true;
}))
.attach (collection(subFork_)
.isApplicableIf ([&](GenNode const& spec) -> bool
{ // »Selector« : require object-like sub scope with type-field "Fork"
return TYPE_Fork == spec.data.recordType();
})
.matchElement ([&](GenNode const& spec, PFork const& elm) -> bool
{
return spec.idi == elm->getID();
})
.constructFrom ([&](GenNode const& spec) -> PFork
{
return make_unique<TrackPresenter> (spec.idi, uiBus_, this->display_);
})
.buildChildMutator ([&](PFork& target, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool
{
if (subID != target->getID()) return false;
target->buildMutator (buff);
return true;
}))
.change(ATTR_name, [&](string val)
{ // »Attribute Setter« : receive a new value for the track name field

View file

@ -210,15 +210,15 @@ namespace test{
})
.matchElement ([&](GenNode const& spec, string const& elm) -> bool
{
return elm == render(spec.data);
return elm == render(spec.data); // »Matcher« : does the diff verb #spec apply to this object?
})
.constructFrom ([&](GenNode const& spec) -> string
{
return render (spec.data);
return render (spec.data); // »Constructor« : build a new child entity to reflect the given diff #spec
})
.assignElement ([&](string& target, GenNode const& spec) -> bool
{
target = render (spec.data);
target = render (spec.data); // »Assigner« : treat this object as value and assign data from the #spec payload
return true;
}))
.attach (collection(nestedObj_)
@ -234,10 +234,9 @@ namespace test{
{
return Opaque{spec.idi};
})
.buildChildMutator ([&](Opaque& target, GenNode::ID const& subID, TreeMutator::Handle buff) -> bool
.buildChildMutator ([&](Opaque& target, GenNode::ID const&, TreeMutator::Handle buff) -> bool
{
if (target.key_ != subID) return false; // require match on already existing child object
target.buildMutator (buff); // delegate to child to build nested TreeMutator
target.buildMutator (buff); // »Recursive Mutator« : delegate to child for building a nested TreeMutator
return true;
}))
.change("type", [&](string typeID)

View file

@ -525,7 +525,7 @@ namespace test{
// This time we build the Mutator bindings in a way to allow mutation
// For one, "mutation" means to assign a changed value to a simple node / attribute.
// And beyond that, mutation entails to open a nested scope and delve into that recursively.
// Here, as this is really just a test and demonstration, we implement those nested scopes aside
// Here, as this is really just a test and demonstration, we implement those nested scopes aside,
// managed within a map and keyed by the sub node's ID.
auto mutator3 =
TreeMutator::build()

View file

@ -37898,15 +37898,19 @@
<icon BUILTIN="yes"/>
</node>
<node CREATED="1611218788908" ID="ID_1503520124" MODIFIED="1611218900241" TEXT="der Matcher pr&#xfc;ft die Objekt-ID">
<arrowlink COLOR="#4a72bc" DESTINATION="ID_1274607629" ENDARROW="Default" ENDINCLINATION="366;0;" ID="Arrow_ID_289559805" STARTARROW="None" STARTINCLINATION="155;13;"/>
<icon BUILTIN="yes"/>
</node>
<node CREATED="1611218813169" ID="ID_1174818531" MODIFIED="1611218900241" TEXT="der Konstruktor enth&#xe4;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&#xfc;r den rekursiven Kind-Mutator">
<node COLOR="#435e98" CREATED="1611218846053" ID="ID_1626706964" MODIFIED="1611402381612" TEXT="Standard-Implementierung f&#xfc;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 COLOR="#435e98" CREATED="1611402402991" ID="ID_1274607629" MODIFIED="1611402440457" TEXT="Standard-Implementierung f&#xfc;r Matcher auf Objekt-ID">
<linktarget COLOR="#4a72bc" DESTINATION="ID_1274607629" ENDARROW="Default" ENDINCLINATION="366;0;" ID="Arrow_ID_289559805" SOURCE="ID_1503520124" STARTARROW="None" STARTINCLINATION="155;13;"/>
<icon BUILTIN="yes"/>
</node>
</node>
</node>
@ -39575,7 +39579,7 @@
<node CREATED="1576202990623" ID="ID_1903460265" MODIFIED="1576203228840" TEXT="konkret entwickelt f&#xfc;r das UI-Element">
<arrowlink COLOR="#298c9e" DESTINATION="ID_645743424" ENDARROW="Default" ENDINCLINATION="-928;-33;" ID="Arrow_ID_653906070" STARTARROW="None" STARTINCLINATION="-2105;61;"/>
</node>
<node COLOR="#338800" CREATED="1456506101544" FOLDED="true" HGAP="322" ID="ID_61713493" MODIFIED="1576281717228" TEXT="Implementierung: Diff-Anbindung" VSHIFT="87">
<node COLOR="#338800" CREATED="1456506101544" FOLDED="true" HGAP="322" ID="ID_61713493" MODIFIED="1611315117066" TEXT="Implementierung: Diff-Anbindung" VSHIFT="87">
<linktarget COLOR="#47e573" DESTINATION="ID_61713493" ENDARROW="Default" ENDINCLINATION="-3400;234;" ID="Arrow_ID_1763921751" SOURCE="ID_486184645" STARTARROW="None" STARTINCLINATION="-792;48;"/>
<linktarget COLOR="#2ab880" DESTINATION="ID_61713493" ENDARROW="Default" ENDINCLINATION="-125;-80;" ID="Arrow_ID_958576106" SOURCE="ID_464295846" STARTARROW="None" STARTINCLINATION="-317;9;"/>
<icon BUILTIN="button_ok"/>
@ -42973,7 +42977,7 @@
<node CREATED="1472122015157" ID="ID_507093208" MODIFIED="1472122038334" TEXT="sinnvolle interne Fehler-Funktionen aufbauen"/>
<node CREATED="1472141026525" ID="ID_215668060" MODIFIED="1512926192127" TEXT="Scope-Wechsel">
<icon BUILTIN="button_ok"/>
<node CREATED="1472172154624" ID="ID_916630849" MODIFIED="1512926192127" TEXT="Zugang zum erzeugten Mutator....">
<node CREATED="1472172154624" FOLDED="true" ID="ID_916630849" MODIFIED="1512926192127" TEXT="Zugang zum erzeugten Mutator....">
<node CREATED="1472172310187" ID="ID_487326937" MODIFIED="1575133339222" TEXT="via buffer-Handle">
<richcontent TYPE="NOTE"><html>
<head>
@ -43074,9 +43078,9 @@
<icon BUILTIN="forward"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1611218339864" ID="ID_341242193" MODIFIED="1611314480869" TEXT="Abk&#xfc;rzung f&#xfc;r rekursives Standard-Binding">
<node COLOR="#338800" CREATED="1611218339864" FOLDED="true" ID="ID_341242193" MODIFIED="1611402354322" TEXT="Abk&#xfc;rzung f&#xfc;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"/>
<icon BUILTIN="button_ok"/>
<node CREATED="1611227233973" ID="ID_489519336" MODIFIED="1611227312132" TEXT="f&#xfc;r den h&#xe4;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"/>
@ -43085,11 +43089,13 @@
<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 COLOR="#338800" CREATED="1611261538911" ID="ID_498748918" MODIFIED="1611402319966" TEXT="Standard-Imlementierung">
<icon BUILTIN="button_ok"/>
<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 COLOR="#435e98" CREATED="1611261556183" ID="ID_236586609" MODIFIED="1611402310840" TEXT="Frage: warum machen wir einen ID-Match?">
<icon BUILTIN="help"/>
<node CREATED="1611261593546" ID="ID_1666826303" MODIFIED="1611261791971" TEXT="typischerweise pr&#xfc;fen wir zu Beginn, ob das zu mutierende Sub-Element auch matcht"/>
<node CREATED="1611261792751" ID="ID_1188415737" MODIFIED="1611261815910" TEXT="man k&#xf6;nnte hierf&#xfc;r den &#xbb;Matcher&#xab; des Collection-Bindings verwenden"/>
<node CREATED="1611261840272" ID="ID_922368010" MODIFIED="1611261871424" TEXT="mehr noch: wie kommt es zu diesem Match-Guard?">
@ -43153,6 +43159,58 @@
</node>
</node>
</node>
<node COLOR="#338800" CREATED="1611402003818" ID="ID_1246335581" MODIFIED="1611402016011" TEXT="den ID-Matcher ebenfalls generieren">
<icon BUILTIN="button_ok"/>
<node CREATED="1611402016970" ID="ID_1067306968" MODIFIED="1611402027948" TEXT="wir brauchen zwar keinen ID-Match beim Erzeugen des Mutators"/>
<node CREATED="1611402028489" ID="ID_809714103" MODIFIED="1611402068427" TEXT="aber der ID-basierte Matcher ist ebenfalls generisch, und sollte automatisch generiert werden">
<icon BUILTIN="yes"/>
</node>
<node COLOR="#338800" CREATED="1611402077673" ID="ID_361760144" MODIFIED="1611402210308" TEXT="Problem: Zugang zur Target-Objekt-ID">
<icon BUILTIN="button_ok"/>
<node CREATED="1611402092625" ID="ID_19089272" MODIFIED="1611402150879" TEXT="k&#xf6;nnte man in DiffMutable-Interface aufnehmen">
<icon BUILTIN="button_cancel"/>
<node CREATED="1611402121148" ID="ID_1241594472" MODIFIED="1611402129847" TEXT="Vorsicht: DiffMutable ist opitonal"/>
<node CREATED="1611402130459" ID="ID_899835141" MODIFIED="1611402146172" TEXT="man m&#xfc;&#xdf;te dann trotzdem noch Duck-Typing verwenden"/>
</node>
<node CREATED="1611402151832" ID="ID_1518275784" MODIFIED="1611402160776" TEXT="Duck-Typing verwenden">
<icon BUILTIN="forward"/>
<node CREATED="1611402162870" ID="ID_697592973" MODIFIED="1611402180642">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p>
erwarte eine Funktion <b>getID()</b>
</p>
</body>
</html>
</richcontent>
</node>
<node CREATED="1611402182252" ID="ID_12989992" MODIFIED="1611402197061" TEXT="und deren Resultat mu&#xdf; per == vergleichbar sein"/>
<node CREATED="1611402199145" ID="ID_317509193" MODIFIED="1611402204788" TEXT="...mit einer GenNode::ID"/>
</node>
<node CREATED="1611402212151" ID="ID_543092941" MODIFIED="1611402232768" TEXT="wenn dies gegeben ist, dann kann man auch den &#xbb;Matcher&#xab; generieren">
<node CREATED="1611402234140" ID="ID_1471601030" MODIFIED="1611402244652" TEXT="f&#xfc;r stage::model::Tangible stets gegeben">
<icon BUILTIN="idea"/>
</node>
<node CREATED="1611402245626" ID="ID_1721371942" MODIFIED="1611402285886" TEXT="sonst: Exception werfen">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
...d.h. man mu&#223; dann halt doch noch den Matcher explizit in der DSL konfigurieren
</p>
</body>
</html>
</richcontent>
<icon BUILTIN="yes"/>
</node>
</node>
</node>
</node>
</node>
</node>
</node>