Scheduler: solve difficulties with member function signature

The approach to provide the ExecutionCtx seems to work out well;
after some investigation I found a solution how to code a generic
signature-check for "any kind of function-like member"...

(the trick is to pass a pointer or member-pointer, which happens
to be syntactically the same and can be handled with our existing
function signature helper after some minor tweaks)
This commit is contained in:
Fischlurch 2023-10-21 23:42:31 +02:00
parent 0d2d8c3413
commit d67c62b02f
5 changed files with 286 additions and 115 deletions

View file

@ -46,17 +46,15 @@
// 03/20 - investigate type deduction bug with PtrDerefIter
// 01/21 - look for ways to detect the presence of an (possibly inherited) getID() function
// 08/22 - techniques to supply additional feature selectors to a constructor call
// 10/23 - search for ways to detect signatures of member functions and functors uniformly
/** @file try.cpp
* Investigate techniques to supply additional descriptive ctor arguments in a type safe way.
* The idea is to provide friend functors, which might tweak or reset internal settings;
* these functors are packaged into free standing friend functions with intuitive naming,
* which, on call-site, look like algebraic expressions/data-types.
*
* If desired, this mechanism can be mixed-in and integrated into a constructor call,
* thus optionally allowing for arbitrary extra qualifiers, even with extra arguments.
* @see builder-qualifier-support.hpp (wrapped as support lib)
* Investigate how to detect the signature of a _function-like member,_ irrespective
* if referring to a static function, a member function or a functor member. Turns out this
* can be achieved in a syntactically uniform way by passing either a pointer or member pointer.
* @see vault::gear::_verify_usable_as_ExecutionContext
* @see lib::meta::isFunMember
*/
typedef unsigned int uint;
@ -67,100 +65,101 @@ typedef unsigned int uint;
#include "lib/test/diagnostic-output.hpp"
#include "lib/util.hpp"
#include <functional>
#include "lib/meta/function.hpp"
template<class TAR>
class PropertyQualifierSupport
struct Stat
{
protected:
using Manipulator = std::function<void(TAR&)>;
struct Qualifier
: Manipulator
{
using Manipulator::Manipulator;
};
static long fun (double, char*) {return 42; }
};
template<class... QUALS>
friend void qualify(TAR& target, Qualifier& qualifier, QUALS& ...qs)
struct Funi
{
std::function<long(double, char*)> fun;
short gun;
};
struct Dyna
{
long fun (double, char*) const {return 42; }
};
using lib::meta::_Fun;
/** @deprecated this is effectively the same than using decltype */
template<typename P>
struct Probe
: _Fun<P>
{
qualifier(target);
qualify(target, qs...);
}
friend void qualify(TAR&){ }
Probe(P&&){}
};
public:
// default construct and copyable
};
template<typename FUN, typename SIG, bool =_Fun<FUN>()>
struct has_SIGx
: std::is_same<SIG, typename _Fun<FUN>::Sig>
{
// has_SIGx() = default;
// has_SIGx(FUN, _Fun<SIG>){ }
};
class Feat
: PropertyQualifierSupport<Feat>
template<typename FUN, typename X>
struct has_SIGx<FUN,X,false>
: std::false_type
{
// has_SIGx() = default;
// has_SIGx(FUN, _Fun<X>){ }
};
template<typename SIG, typename FUN>
constexpr inline auto
isFunMember (FUN)
{
friend Qualifier bla();
friend Qualifier blubb(string);
public:
Feat() = default;
template<class... QS>
Feat(Qualifier qual, QS... qs)
: Feat{}
{
qualify(*this, qual, qs...);
}
operator string () const
{
return "Feat{"+prop_+"}";
}
private:
string prop_{""};
};
Feat::Qualifier
bla()
{
return Feat::Qualifier{[](Feat& feat)
{
feat.prop_ = "bla";
}};
}
Feat::Qualifier
blubb(string murks)
{
return Feat::Qualifier{[=](Feat& feat)
{
feat.prop_ += ".blubb("+murks+")";
}};
}
return has_SIGx<FUN,SIG>{};
}
#define ARSERT_MEMBER_FUNCTOR(_EXPR_, _SIG_) \
static_assert (isFunMember<_SIG_>(_EXPR_), \
"Member " STRINGIFY(_EXPR_) " unsuitable, expect function signature: " STRINGIFY(_SIG_));
int
main (int, char**)
{
Feat f0;
SHOW_EXPR(f0);
using F1 = decltype(Stat::fun);
using F2 = decltype(Funi::fun);
using F3 = decltype(&Dyna::fun);
Feat f1(bla());
SHOW_EXPR(f1);
SHOW_TYPE(F1)
SHOW_TYPE(F2)
SHOW_TYPE(F3)
Feat f2(blubb("Ψ"));
SHOW_EXPR(f2);
using F1a = decltype(&Stat::fun);
using F2a = decltype(&Funi::fun);
using F2b = decltype(&Funi::gun);
SHOW_TYPE(F1a)
SHOW_TYPE(F2a)
SHOW_TYPE(F2b)
Feat f3(bla(),blubb(""));
SHOW_EXPR(f3);
SHOW_TYPE(_Fun<F1>::Sig)
SHOW_TYPE(_Fun<F2>::Sig)
SHOW_TYPE(_Fun<F3>::Sig)
SHOW_TYPE(_Fun<F1a>::Sig)
SHOW_TYPE(_Fun<F2a>::Sig)
Feat f4(blubb("💡"), bla()); // Note: evaluated from left to right, bla() overwrites prop
SHOW_EXPR(f4);
SHOW_EXPR(_Fun<F2a>::value)
SHOW_EXPR(_Fun<F2b>::value)
cout << "\n--------\n";
SHOW_EXPR(bool(isFunMember<long(double,char*)>(&Stat::fun)))
SHOW_EXPR(bool(isFunMember<long(double,char*)>(&Funi::fun)))
SHOW_EXPR(bool(isFunMember<long(double,char*)>(&Funi::gun)))
SHOW_EXPR(bool(isFunMember<long(double,char*)>(&Dyna::fun)))
ARSERT_MEMBER_FUNCTOR (&Stat::fun, long(double,char*));
ARSERT_MEMBER_FUNCTOR (&Dyna::fun, long(double,char*));
cout << "\n.gulp.\n";
return 0;

View file

@ -164,7 +164,7 @@
* Defines a metafunction (template), allowing to detect
* the presence of a member function with the specific
* signature, as defined by the parameters.
* @note this check is not sensible to overloads,
* @note this check is not sensitive to overloads,
* due to the explicitly given argument types
*/
#define META_DETECT_FUNCTION(_RET_TYPE_,_FUN_NAME_,_ARGS_) \

View file

@ -175,6 +175,12 @@ namespace meta{
: _Fun<RET(ARGS...)>
{ };
/** allow also to probe _plain member fields,_ which may hold a functor */
template<class C, typename FUN>
struct _Fun<FUN (C::*)>
: _Fun<FUN>
{ };
@ -187,11 +193,17 @@ namespace meta{
* or std::function instance, or λ instance or language function
* reference or function pointer
*/
template<typename FUN, typename SIG>
template<typename FUN, typename SIG, bool =_Fun<FUN>()>
struct has_Sig
: std::is_same<SIG, typename _Fun<FUN>::Sig>
{ };
/** catch-all to prevent compilation failure for anything not function-like. */
template<typename FUN, typename X>
struct has_Sig<FUN,X, false>
: std::false_type
{ };
/**
* Macro for a compile-time check to verify the given
* generic functors or lambdas expose some expected signature.
@ -203,6 +215,33 @@ namespace meta{
/**
* Helper to pick up a member field for verification
* @tparam SIG signature of the _function like_ entity expected
* @tparam FUN address- or member-pointer, e.g. `&Class::member`
* @return suitably parametrised \ref has_Sig instance (which is bool convertible)
* @remark intended for use with generic types, when expecting a _somehow invokable_
* member, irrespective if a static function, member function or functor object
*/
template<typename SIG, typename FUN>
constexpr inline auto
isFunMember (FUN)
{
return has_Sig<FUN,SIG>{};
}
/**
* Macro for a compile-time check to verify some member is present
* and comprises something invokable with a specific signature.
* @remark typically used with _generic types_ or bindings
*/
#define ASSERT_MEMBER_FUNCTOR(_EXPR_, _SIG_) \
static_assert (lib::meta::isFunMember<_SIG_>(_EXPR_), \
"Member " STRINGIFY(_EXPR_) " unsuitable, expect function signature: " STRINGIFY(_SIG_));
/** Placeholder marker for a special argument position to be supplied later */

View file

@ -182,21 +182,13 @@ namespace gear {
constexpr void
_verify_usable_as_ExecutionContext ()
{
#define ASSERT_MEMBER_FUNCTOR(_EXPR_, _SIG_) \
static_assert (lib::meta::has_Sig<decltype(_EXPR_), _SIG_>(), \
"Execution-Context: " STRINGIFY(_EXPR_) " expect function with signature: " STRINGIFY(_SIG_));
ASSERT_MEMBER_FUNCTOR (&EXE::post, Proc(Time, Activity&, EXE&));
ASSERT_MEMBER_FUNCTOR (&EXE::work, void(Time, size_t));
ASSERT_MEMBER_FUNCTOR (&EXE::done, void(Time, size_t));
ASSERT_MEMBER_FUNCTOR (&EXE::tick, Proc(Time));
ASSERT_MEMBER_FUNCTOR (EXE::post, Proc(Time, Activity&, EXE&));
ASSERT_MEMBER_FUNCTOR (EXE::work, void(Time, size_t));
ASSERT_MEMBER_FUNCTOR (EXE::done, void(Time, size_t));
ASSERT_MEMBER_FUNCTOR (EXE::tick, Proc(Time));
ASSERT_MEMBER_FUNCTOR (EXE::getWaitDelay, Offset());
ASSERT_MEMBER_FUNCTOR (EXE::getSchedTime, Time());
#undef ASSERT_MEMBER_FUNCTOR
ASSERT_MEMBER_FUNCTOR (&EXE::getWaitDelay, Offset());
ASSERT_MEMBER_FUNCTOR (&EXE::getSchedTime, Time());
}
}//(End)namespace activity
@ -604,7 +596,7 @@ namespace gear {
activity::Proc
Activity::activate (Time now, EXE& executionCtx)
{
// activity::_verify_usable_as_ExecutionContext<EXE>();
activity::_verify_usable_as_ExecutionContext<EXE>();
switch (verb_) {
case INVOKE:
@ -651,7 +643,7 @@ namespace gear {
activity::Proc
Activity::dispatch (Time now, EXE& executionCtx)
{
// activity::_verify_usable_as_ExecutionContext<EXE>();
activity::_verify_usable_as_ExecutionContext<EXE>();
switch (verb_) {
case NOTIFY:
@ -681,7 +673,7 @@ namespace gear {
activity::Proc
Activity::notify (Time now, EXE& executionCtx)
{
// activity::_verify_usable_as_ExecutionContext<EXE>();
activity::_verify_usable_as_ExecutionContext<EXE>();
switch (verb_) {
case GATE:

View file

@ -57321,6 +57321,121 @@
</node>
</node>
</node>
<node CREATED="1697906135803" ID="ID_47415467" MODIFIED="1697927623614" TEXT="Function utils">
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#435e98" CREATED="1697906141042" FOLDED="true" ID="ID_334205441" MODIFIED="1697927708161" TEXT="lib::meta::_Fun">
<font BOLD="true" NAME="SansSerif" SIZE="12"/>
<icon BUILTIN="forward"/>
<node CREATED="1697906147625" ID="ID_940224255" MODIFIED="1697906184451">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p>
damit kann man von jedem <i>&#187;Invocable&#171; </i>eine Signatur abgreifen
</p>
</body>
</html></richcontent>
</node>
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#690f14" CREATED="1697906227094" ID="ID_1918952021" LINK="https://issues.lumiera.org/ticket/994" MODIFIED="1697906274433" TEXT="inzwischen auch f&#xfc;r &#x3bb; verwendbar (siehe #994)">
<icon BUILTIN="idea"/>
</node>
<node CREATED="1697906296845" ID="ID_1333452635" MODIFIED="1697906312015" TEXT="potentielle Gefahr: generische &#x3bb;">
<node CREATED="1697906315163" ID="ID_1259843032" MODIFIED="1697906324382" TEXT="diese sind Templates"/>
<node CREATED="1697906360413" ID="ID_1391705943" MODIFIED="1697906588857" TEXT="man mu&#xdf; sie instantiieren &#x2014; mit vorgegebenen Argument-Typ(en)">
<linktarget COLOR="#ee5278" DESTINATION="ID_1391705943" ENDARROW="Default" ENDINCLINATION="161;7;" ID="Arrow_ID_1833361444" SOURCE="ID_648143154" STARTARROW="None" STARTINCLINATION="-42;-40;"/>
</node>
</node>
</node>
<node COLOR="#338800" CREATED="1697906435810" FOLDED="true" ID="ID_717292125" MODIFIED="1697927695084" TEXT="beliebige funktions-artige Member analysieren">
<linktarget COLOR="#6e85a1" DESTINATION="ID_717292125" ENDARROW="Default" ENDINCLINATION="-1818;113;" ID="Arrow_ID_979493428" SOURCE="ID_118455784" STARTARROW="None" STARTINCLINATION="-869;-95;"/>
<icon BUILTIN="button_ok"/>
<node COLOR="#338800" CREATED="1697905686767" ID="ID_1640341474" MODIFIED="1697927541329" TEXT="lib::meta::_Fun f&#xfc;r einfache Member-Pointer erg&#xe4;nzen">
<icon BUILTIN="button_ok"/>
<node CREATED="1697905716970" ID="ID_1853665060" MODIFIED="1697905729917" TEXT="unterst&#xfc;tzt bisher nur Function-Member-Pointer"/>
<node CREATED="1697905731145" ID="ID_1467529623" MODIFIED="1697906017054" TEXT="die Erweiterung erscheint mir relativ ungef&#xe4;hrlich">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
...denn man verwendet diese Metafunktion ja stets explizit mit einem gegebenen Typ, und im Falle eines Ausdrucks mu&#223; man noch einen decltype() darum wickeln. M&#246;gliche Probleme:
</p>
<ul>
<li>
der Ausdruck in decltype()&#160;ist syntaktisch gar nicht valide (z.B. Scope::member&#160;&#160;bei einer Member-Funktion)
</li>
<li>
der sich ergebende Typ ist keine Funktion, und deshalb sind die nested-typedefs (Sig, Args...) nicht vorhanden &#10233; compile Fehler bzw. SFINAE
</li>
</ul>
</body>
</html></richcontent>
<node CREATED="1697906024514" ID="ID_1643639913" MODIFIED="1697906047450" TEXT="m&#xf6;gliche Gefahr: generische &#x3bb;"/>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1697906053470" ID="ID_648143154" MODIFIED="1697906588857" TEXT="wenn man diese per negativ-Check zu erkennen versucht">
<arrowlink COLOR="#ee5278" DESTINATION="ID_1391705943" ENDARROW="Default" ENDINCLINATION="161;7;" ID="Arrow_ID_1833361444" STARTARROW="None" STARTINCLINATION="-42;-40;"/>
<icon BUILTIN="clanbomber"/>
</node>
<node CREATED="1697906594973" ID="ID_1834320957" MODIFIED="1697906613333" TEXT="lib::IterExplorer macht das">
<icon BUILTIN="info"/>
<node CREATED="1697906663564" ID="ID_1606991462" MODIFIED="1697906676958" TEXT="aber an einer Stelle, an der man einen Funktor &#xfc;bergibt"/>
<node CREATED="1697906678426" ID="ID_1127757601" MODIFIED="1697906784367" TEXT="ein blanken Member-Pointer macht an dieser Stelle &#xfc;berhaupt keinen Sinn">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
Beispiel: explore(elements).transform(....irgendwas....)
</p>
</body>
</html></richcontent>
</node>
<node BACKGROUND_COLOR="#d2beaf" COLOR="#5c4d6e" CREATED="1697906733939" ID="ID_798052808" MODIFIED="1697906747346" TEXT="m&#xf6;glicherweise ist die Fehlermeldung dann aber verwirrend">
<icon BUILTIN="hourglass"/>
</node>
</node>
</node>
<node CREATED="1697905742823" ID="ID_1985279591" MODIFIED="1697905792612" TEXT="es gibt ja immer noch den bool-check (&#x27fc; ist eine Funktion)"/>
</node>
<node COLOR="#338800" CREATED="1697906799162" ID="ID_1510624814" MODIFIED="1697927539161" TEXT="Hilfsmittel f&#xfc;r einfache Signatur-Checks">
<icon BUILTIN="button_ok"/>
<node CREATED="1697925677041" ID="ID_1103856898" MODIFIED="1697925691803" TEXT="has_Sig w&#xe4;re eigentlich bereits hinreichend">
<node CREATED="1697925743952" ID="ID_949441284" MODIFIED="1697925765736" TEXT="man k&#xf6;nnte einen Konstruktor verwenden und template argument deduction"/>
<node CREATED="1697925766453" ID="ID_503135543" MODIFIED="1697925790856" TEXT="weiteres Problem: wenn das &#xfc;bergebene Objekt &#xfc;berhaupt keine Funktion ist...."/>
</node>
<node CREATED="1697925692678" ID="ID_1669099345" MODIFIED="1697925712517" TEXT="aber die Reihenfolge der Template-Argumente ist genau falsch herum"/>
<node CREATED="1697925713308" ID="ID_770980099" MODIFIED="1697925732217" TEXT="dann besser eine free-Function">
<icon BUILTIN="yes"/>
</node>
<node CREATED="1697925795562" ID="ID_340665610" MODIFIED="1697925947597" TEXT="und catch-all-Specialisation">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
has_Sig sollte keine Compile-Fehler ausl&#246;sen, wenn der gegebene Typ &#252;berhaupt nicht Funktions-artig ist (&#10233; denn dann ist die Aussage trivialer Weise wiederlegt; was gar keine Signatur hat, kann auch nicht eine bestimmte Signatur haben). Realisieren kann man das &#252;ber den bool-Check, den ich vor einiger Zeit bereits in lib::meta::_Fun eingebaut habe (im Zusammenhang mit IterExplorer)
</p>
</body>
</html></richcontent>
</node>
<node CREATED="1697925949821" ID="ID_1361817287" MODIFIED="1697926000759" TEXT="dann auch gleich noch ein spezielles ASSERT-Macro bereitstellen">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
Warum Macro? damit man per STRINGIFY() einen lesbareren Hinweis in die Static-Assertion bekommt
</p>
</body>
</html></richcontent>
</node>
</node>
</node>
</node>
</node>
</node>
<node CREATED="1573230375839" ID="ID_270568302" MODIFIED="1573230382276" TEXT="policies">
@ -80285,7 +80400,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<node COLOR="#338800" CREATED="1690490297125" ID="ID_1766901032" MODIFIED="1690490313350" TEXT="Vorgriff auf Concepts: Signaturen statisch pr&#xfc;fen">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1697849524591" ID="ID_747589769" MODIFIED="1697849564995" TEXT="statische Pr&#xfc;fung schwierig">
<node COLOR="#435e98" CREATED="1697849524591" FOLDED="true" ID="ID_747589769" MODIFIED="1697927669544" TEXT="statische Pr&#xfc;fung schwierig">
<icon BUILTIN="messagebox_warning"/>
<node CREATED="1697849570913" ID="ID_155562107" MODIFIED="1697849610947" TEXT="ging gut solange ich stets Functor-Objekte verwendet hatte">
<richcontent TYPE="NOTE"><html>
@ -80297,9 +80412,39 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
...also solange ich nur mit Unit-Tests gearbeitet habe
</p>
</body>
</html></richcontent>
</node>
<node CREATED="1697901649263" ID="ID_1005179743" MODIFIED="1697902145753" TEXT="der Zugriff auf den Typ h&#xe4;ngt von der Art des Members ab">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<ul>
<li>
von einer statischen Funktion kan man direkt den Typ aufgreifen, qualifiziert durch den Scope: <font face="Monospaced" color="#1509b8">decltype( Scope::fun )</font>
</li>
<li>
von einem regul&#228;ren Member-Function kann man den Typ nach einem Pseudo-Zugriff erfassen: <font face="Monospaced" color="#1509b8">decltype( std::declval&lt;Scope&gt;().fun )</font>
</li>
<li>
bei einem Functor-Member geht das nicht, aber man kann hier entweder statisch qualifzieren (wie 1.Fall), oder einen Pointer nehmen: <font face="Monospaced" color="#1509b8">decltype( &amp;Scope::fun )</font>
</li>
</ul>
</body>
</html>
</richcontent>
</node>
<node CREATED="1697902156764" ID="ID_1064629278" MODIFIED="1697902191903" TEXT="die Syntax Address-of-scope-qualfied-Member w&#xe4;re f&#xfc;r alle drei F&#xe4;lle valide">
<icon BUILTIN="idea"/>
</node>
<node CREATED="1697902217469" ID="ID_1785157426" MODIFIED="1697902255305" TEXT="...liefert aber in jedem Fall einen anders zu behandelnden Typ-Ausdruck">
<icon BUILTIN="messagebox_warning"/>
</node>
<node COLOR="#338800" CREATED="1697905677360" ID="ID_118455784" MODIFIED="1697927641347" TEXT="Hilfsmittel ausbauen...">
<arrowlink COLOR="#6e85a1" DESTINATION="ID_717292125" ENDARROW="Default" ENDINCLINATION="-1818;113;" ID="Arrow_ID_979493428" STARTARROW="None" STARTINCLINATION="-869;-95;"/>
<icon BUILTIN="button_ok"/>
</node>
</node>
</node>
<node COLOR="#338800" CREATED="1690068793535" ID="ID_1906823317" MODIFIED="1697844595733" TEXT="ist tats&#xe4;chlich implementiert durch den Scheduler selber">
@ -80320,8 +80465,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
der Scheduler-Service <i>&#252;berhaupt</i>
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<richcontent TYPE="NOTE"><html>
<head>
@ -80331,8 +80475,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
...n&#228;mlich die F&#228;higkeit, einen Activity-chain zu einem geplanten Zeitpunkt oder auf Signal hin auszuf&#252;hren &#8212; und diese F&#228;higkeit mu&#223; selbstverst&#228;ndlich der Sprache selber zu Gebote stehen, damit sie komplexe Aktionsmuster flexibel ausdr&#252;cken kann
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
</node>
<node COLOR="#338800" CREATED="1692286145852" ID="ID_596906210" MODIFIED="1692370174170" TEXT="Fake-Setup f&#xfc;r Tests (ActivityDetector)">
@ -81876,8 +82019,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
es gen&#252;gte, an diesen Stellen die Ausf&#252;hrung der abstrahierten Aktionen zu loggen
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1697844649091" ID="ID_939726274" MODIFIED="1697844696080" TEXT="als Abstraktion wurde (bewu&#xdf;t) ein Template-Parameter gew&#xe4;hlt"/>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1697844697092" ID="ID_766922475" MODIFIED="1697845768002" TEXT="kokretes Funktions-Binding">
@ -81896,8 +82038,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
&#10233; <i>der Scheduler selber</i>&#160;kann diese Rolle <b>generisch</b>&#160;&#252;bernehmen
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1697845608675" ID="ID_610860781" MODIFIED="1697845760326" TEXT="noch sauberer: ein Sub-Interface-Mapping">
<richcontent TYPE="NOTE"><html>