Library: use as a foundation for the branch-combinator

After all the preparation, now this panes out quite well:
 * use a simple 3-way branch structure
 * the model type was already pre-selected by the `_Join` Model selector
 * can just pass the result-model elements to a constructor/builder
 * incremental extension can be directly mapped to the predecessor model
This commit is contained in:
Fischlurch 2025-01-22 01:11:05 +01:00
parent a8231150a5
commit e3fe8fe380
5 changed files with 190 additions and 470 deletions

View file

@ -167,33 +167,6 @@ namespace meta {
#if false ////////////////////////////////////////////////////////////////////////////////TODO reorder
/* ==== Rebinding Variadic Arguments ==== **/
/**
* Metaprogramming helper to transfer variadic arguments.
* - builds a new type instantiation from the Template \a X
* - possibly picks up the variadic argument pack from a given
* source template `U<ARGS....>`
* @tparam X a variadic template
*/
template<template<typename...> class X, typename...ARGS>
struct RebindVariadic
{
using Type = X<ARGS...>;
};
template<template<typename...> class X
,template<typename...> class U
,typename...ARGS>
struct RebindVariadic<X, U<ARGS...>>
{
using Type = X<ARGS...>;
};
#endif ////////////////////////////////////////////////////////////////////////////////TODO reorder

View file

@ -14,156 +14,48 @@
/** @file variadic-rebind.hpp
** Metaprogramming support to rebuild and rebind variadic templates.
** The type rebinding- and helper templates in this header allow to perform
** simple sequence manipulations on sequences of template parameters extracted
** from variadic parameter packs. The goal is to (pre)process flexible argument
** lists _at compile time,_ driven by template instantiation, allowing to specialise
** and react specifically on some concrete pattern of argument types.
** The sequence of variadic arguments itself is often difficult to manipulate,
** because a _parameter pack_ is not a type proper in C++, but rather some meta
** mapping expanded immediately by the compiler. For more elaborate processing,
** this sequence must thus be re-mapped into a format that allows to pass partial
** results from recursive evaluations (since all of this »processing« is actually
** a form of functional programming).
**
** @warning the metaprogramming part of Lumiera to deal with type sequences is in a
** state of transition, since C++11 now offers direct language support for
** processing of flexible template parameter sequences ("parameter packs").
** It is planned to regroup and simplify our homemade type sequence framework
** to rely on variadic templates and integrate better with std::tuple.
** It is clear that we will _retain some parts_ of our own framework,
** since programming with _Loki-style typelists_ is way more obvious
** and straight forward than handling of template parameter packs,
** since the latter can only be rebound through pattern matching.
** @todo transition lib::meta::Types to variadic parameters /////////////////////////////////TICKET #987
**
** @see control::CommandDef usage example
** @see TupleHelper_test
** For many _simple cases_ though it is sufficient just to re-bind a template to
** another template's variadic sequence, possibly with some basic manipulation.
** Such a meta-processing can be coded very succinctly, by passing the target
** template to receive the altered sequence as a template-template parameter.
** @warning this kind of recursive remoulding typically imposes an O(n) effort
** during compilation, sometimes even O(n²) (notably reversing a sequence).
** Thus be sure to apply to short type sequences only
** endcode
** \par Usage example
** \code{.cpp}
** template<typename...CASES>
** struct MyModel
** {
** using SubSeq = typename _Vari<MyModel, CASES...>::Prefix;
**
** // adapt a predecessor sequence
** MyModel (SubSeq&& subModel);
**
**
** using Tuple = typename RebindVariadic<std::tuple, CASES...>::Type;
** }
** \endcode
** @see param-weaving-pattern.hpp "usage example"
** @see util::parse::AltModel "usage example"
** @see typelist.hpp
** @see function.hpp
** @see generator.hpp
**
*/
#ifndef LIB_META_VARIADIC_REBIND_H
#define LIB_META_VARIADIC_REBIND_H
//#include "lib/meta/typelist.hpp"
//#include "lib/meta/typelist-util.hpp"
//#include "lib/meta/typeseq-util.hpp"
//#include "lib/meta/util.hpp"
namespace lib {
namespace meta {
#if false ////////////////////////////////////////////////////////////////////////////////TODO reorder
/* ==== Build Variadic Sequences ==== **/
/** Hold a sequence of index numbers as template parameters */
template<size_t...idx>
struct IndexSeq
{
template<size_t i>
using AppendElm = IndexSeq<idx..., i>;
template<size_t i>
using PrependElm = IndexSeq<i, idx...>;
};
/**
* build regular sequences of index number
* e.g. `IndexSeq<0, 1, 2, ..., n-1>`
*/
template<size_t n>
struct BuildIndexSeq
{
using Ascending = typename BuildIndexSeq<n-1>::Ascending::template AppendElm<n-1>;
using Descending = typename BuildIndexSeq<n-1>::Descending::template PrependElm<n-1>;
template<size_t d>
using OffsetBy = typename BuildIndexSeq<n-1>::template OffsetBy<d>::template AppendElm<n-1+d>;
template<size_t x>
using FilledWith = typename BuildIndexSeq<n-1>::template FilledWith<x>::template AppendElm<x>;
template<size_t c>
using First = typename BuildIndexSeq<std::min(c,n)>::Ascending;
template<size_t c>
using After = typename BuildIndexSeq< (n>c)? n-c : 0>::template OffsetBy<c>;
};
template<>
struct BuildIndexSeq<0>
{
using Empty = IndexSeq<>;
using Ascending = Empty;
using Descending = Empty;
template<size_t>
using OffsetBy = Empty;
template<size_t>
using FilledWith = Empty;
template<size_t>
using First = Empty;
template<size_t>
using After = Empty;
};
/**
* build a sequence of index numbers based on a type sequence
*/
template<typename...TYPES>
struct BuildIdxIter
{
enum {SIZ = sizeof...(TYPES) };
using Builder = BuildIndexSeq<SIZ>;
using Ascending = typename Builder::Ascending;
using Descending = typename Builder::Descending;
template<size_t d>
using OffsetBy = typename Builder::template OffsetBy<d>;
template<size_t x>
using FilledWith = typename Builder::template FilledWith<x>;
template<size_t c>
using First = typename Builder::template First<c>;
template<size_t c>
using After = typename Builder::template After<c>;
};
/** build an index number sequence from a type sequence */
template<typename...TYPES>
struct BuildIdxIter<Types<TYPES...>>
{
///////////////////////TICKET #987 : since Types<T...> is not variadic, need to strip NullType here (instead of just using sizeof...(TYPES)
enum {SIZ = lib::meta::count<typename Types<TYPES...>::List>::value };
using Builder = BuildIndexSeq<SIZ>;
using Ascending = typename Builder::Ascending;
using Descending = typename Builder::Descending;
template<size_t d>
using OffsetBy = typename Builder::template OffsetBy<d>;
template<size_t x>
using FilledWith = typename Builder::template FilledWith<x>;
template<size_t c>
using First = typename Builder::template First<c>;
template<size_t c>
using After = typename Builder::template After<c>;
};
#endif ////////////////////////////////////////////////////////////////////////////////TODO reorder
@ -197,6 +89,22 @@ namespace meta {
/* ==== Rebuild with remoulded variadic sequence ==== **/
/**
* Metaprogramming helper to remould the type sequence
* in the template arguments of a variadic template.
* The key idea is to construct a new instance of the
* target template with an altered sequence; the target
* template itself is passed as template-template param.
* - `Penult` get the penultimate element of the sequence
* - `Ultima` get the last element of the sequence
* - `Prefix` rebind to the prefix sequence, leaving out the last
* - `Remain` rebind to the sequence starting with the second
* - `Revers` rebind to a sequence with reversed order
* A secondary helper template variant is provided for rebinding
* while prepending or appending a single type parameter.
* @note does not work with empty sequences; also the penultimate
* of a one-element sequence is mapped to NullType
*/
template<template<class...> class L, typename...XS>
struct _Vari;
@ -221,7 +129,7 @@ namespace meta {
struct _Vari<L, X>
{
using Ultima = X;
using Penult = NullType;
using Penult = NullType; ///< marker for undefined
using Remain = L<X>;
using Revers = L<X>;
using Prefix = L<>;
@ -241,248 +149,5 @@ namespace meta {
using Revers = typename _Vari<L, Ultima, _Tail_Rev_>::Prepend;
};
#if false ////////////////////////////////////////////////////////////////////////////////TODO reorder
/* ==== Build and Rebuild variadic type sequences ==== **/
/**
* Variadic type sequence builder.
* This metaprogramming helper template provides an unified view
* to handle _»tuple-like« types and variadic _type sequences._
* - the constant #SIZ gives the number of elements
* - the nested type #Idx can be used as _index sequence_
* - #Seq is a _variadic type sequence_ with the extracted types
* - #Tup is a std::tuple over these types
* - the nested template #Apply wraps each type into another template
* - #Rebind likewise instantiates another template with the element types
* - #AndAll applies a predicate and combines the result with _logical and_
* - #OrAll similarly evaluates _logical or_ on the application results
*/
template<class X, typename =void>
struct ElmTypes
{
static constexpr size_t SIZ = 1;
using Idx = std::index_sequence<SIZ>;
using Seq = TySeq<X>;
using Tup = std::tuple<X>;
template<template<class> class META>
using Apply = TySeq<META<X>>;
template<template<typename...> class O>
using Rebind = O<X>;
template<template<class> class PRED>
using AndAll = std::__and_<PRED<X>>;
template<template<class> class PRED>
using OrAll = std::__or_<PRED<X>>;
};
/** Partial specialisation to handle type sequences */
template<typename...TYPES>
struct ElmTypes<TySeq<TYPES...>>
{
static constexpr size_t SIZ = sizeof...(TYPES);
using Idx = std::make_index_sequence<SIZ>;
using Seq = TySeq<TYPES...>;
using Tup = std::tuple<TYPES...>;
template<template<class> class META>
using Apply = TySeq<META<TYPES>...>;
template<template<typename...> class O>
using Rebind = typename lib::meta::RebindVariadic<O, Seq>::Type;
template<template<class> class PRED>
using AndAll = typename ElmTypes<Apply<PRED>>::template Rebind<std::__and_>;
template<template<class> class PRED>
using OrAll = typename ElmTypes<Apply<PRED>>::template Rebind<std::__or_>;
};
/** partial specialisation to handle types
* supporting the C++ »tuple protocol«
*/
template<class TUP>
struct ElmTypes<TUP, enable_if_TupleProtocol<TUP>>
{
template<typename>
struct Extract;
template<size_t...idx>
struct Extract<std::index_sequence<idx...>>
{
using ElmTypes = TySeq<typename std::tuple_element<idx,TUP>::type ...>;
};
static constexpr size_t SIZ = std::tuple_size<TUP>::value;
using Idx = std::make_index_sequence<SIZ>;
using Seq = typename Extract<Idx>::ElmTypes;
using Tup = typename RebindVariadic<std::tuple, Seq>::Type;
template<template<class> class META>
using Apply = typename ElmTypes<Seq>::template Apply<META>;
template<template<typename...> class O>
using Rebind = typename RebindVariadic<O, Seq>::Type;
template<template<class> class PRED>
using AndAll = typename ElmTypes<Apply<PRED>>::template Rebind<std::__and_>;
template<template<class> class PRED>
using OrAll = typename ElmTypes<Apply<PRED>>::template Rebind<std::__or_>;
};
/* ==== Invoke with index from variadic ==== **/
/** helper to invoke a functor, passing instances of std::integral_constant
* @tparam N size of the index-sequence to use for instantiation
*/
template<size_t N>
class WithIdxSeq
{
template<class FUN, size_t...idx>
static void
invoke (FUN&& fun, std::index_sequence<idx...>)
{
(fun (std::integral_constant<size_t,idx>{}), ...);
}
public:
template<class FUN>
static void
invoke (FUN&& fun)
{
invoke (std::forward<FUN>(fun), std::make_index_sequence<N>{});
}
};
/**
* Invoke a function (or λ) with index numbers derived from some variadic count.
* Notably this construct can be used for compile-time iteration over a structure.
* Instances of `std::integral_constant` are passed in sequence to the functor.
* The _size_ of the index sequence is derived from the following sources
* - if the type \a TTX is _tuple-like,_ then std::tuple_size<TTX> is used
* - otherwise, if the type is a loki-style type sequence or type list,
* the number of type nodes is used
* - otherwise, as fall-back the number of template parameters is used
*/
template<class TTX, class FUN>
inline void
forEachIDX (FUN&& fun)
{
auto N = []{
if constexpr (is_Structured<TTX>())
return size_t(std::tuple_size<TTX>::value);
else
if constexpr (lib::meta::is_Typelist<TTX>::value)
return lib::meta::count<typename TTX::List>::value;
else
{ // Fallback: rebind template arguments into a type sequence
using Seq = typename RebindVariadic<TySeq, TTX>::Type;
return size_t(count<typename Seq::List>::value);
}
};
WithIdxSeq<N()>::invoke (std::forward<FUN> (fun));
}
/* ==== Manipulation of variadic arguments ==== **/
namespace { // Implementation delegate template...
/**
* @internal pick a single argument from a variadic parameter pack
* @tparam i the index number (zero based) of the argument to select
* @warning i must be smaller than the number of arguments available
*/
template<size_t i>
struct SelectVararg
{
template<typename ARG, typename...ARGS>
static auto
get (ARG, ARGS&& ...args)
{
return SelectVararg<i-1>::get (std::forward<ARGS> (args)...);
}
};
template<>
struct SelectVararg<0>
{
template<typename ARG, typename...ARGS>
static auto
get (ARG&& a, ARGS...)
{
return std::forward<ARG>(a);
}
};
/**
* @internal helper to decide if SelectVararg shall be applied.
* When the boolean condition does not hold, then, instead of selecting
* from the argument list, an element of type DEFAULT is created as fallback.
*/
template<bool, typename, size_t idx>
struct SelectOrInit
: SelectVararg<idx>
{ };
template<typename DEFAULT, size_t idx>
struct SelectOrInit<false, DEFAULT, idx>
{
template<typename...ARGS>
static DEFAULT
get (ARGS&&...)
{
return DEFAULT{};
}
};
}//(End)Implementation
/**
* Helper to single out one argument from a variadic argument pack.
* @tparam idx the index number (zero based) of the argument to select
* @remark typically this function is used "driven" by an likewise variadic index sequence,
* where the index sequence itself is picked up by a pattern match; this usage pattern
* allows arbitrarily to handle some of the arguments of a variable argument list,
* as determined by the index sequence passed in.
*/
template<size_t idx, typename...ARGS>
constexpr inline auto
pickArg (ARGS&&... args)
{
static_assert (idx < sizeof...(args), "insufficient number of arguments");
return SelectVararg<idx>::get (std::forward<ARGS> (args)...);
}
/**
* Helper to pick one initialisation argument from a variadic argument pack,
* falling back to a default constructed element of type `DEFAULT` in case of
* insufficient number of variadic arguments.
* @tparam idx the index number (zero based) of the argument to select
* @tparam DEFALUT type of the default element to construct as fallback
*/
template<size_t idx, typename DEFAULT, typename...ARGS>
constexpr inline auto
pickInit (ARGS&&... args)
{
return SelectOrInit<(idx < sizeof...(args)), DEFAULT, idx>::get (std::forward<ARGS> (args)...);
}
#endif ////////////////////////////////////////////////////////////////////////////////TODO reorder
}} // namespace lib::meta
#endif /*LIB_META_VARIADIC_REBIND_H*/

View file

@ -189,38 +189,46 @@ namespace util {
{
using Alt = lib::BranchCase<CASES...>;
template<typename INIT, typename =lib::meta::disable_if_self<AltModel,INIT>>
AltModel (INIT&& init)
: Alt{Alt::TOP, forward<INIT> (init)}
{ }
template<typename EXTRA>
using Additionally = AltModel<CASES...,EXTRA>;
using SubSeq = typename _Vari<AltModel, CASES...>::Prefix;
using Penult = typename _Vari<AltModel, CASES...>::Penult;
using Ultima = typename _Vari<AltModel, CASES...>::Ultima;
template<typename EX>
using Additionally = AltModel<CASES...,EX>;
template<typename EX>
Additionally<EX>
addBranch()
template<typename EXTRA>
Additionally<EXTRA>
addBranch() ///< mark-up existing model to add a further branch-case
{
Additionally<EX>& upFaked = reinterpret_cast<Additionally<EX>&> (*this);
Additionally<EXTRA>& upFaked = reinterpret_cast<Additionally<EXTRA>&> (*this);
return {move (upFaked)};
} // this trick works due to similar storage layout
/* === Builder functions to mark which side of the combinator to pick === */
using SubSeq = typename _Vari<AltModel, CASES...>::Prefix; ///< a nested sub-model to extend
using Penult = typename _Vari<AltModel, CASES...>::Penult; ///< plain value expected for left-branch
using Ultima = typename _Vari<AltModel, CASES...>::Ultima; ///< plain value expected for right-branch
static AltModel
mark_left (SubSeq&& leftCases)
{
return {leftCases.template addBranch<Ultima>()};
}
AltModel (SubSeq&& leftCases)
: AltModel{leftCases.template addBranch<Ultima>()}
{ }
static AltModel
mark_left (Penult&& leftCase)
{
return {Alt::TOP-1, move(leftCase)};
}
AltModel (Penult&& leftCase)
: Alt{Alt::TOP-1, move(leftCase)}
{ }
static AltModel
mark_right (Ultima&& rightCase)
{
return {Alt::TOP, move(rightCase)};
}
AltModel (Ultima&& rightCase)
: Alt{Alt::TOP, move(rightCase)}
private:
template<typename INIT>
AltModel (size_t branchID, INIT&& init)
: Alt{branchID, forward<INIT> (init)}
{ }
};
@ -246,7 +254,7 @@ namespace util {
using Result = TAG<R1,R2>;
};
/** Generic case : extend a structured model by further branch */
/** Generic case : extend a structured model by further branch */
template<template<class...> class TAG, class...RS, class R2>
struct _Join<TAG,TAG<RS...>,R2>
{

View file

@ -174,7 +174,7 @@ namespace test {
auto e2 = parseSeq(s2);
CHECK ( e2.result);
using SeqRes = std::decay_t<decltype(*e2.result)>; // Note: the result type depends on the actual syntax construction
using SeqRes = decltype(e2)::Result; // Note: the result type depends on the actual syntax construction
CHECK (is_Tuple<SeqRes>()); // Result model from sequence is the tuple of terminal results
auto& [r1,r2] = *e2.result;
CHECK (r1.str() == "hello"_expect);
@ -219,56 +219,80 @@ namespace test {
*/
void
acceptAlternatives()
{
{ //_______________________________
// Demonstrate Alt-Model mechanics
using R1 = char;
using R2 = string;
using R3 = double;
// build Model-Alternatives incrementally
using A1 = AltModel<R1>;
SHOW_EXPR(showType<A1>())
CHECK (showType<A1>() == "parse::AltModel<char>"_expect);
string s{"second"};
using A2 = A1::Additionally<R2>;
SHOW_EXPR(showType<A2>())
CHECK (showType<A2>() == "parse::AltModel<char, string>"_expect);
A2 model2{s};
SHOW_EXPR(sizeof(A2));
// create instance to represent this second branch...
A2 model2 = A2::mark_right ("seduced");
CHECK (sizeof(A2) >= sizeof(string)+sizeof(size_t));
SHOW_EXPR(model2.SIZ);
CHECK (model2.SIZ == sizeof(string));
SHOW_EXPR(model2.TOP);
CHECK (model2.TOP == 1);
SHOW_EXPR(model2.selected())
CHECK (model2.TOP == 1);
CHECK (model2.selected() == 1);
SHOW_EXPR(model2.get<1>())
CHECK (model2.get<1>() == "second");
CHECK (model2.get<1>() == "seduced");
using A3 = A2::Additionally<R3>;
A3 model3{model2.addBranch<R3>()};
SHOW_TYPE(A3)
SHOW_EXPR(showType<A3>())
A3 model3 = A3::mark_left (move (model2));
CHECK (showType<A3>() == "parse::AltModel<char, string, double>"_expect);
SHOW_EXPR(sizeof(A3));
CHECK (sizeof(A3) == sizeof(A2));
SHOW_EXPR(model3.SIZ);
SHOW_EXPR(model3.TOP);
CHECK (model3.TOP == 2);
SHOW_EXPR(model3.selected())
CHECK (model3.TOP == 2);
CHECK (model3.selected() == 1);
SHOW_EXPR(model3.get<1>())
CHECK (model3.get<1>() == "second");
CHECK (model3.get<1>() == "seduced");
auto res = move(model3);
SHOW_TYPE(decltype(res))
SHOW_EXPR(showType<decltype(res)>())
CHECK (showType<decltype(res)>() == "parse::AltModel<char, string, double>"_expect);
SHOW_EXPR(sizeof(res))
CHECK (sizeof(res) == sizeof(A2));
SHOW_EXPR(res.selected())
CHECK (res.selected() == 1);
SHOW_EXPR(res.get<1>())
CHECK (res.get<1>() == "second");
CHECK (res.selected() == 1);
CHECK (res.get<1>() == "seduced");
//_____________________________________________
// Demonstration: how branch combinator works....
auto term1 = buildConnex ("brazen");
auto term2 = buildConnex ("bragging");
auto parseAlt = [&](StrView toParse)
{
using R1 = decltype(term1)::Result;
using R2 = decltype(term2)::Result;
using SumResult = AltModel<R1,R2>;
using SumEval = Eval<SumResult>;
auto eval1 = term1.parse (toParse);
if (eval1.result)
{
uint endBranch1 = eval1.consumed;
return SumEval{SumResult::mark_left (move(*eval1.result))
,endBranch1
};
}
auto eval2 = term2.parse (toParse);
if (eval2.result)
{
uint endBranch2 = eval2.consumed;
return SumEval{SumResult::mark_right (move(*eval2.result))
,endBranch2
};
}
return SumEval{std::nullopt};
};
string s1{"decent contender"};
string s2{"brazen dicktator"};
auto e1 = parseAlt(s1);
CHECK (not e1.result); // does not compute....
auto e2 = parseAlt(s2); // one hell of a match!
CHECK ( e2.result);
CHECK (e2.result->selected() == 0); // Selector-ID of the first matching branch (here #0)
CHECK (e2.result->get<0>().str() == "brazen"); // We know that branch#0 holds a RegExp-Matcher (from term1)
CHECK (e2.result->get<0>().suffix() == " dicktator");
}
};

View file

@ -56523,15 +56523,17 @@
<node CREATED="1737483220362" ID="ID_480511006" MODIFIED="1737483229460" TEXT="die F&#xe4;lle sind durch das _Join-Template bereits vorsortiert"/>
<node CREATED="1737483230120" ID="ID_1640338591" MODIFIED="1737483245762" TEXT="dieses generiert auch bereits die korrekte AltModel-Instanz"/>
</node>
<node CREATED="1737483307902" ID="ID_1499952349" MODIFIED="1737483323544" TEXT="brauche passende Konstruktoren">
<node COLOR="#338800" CREATED="1737483307902" ID="ID_1499952349" MODIFIED="1737507877713" TEXT="brauche passende Konstruktoren">
<icon BUILTIN="button_ok"/>
<node CREATED="1737483324699" ID="ID_1345828060" MODIFIED="1737483327919" TEXT="und zwar...">
<node CREATED="1737483328843" ID="ID_1658557856" MODIFIED="1737483335394" TEXT="links-sub-Model"/>
<node CREATED="1737483335987" ID="ID_986287971" MODIFIED="1737483342517" TEXT="links-Einzelwert"/>
<node CREATED="1737483343129" ID="ID_1446250040" MODIFIED="1737483347092" TEXT="rechts-Einzelwert"/>
</node>
<node CREATED="1737483348177" ID="ID_1350491098" MODIFIED="1737483358069" TEXT="brauche dazu die konkreten Typen">
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1737483360495" ID="ID_1943823761" MODIFIED="1737483375712" TEXT="per Rebinding-Hilfstemplate aufbauen">
<icon BUILTIN="flag-yellow"/>
<node COLOR="#435e98" CREATED="1737483348177" ID="ID_1350491098" MODIFIED="1737507857907" TEXT="brauche dazu die konkreten Typen">
<icon BUILTIN="yes"/>
<node COLOR="#338800" CREATED="1737483360495" FOLDED="true" ID="ID_1943823761" MODIFIED="1737507871184" TEXT="per Rebinding-Hilfstemplate aufbauen">
<icon BUILTIN="button_ok"/>
<node CREATED="1737483378196" ID="ID_1296449236" LINK="https://stackoverflow.com/a/34941417/444796" MODIFIED="1737483733563" TEXT="Problem: C++ kann nur sehr limitiert gegen Argument-Packs matchen">
<richcontent TYPE="NOTE"><html>
<head>
@ -56580,7 +56582,7 @@
</node>
</node>
</node>
<node CREATED="1737496156742" ID="ID_1900356198" MODIFIED="1737496733785" TEXT="das sieht nach einem generischen Tool aus">
<node BACKGROUND_COLOR="#e1e2ba" COLOR="#435e98" CREATED="1737496156742" ID="ID_1900356198" MODIFIED="1737507842748" TEXT="das sieht nach einem generischen Tool aus">
<linktarget COLOR="#fdfcc6" DESTINATION="ID_1900356198" ENDARROW="Default" ENDINCLINATION="17;-73;" ID="Arrow_ID_987482225" SOURCE="ID_1009577491" STARTARROW="None" STARTINCLINATION="-279;12;"/>
<icon BUILTIN="idea"/>
<node CREATED="1737496171990" ID="ID_1854927200" MODIFIED="1737496227434" TEXT="&#xd83e;&#xdc32; neuer Header: rebind-variadic-rebind.hpp"/>
@ -56612,6 +56614,54 @@
</node>
</node>
</node>
<node CREATED="1737503596247" ID="ID_371220796" MODIFIED="1737503608693" TEXT="Auswertung und Model-Erzeugung">
<node COLOR="#338800" CREATED="1737503609673" ID="ID_984166098" MODIFIED="1737507882083" TEXT="wieder zun&#xe4;chst freistehend im Test ausformulieren">
<icon BUILTIN="button_ok"/>
</node>
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#435e98" CREATED="1737504161230" ID="ID_757848579" MODIFIED="1737507887375" TEXT="gleiches Baumuster wie der seqConnex">
<icon BUILTIN="idea"/>
<node CREATED="1737504193738" ID="ID_1385503322" MODIFIED="1737504206428" TEXT="wird hier sogar noch einfacher"/>
<node CREATED="1737504208567" ID="ID_1221738967" MODIFIED="1737507938424" TEXT="Design scheint sehr gut zu passen">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
...Hinweis darauf ist der Umstand, da&#223; ich gar nicht mehr viel auswerten / pr&#252;fen mu&#223;, sondern direkt der Match auf die Konstruktor-Argumente den Rest der Logik erledigt.
</p>
</body>
</html>
</richcontent>
</node>
</node>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1737504141641" ID="ID_457554053" MODIFIED="1737507459721" TEXT="Problem: Konstruktor-Signaturen k&#xf6;nnen zusammenfallen">
<icon BUILTIN="messagebox_warning"/>
<node CREATED="1737504226576" ID="ID_1730264427" MODIFIED="1737504238455" TEXT="so ein Pech aber auch...">
<icon BUILTIN="smily_bad"/>
</node>
<node CREATED="1737504240388" ID="ID_718576565" MODIFIED="1737504257555" TEXT="...wenn zuf&#xe4;llig beide Zweige im Kombinator den gleichen Ergebnistyp haben"/>
<node CREATED="1737504264413" ID="ID_198801973" MODIFIED="1737504277138" TEXT="mu&#xdf; anderweitig einen klaren Marker mitgeben"/>
<node CREATED="1737504278222" ID="ID_7748352" MODIFIED="1737504291496" TEXT="&#x27f9; Builder-Funktionen bieten">
<node CREATED="1737507477423" ID="ID_712733860" MODIFIED="1737507490145" TEXT="ist ma&#xdf;geschneidert f&#xfc;r diesen einen Fall"/>
<node CREATED="1737507491637" ID="ID_397795294" MODIFIED="1737507505855" TEXT="Clients sollen nur selected() und get&lt;i&gt;() verwenden"/>
<node CREATED="1737507511586" ID="ID_1197062880" MODIFIED="1737507548997" TEXT="eigentlichen Konstruktor besser private machen">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
da geht n&#228;mlich eine Branch-ID ein, und die mu&#223; &lt;= TOP sein
</p>
</body>
</html>
</richcontent>
<icon BUILTIN="yes"/>
</node>
</node>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1737417144217" ID="ID_375516701" MODIFIED="1737417153090" TEXT="in einen Connex-Builder verpacken">
<icon BUILTIN="flag-yellow"/>