diff --git a/research/try.cpp b/research/try.cpp index 15c95d059..0f26bf401 100644 --- a/research/try.cpp +++ b/research/try.cpp @@ -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 - 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 #include -#include -#include -#include 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_ <; -using Strs = std::vector; -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 + class Can_retrieve_and_compare_ID + { + template() == std::declval().getID())> + struct Probe + { }; \ + \ + template \ + static Yes_t check(Probe * ); \ + template \ + static No_t check(...); \ + \ + public: \ + static const bool value = (sizeof(Yes_t)==sizeof(check(0))); \ + }; + + +class Base + { + EntryID 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::value ); + SHOW_EXPR( HasArglessFun_getID::value ); + SHOW_EXPR( Can_retrieve_and_compare_ID::value ); -// using ITSR = typename ITS::reference; -// lib::test::TypeDebugger buggy; + SHOW_EXPR( d1 ); + SHOW_EXPR( d1.getID() ); + SHOW_EXPR( HasFunSig_getID::value ); + SHOW_EXPR( HasArglessFun_getID::value ); + SHOW_EXPR( Can_retrieve_and_compare_ID::value ); - auto dings = elems(ss); + SHOW_EXPR( r1 ); + SHOW_EXPR( HasFunSig_getID::value ); + SHOW_EXPR( HasArglessFun_getID::value ); + SHOW_EXPR( Can_retrieve_and_compare_ID::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; diff --git a/src/lib/diff/diff-mutable.hpp b/src/lib/diff/diff-mutable.hpp index f6a05a9c5..d01c13794 100644 --- a/src/lib/diff/diff-mutable.hpp +++ b/src/lib/diff/diff-mutable.hpp @@ -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*/ diff --git a/src/lib/diff/tree-mutator-collection-binding.hpp b/src/lib/diff/tree-mutator-collection-binding.hpp index 9520bbce4..ea4f41458 100644 --- a/src/lib/diff/tree-mutator-collection-binding.hpp +++ b/src/lib/diff/tree-mutator-collection-binding.hpp @@ -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_; diff --git a/src/lib/diff/tree-mutator-diffmutable-binding.hpp b/src/lib/diff/tree-mutator-diffmutable-binding.hpp index 754888dad..f0e799e52 100644 --- a/src/lib/diff/tree-mutator-diffmutable-binding.hpp +++ b/src/lib/diff/tree-mutator-diffmutable-binding.hpp @@ -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 + class Can_retrieve_and_compare_ID + { + template() == std::declval().getID())> + struct Probe + { }; + + template + static Yes_t check(Probe * ); + template + static No_t check(...); + + public: + static const bool value = (sizeof(Yes_t)==sizeof(check(0))); + }; template struct Can_access_ID - : HasFunSig_getID::Type> + : Can_retrieve_and_compare_ID::Type> { }; template - struct Is_DiffMutable + struct Is_DiffMutable ///< Metafunction: does the target implement the DiffMutable interface? : meta::is_Subclass { }; template - struct Is_wrapped_DiffMutable + struct Is_wrapped_DiffMutable ///< Metafunction: is this a DiffMutable wrapped into a smart-ptr? : __and_< meta::Unwrap , Is_DiffMutable::Type>> { }; @@ -98,22 +119,26 @@ namespace diff{ : __or_< Is_DiffMutable , Is_wrapped_DiffMutable> { } ; - + + /** + * Metaprogramming helper to retrieve the object identity, whenever + * the target object for the diff defines a `getID()` function. + */ template 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 struct _AccessID>> { - 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 struct _DefaultBinding>> { @@ -145,7 +178,7 @@ namespace diff{ }; - + }//(END)Mutator-Builder decorator components... diff --git a/src/lib/diff/tree-mutator.hpp b/src/lib/diff/tree-mutator.hpp index b6b994797..d6cd86a23 100644 --- a/src/lib/diff/tree-mutator.hpp +++ b/src/lib/diff/tree-mutator.hpp @@ -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" diff --git a/src/lib/meta/duck-detector.hpp b/src/lib/meta/duck-detector.hpp index 8b9157be1..9ce701f4e 100644 --- a/src/lib/meta/duck-detector.hpp +++ b/src/lib/meta/duck-detector.hpp @@ -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 \ @@ -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 \ diff --git a/src/stage/interact/interaction-director.cpp b/src/stage/interact/interaction-director.cpp index 742601cba..5dd03dde8 100644 --- a/src/stage/interact/interaction-director.cpp +++ b/src/stage/interact/interaction-director.cpp @@ -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 diff --git a/src/stage/panel/timeline-panel.cpp b/src/stage/panel/timeline-panel.cpp index 0dd160bb3..cc7c1be03 100644 --- a/src/stage/panel/timeline-panel.cpp +++ b/src/stage/panel/timeline-panel.cpp @@ -74,8 +74,9 @@ namespace panel { namespace { - bool - isEmptyTimeline (auto& pages) + template + inline bool + isEmptyTimeline (P const& pages) { return 1 == pages.size() and dynamic_cast (pages[0].get()); diff --git a/src/stage/timeline/clip-presenter.cpp b/src/stage/timeline/clip-presenter.cpp index 9099bd98a..587731f9c 100644 --- a/src/stage/timeline/clip-presenter.cpp +++ b/src/stage/timeline/clip-presenter.cpp @@ -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 (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 timing = spec.retrieveAttribute (string{ATTR_timing}); return make_unique (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 (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]() diff --git a/src/stage/timeline/timeline-controller.cpp b/src/stage/timeline/timeline-controller.cpp index 013550ad5..beebaf72f 100644 --- a/src/stage/timeline/timeline-controller.cpp +++ b/src/stage/timeline/timeline-controller.cpp @@ -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(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) diff --git a/src/stage/timeline/track-presenter.hpp b/src/stage/timeline/track-presenter.hpp index 10dfaa151..9021e5e9b 100644 --- a/src/stage/timeline/track-presenter.hpp +++ b/src/stage/timeline/track-presenter.hpp @@ -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 (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 (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 timing = spec.retrieveAttribute (string{ATTR_timing}); return make_unique (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 (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 diff --git a/tests/library/diff/diff-complex-application-test.cpp b/tests/library/diff/diff-complex-application-test.cpp index d3db25756..1da758b19 100644 --- a/tests/library/diff/diff-complex-application-test.cpp +++ b/tests/library/diff/diff-complex-application-test.cpp @@ -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) diff --git a/tests/library/diff/tree-mutator-binding-test.cpp b/tests/library/diff/tree-mutator-binding-test.cpp index 27b878a4b..044715634 100644 --- a/tests/library/diff/tree-mutator-binding-test.cpp +++ b/tests/library/diff/tree-mutator-binding-test.cpp @@ -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() diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index f14cab1d1..cafa806eb 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -37898,15 +37898,19 @@ + - + - + + + + @@ -39575,7 +39579,7 @@ - + @@ -42973,7 +42977,7 @@ - + @@ -43074,9 +43078,9 @@ - + - + @@ -43085,11 +43089,13 @@ - + + - + + @@ -43153,6 +43159,58 @@ + + + + + + + + + + + + + + + + + + + + + +

+ erwarte eine Funktion getID() +

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

+ ...d.h. man muß dann halt doch noch den Matcher explizit in der DSL konfigurieren +

+ + +
+ +
+
+
+