BufferProvider: research how to build a policy with callbacks

While it is trivially simple to define some struct with callback functions,
such a definition turns out to be obnoxiously complex and hard to read in pracice,
thereby nullifying any benefit from the shorthand λ-based notation.

This kind of problem with the readability at client-side is quite common
when using callbacks for a policy-based design. To overcome this difficutly,
it would be necessary to define the policy and the callbacks in one
succinct builder expression. Unfortunately such a builder syntax
typically requires to write a lot of boilerplate template code.

After several failed experiments, I have found a bold new way to
exploit features of C++20 in conjunction with a macro, allowing
to generate a suitable data record directly form an C++ expression.
This commit is contained in:
Fischlurch 2026-02-25 23:20:26 +01:00
parent 77217d9785
commit 8437710232
14 changed files with 518 additions and 121 deletions

View file

@ -1,56 +0,0 @@
/*
MAYBE-COMPARE.hpp - guarded invocation of comparisons
Copyright (C)
2009, Hermann Vosseler <Ichthyostega@web.de>
  **Lumiera** is free software; you can redistribute it and/or modify it
  under the terms of the GNU General Public License as published by the
  Free Software Foundation; either version 2 of the License, or (at your
  option) any later version. See the file COPYING for further details.
*/
/** @file maybe-compare.hpp
** helpers for fail-safe invocation of comparison operations from generic code.
*/
#ifndef LIB_META_MAYBE_COMPARE_H
#define LIB_META_MAYBE_COMPARE_H
namespace lib {
namespace meta{
/**
* Trait template for invoking equality comparison.
* This allows to treat some types specifically....
*/
template<typename X>
struct Comparator
{
static bool
equals (X const& x1, X const& x2)
{
return x1 == x2;
}
};
/* == add specialisations here == */
template<typename X>
inline bool
equals_safeInvoke (X const& x1, X const& x2)
{
return Comparator<X>::equals(x1,x2);
}
}} // namespace lib::meta
#endif

View file

@ -0,0 +1,89 @@
/*
PROP-BUILDER.hpp - generate data record with arbitrary named fields
Copyright (C)
2026, Hermann Vosseler <Ichthyostega@web.de>
  **Lumiera** is free software; you can redistribute it and/or modify it
  under the terms of the GNU General Public License as published by the
  Free Software Foundation; either version 2 of the License, or (at your
  option) any later version. See the file COPYING for further details.
*/
/** @file prop-builder.hpp
** A builder for compile-time generation of data records with arbitrary named fields.
*/
#ifndef LIB_META_PROP_BUILDER_H
#define LIB_META_PROP_BUILDER_H
#include "lib/meta/trait.hpp"
#include <utility>
namespace lib {
namespace meta{
using std::move;
using std::forward;
/**
* Anchor record and builder mechanics for generating data records.
* @see PropBuilder_test::simpleUsage
*/
struct PropBuilder
{
/**
* Core builder operation: invoke a builder-λ to extend »this«
* @param layerBilder a specially crafted invokable, that takes _this object_
* and the given property value and creates an instance of a derived class.
* @note the macro \ref PROP_FIELD below will generate a suitable argument.
* @param val an arbitrary (movable) data element to be stored as property.
* @remark C++20 introduced the syntax for _explicit object member functions_,
* which allow to bind the "self" object instance to an explicitly defined
* first function parameter. Since this builder operation will be invoked
* on the generated compound object, the template parameter \a BAS will
* denote this compound, and the next builder-λ will derive from it.
*/
template<class BAS, class LAY, typename VAL>
auto
define (this BAS&& chain, LAY const& layerBuilder, VAL&& val)
{
return layerBuilder (move(chain), forward<VAL>(val));
}
};
#define PROP_FIELD(FIELD_NAME) \
[]<class BAS, typename VAL> \
(BAS&& bas, VAL&& val) \
{ \
using BaseLayer = std::decay_t<BAS>;\
using Property = std::decay_t<VAL>; \
\
struct DataLayer_ ## FIELD_NAME \
: BaseLayer \
{ \
Property FIELD_NAME; \
\
DataLayer_ ## FIELD_NAME (BaseLayer b \
,Property v) \
: BaseLayer{move(b)} \
,FIELD_NAME{move(v)} \
{ } \
}; \
return DataLayer_ ## FIELD_NAME {forward<BAS>(bas)\
,forward<VAL>(val) \
}; \
}
}} // namespace lib::meta
#endif /*LIB_META_PROP_BUILDER_H*/

View file

@ -57,9 +57,6 @@
//Forward declarations for the Unwrap helper....
namespace boost{
template<class X> class reference_wrapper;
}
namespace std {
template<class X> class reference_wrapper;
template<class X> class shared_ptr;
@ -159,19 +156,6 @@ namespace meta {
}
};
template<typename X>
struct Unwrap<boost::reference_wrapper<X>>
: std::true_type
{
using Type = X;
static X&
extract (boost::reference_wrapper<X> wrapped)
{
return wrapped;
}
};
template<typename X>
struct Unwrap<std::reference_wrapper<X>>
: std::true_type
@ -328,7 +312,11 @@ namespace meta {
, is_same<I,S>
>
{ };
template<typename S, typename I>
inline constexpr bool is_Subclass_v = is_Subclass<S,I>();
/** compare for unadorned base type, disregarding const and references */
template<typename S, typename I>
struct is_basically

View file

@ -130,7 +130,7 @@ namespace lib {
TY&
create (ARGS&& ...args)
{
static_assert ( meta::is_Subclass<TY,I>()
static_assert ( meta::is_Subclass_v<TY,I>
&& sizeof(TY) <= siz,
"ElementHolder buffer too small");

View file

@ -137,7 +137,7 @@ namespace lib {
using std::is_volatile_v;
using std::is_const_v;
using std::is_same_v;
using lib::meta::is_Subclass;
using lib::meta::is_Subclass_v;
using several::ArrayBucket;
@ -743,7 +743,7 @@ namespace lib {
typename Policy::Fac& factory(*this);
if (is_Subclass<TVal,IVal>() and has_virtual_destructor_v<IVal>)
if (is_Subclass_v<TVal,IVal> and has_virtual_destructor_v<IVal>)
{
__ensureMark<TVal> (VIRTUAL);
return [factory](ArrayBucket<I>* bucket){ unConst(factory).template destroy<IVal> (bucket); };
@ -753,7 +753,7 @@ namespace lib {
__ensureMark<TVal> (TRIVIAL);
return [factory](ArrayBucket<I>* bucket){ unConst(factory).template destroy<TVal> (bucket); };
}
if (is_same_v<TVal,EVal> and is_Subclass<EVal,IVal>())
if (is_same_v<TVal,EVal> and is_Subclass_v<EVal,IVal>)
{
__ensureMark<TVal> (ELEMENT);
return [factory](ArrayBucket<I>* bucket){ unConst(factory).template destroy<EVal> (bucket); };

View file

@ -741,7 +741,7 @@ namespace lib {
inline void
launchDetached (ThreadHookable::Launch&& launchBuilder)
{
static_assert (lib::meta::is_Subclass<TAR, ThreadHookable>());
static_assert (lib::meta::is_Subclass_v<TAR, ThreadHookable>);
new TAR{move(launchBuilder)
.atExit([](TAR& selfAllocation)

View file

@ -277,7 +277,7 @@ namespace interact {
// append ID of the new element to be created
// unless it's already there (and thus exists)
resolver.append (elementTypeID);
return move (resolver); //////////////////////////////////////////////////////////////////TICKET #1402 : need a better solution for the builder-terminal-op. (it collides with the templated UICoord ctor) |NOTE: the compiler waring is wrong here, the move() is essential
return move (resolver); //////////////////////////////////////////////////////////////////TICKET #1402 : need a better solution for the builder-terminal-op. (it collides with the templated UICoord ctor) |NOTE: the compiler warning is wrong here, the move() is essential
// use the first suitable solution and exit
}
else

View file

@ -64,7 +64,7 @@ namespace model {
class ZoomMetric
: public I
{
static_assert(lib::meta::is_Subclass<I, DisplayMetric>());
static_assert(lib::meta::is_Subclass_v<I, DisplayMetric>);
protected:
ZoomWindow zoomWindow_;

View file

@ -31,7 +31,6 @@
#include "lib/meta/function-closure.hpp"
#include "lib/meta/tuple-helper.hpp"
#include "lib/meta/tuple-record-init.hpp"
#include "lib/meta/maybe-compare.hpp"
#include "steam/control/command-closure.hpp"
#include "steam/control/argument-erasure.hpp"
#include "lib/typed-allocation-manager.hpp"

View file

@ -31,7 +31,6 @@
#ifndef CONTROL_MEMENTO_TIE_H
#define CONTROL_MEMENTO_TIE_H
#include "lib/meta/maybe-compare.hpp"
#include "lib/meta/function-closure.hpp"
#include "steam/control/command-signature.hpp"
#include "lib/item-wrapper.hpp"
@ -48,7 +47,6 @@ namespace control {
using lib::meta::func::bindLast;
using lib::meta::func::chained;
using lib::meta::equals_safeInvoke;
using lib::wrapper::ItemWrapper;

View file

@ -296,6 +296,11 @@ return: 0
END
TEST "Generated data records" PropBuilder_test <<END
return: 0
END
TEST "Variadic template diagnostics" TestHelperVariadic_test <<END
out-lit: --no-arg--
out-lit: :.

View file

@ -30,7 +30,7 @@ namespace lib {
namespace test{
using std::string;
using meta::is_Subclass;
using meta::is_Subclass_v;
using util::isSameObject;
using util::isSameAdr;
using util::getAdr;
@ -80,7 +80,7 @@ namespace test{
verify_FrontBlock()
{
using Block1 = HeteroData<uint,double>;
CHECK ((is_Subclass<Block1::NewFrame, std::tuple<uint,double>>()));
CHECK ((is_Subclass_v<Block1::NewFrame, std::tuple<uint,double>>));
auto b1 = Block1::build (42, 1.61803);
CHECK (1.61803 == b1.get<1>());
@ -109,11 +109,11 @@ namespace test{
verify_ChainBlock()
{
using Block1 = HeteroData<uint>;
CHECK ((is_Subclass<Block1::NewFrame, std::tuple<uint>>()));
CHECK ((is_Subclass_v<Block1::NewFrame, std::tuple<uint>>));
using Constructor = Block1::Chain<double,string>;
using Block2 = Constructor::NewFrame;
CHECK ((is_Subclass<Block2, std::tuple<double, string>>()));
CHECK ((is_Subclass_v<Block2, std::tuple<double, string>>));
auto b1 = Block1::build (41);
auto b2 = Constructor::build (1.61, "Φ");

View file

@ -0,0 +1,235 @@
/*
PropBuilder(Test) - verify helpers for working with tuples and type sequences
Copyright (C)
2026, Hermann Vosseler <Ichthyostega@web.de>
  **Lumiera** is free software; you can redistribute it and/or modify it
  under the terms of the GNU General Public License as published by the
  Free Software Foundation; either version 2 of the License, or (at your
  option) any later version. See the file COPYING for further details.
* *****************************************************************/
/** @file prop-builder-test.cpp
** Demonstration and explanation of compile-time generated data records.
**
*/
#include "lib/test/run.hpp"
#include "lib/test/test-helper.hpp"
#include "lib/meta/prop-builder.hpp"
//#include "lib/meta/tuple-helper.hpp"
//#include "meta/typelist-diagnostics.hpp"
//#include "meta/tuple-diagnostics.hpp"
//#include "lib/format-string.hpp"
//#include "lib/format-cout.hpp"
//#include "lib/hetero-data.hpp"
#include "lib/test/diagnostic-output.hpp"////////////////TODO
#include <string>
using std::string;
using lib::test::showType;
//using lib::test::showSizeof;
//using util::toString;
//using util::_Fmt;
//using std::is_same_v;
//using std::make_tuple;
//using std::get;
namespace lib {
namespace meta {
namespace test {
namespace { // test data
} // (End) test data
#define TYPE(_TY_) showType<decltype(_TY_)>()
/*********************************************************************//**
* @test Demonstrate how data records with arbitrary, named fields
* can be generated at compile time, using C++20 features.
* - explain the underlying mechanics
* - show a usage scenario
*/
class PropBuilder_test : public Test
{
virtual void
run (Arg)
{
simpleUsage();
explain_building_blocks();
demonstrate_usage_scenario();
}
/** @test show syntax and usage
*/
void
simpleUsage()
{
auto record = PropBuilder()
.define (PROP_FIELD(one), 11u)
.define (PROP_FIELD(two), "2")
;
CHECK (record.one == 11u);
CHECK (record.two == "2"_expect);
CHECK (TYPE(record.one) == "uint"_expect);
CHECK (TYPE(record.two) == "const char *"_expect);
record.one = 55;
CHECK (record.one == 55u);
auto copy = record;
copy.two = "something different";
CHECK (record.two == "2"_expect);
CHECK (copy.two == "something different"_expect);
}
struct Base
{
template<class X>
string
showSelfType (this X&)
{
return showType<X>();
}
};
struct Derived : Base { };
/** @test demonstrate and verify the language features
* used to generate arbitrary record data types.
* - capture self-type with C++20 _explicit object member function_
* - use generic-λ to generate a derived class with a property field
*/
void
explain_building_blocks()
{
// The first building block relies on the ability to pick up at invocation time
// the actual type that is used to call a member function, even while this function
// was defined somewhere in a base class: C++20 introduced a special syntax to mark
// the first "this" argument, passed automatically to each member invocation.
// And since code from a function template is generated late only, on actual use,
// we can define a type parameter to capture the "self type" explicitly:
Derived derived;
Base& baseref{derived};
CHECK (baseref.showSelfType() == "test::PropBuilder_test::Base"_expect );
CHECK (derived.showSelfType() == "test::PropBuilder_test::Derived"_expect );
// The next building block exploits the fact that C++ allows to define
// a struct or class locally, within a function scope; and while this
// local type can be _referred to_ only locally, within this function,
// it is _not a hidden type_. Data elements using such a local type
// can be returned by-value — which allows us to "drop off" such
// a data record, and this also implicitly its type.
// While this seems rather pointless in itself, it can be leveraged
// with the help of C++20 explicit lambda template parameters: we can
// move an instance of a base class into a function, thereby pick up
// its type, and define a new derived class, and even move the given
// base class instance into the embedded base class sub-object:
auto dropperLambda = []<class BAS, typename VAL>(BAS&& bas, VAL&& val)
{
using BaseLayer = std::decay_t<BAS>;
using Property = std::decay_t<VAL>;
struct DataLayer_somthing
: BaseLayer
{
Property something;
DataLayer_somthing (BaseLayer b, Property v)
: BaseLayer{move(b)}
, something{move(v)}
{ }
DataLayer_somthing() = default;
};
return DataLayer_somthing{forward<BAS>(bas)
,forward<VAL>(val)
};
};
struct SomeRecord
{
float someData;
};
ushort evil = 55;
auto compoundRecord = dropperLambda (SomeRecord{0.13}, evil);
// here SomeRecord was created transiently, moved into the dropper-λ,
// used there as base for a derived struct DataLayer_something, which
// features an additional property `something`, initialised with an
// arbitrary value (and type) also passed on invocation.
// The result is a record object, which incorporates the base, together
// with an additional "Layer" holding the new property, stored efficiently.
using Compound = decltype(compoundRecord);
SHOW_TYPE(decltype(compoundRecord))
SHOW_EXPR(TYPE(compoundRecord))
SHOW_EXPR(sizeof(SomeRecord))
CHECK (sizeof(SomeRecord) == sizeof(float));
SHOW_EXPR(sizeof(Compound))
CHECK (sizeof(Compound) >= sizeof(float)+sizeof(ushort));
SHOW_EXPR(alignof(SomeRecord))
SHOW_EXPR(alignof(Compound))
CHECK (alignof(Compound) == alignof(SomeRecord));
CHECK (sizeof(Compound) == 2 * alignof(float));
SHOW_EXPR(std::is_class_v<Compound>)
SHOW_EXPR(std::is_trivial_v<Compound>)
SHOW_EXPR(std::is_trivial_v<SomeRecord>)
SHOW_EXPR(std::is_trivially_copyable_v<SomeRecord>)
SHOW_EXPR(std::is_trivially_copyable_v<Compound>)
SHOW_EXPR(std::is_standard_layout_v<Compound>)
CHECK (std::is_class_v<Compound>);
CHECK (std::is_trivial_v<Compound>);
CHECK (std::is_trivial_v<SomeRecord>);
CHECK (std::is_trivially_copyable_v<Compound>);
CHECK (std::is_trivially_copyable_v<SomeRecord>);
// Yet obviously it is not a POD,
// since non-static members reside at several levels
CHECK (not std::is_standard_layout_v<Compound>);
SHOW_EXPR((is_Subclass_v<Compound, SomeRecord>))
CHECK ((is_Subclass_v<Compound, SomeRecord>));
}
/** @test show how to use generated records to implement a policy.
*/
void
demonstrate_usage_scenario()
{
}
};
/** Register this test class... */
LAUNCHER (PropBuilder_test, "unit meta");
}}} // namespace lib::meta::test

View file

@ -58599,6 +58599,69 @@
<node CREATED="1751414247721" ID="ID_1171949349" MODIFIED="1751414277013" TEXT="&#x27f9; lib::meta::apply als Ersatz (und lib::meta::getElm)"/>
</node>
</node>
<node CREATED="1772066411633" ID="ID_50275437" MODIFIED="1772066416949" TEXT="Code-Generierung">
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1772066190351" ID="ID_940571255" MODIFIED="1772066239070" TEXT="PropBuilder : Record mit benannten Feldern generieren">
<icon BUILTIN="yes"/>
<node CREATED="1772066253767" ID="ID_1072763198" MODIFIED="1772067242722" TEXT="Neue Metaprgramming-Technik &#x27f8; f&#xfc;r Policy-based Design">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
...das will ich schon seit endlos langer Zeit, und bin aber immer wieder zu dem Schlu&#223; gekommen, da&#223; es in C++ nicht wirklich geht, da uns die Introspektion von Record-Typen fehlt.
</p>
<p>
2026/2 : nun hatte ich wieder so einen Fall, und hab mich drei Tage lang gr&#252;n und blau ge&#228;rgert, weil es so sagenhaft umst&#228;ndlich (und auch verwirrend) ist, wenn man eine Policy definieren m&#246;chte, die <i>mehrere Lambdas</i>&#160;zur Konfiguration verwendet. Konkret war das (nat&#252;rlich) nur ein Nebenschauplatz, und &#187;eigentlich braucht man es nicht&#171; ...
</p>
<p>
jedenfalls hab ich mal wieder ganz locker ein Design gemacht, und mir gedacht &#8222;den Rest konfiguriert man dann ganz einfach per Policy&#8220;. Denkste. Und da ich jetzt schon so viel Zeit vergeudet habe (Hallo &#187;sunken cost fallacy&#171;, nice to meet you again!), hab ich mich auf eine Macro-basierte L&#246;sung eingeschossen. Nachdem ich dann dar&#252;ber zwei Tage gebr&#252;tet hatte, sind mir die &#955;-Template-Parameter eingefallen, die es ja erst seit C++20 gibt. Beim Nachlesen in der Doku bin ich dann zuf&#228;llig auch noch auf die &#187;Explicit object member functions&#171; gesto&#223;en &#8212; ein Feature, das mir komplett entgangen war. Beide zusammen <i>sind ein echter Game-Changer!</i>&#160;Denn damit kann man nun nicht nur einen Typ in einer Funktion generieren (das konnte man schon immer), sondern das per Lambda <b>in einem Ausdruck</b>&#160;machen, ohne noch eine separate Deklaration zu brauchen. Das erscheint mir jetzt als ein Quantensprung, und hebt das &#187;Policy-based-Design&#171; auf einen anderen Level der Expressivit&#228;t.
</p>
</body>
</html></richcontent>
<linktarget COLOR="#2b57f9" DESTINATION="ID_1072763198" ENDARROW="Default" ENDINCLINATION="2965;140;" ID="Arrow_ID_198810341" SOURCE="ID_1029922863" STARTARROW="None" STARTINCLINATION="-2090;136;"/>
<icon BUILTIN="info"/>
</node>
<node CREATED="1772067250896" ID="ID_1640694078" MODIFIED="1772067306544" TEXT="Aufgabe: einen &#xbb;ma&#xdf;geschneiderten&#xab; Daten-Record...">
<node CREATED="1772067307899" ID="ID_919196456" MODIFIED="1772067410868" TEXT="kompakt und lesbar &#xbb;inline&#xab; definieren">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
...das ist die entscheidende Verbesserung; abgesehen davon ist jede Klasse oder Struct ein &#187;ma&#223;geschneiderter Daten-Record&#171; &#8212; aber die <i>boilerplate</i>&#160; allein zum Initialisieren ist so umst&#228;ndlich, da&#223; das Ergebnis nicht mehr <i>selbsterkl&#228;rend ist.</i>
</p>
</body>
</html></richcontent>
</node>
<node CREATED="1772067418044" ID="ID_1314882599" MODIFIED="1772067438651" TEXT="und gleich mit inline-&#x3bb; und Kontext-capture initialisieren"/>
</node>
<node CREATED="1772068221512" ID="ID_774607578" MODIFIED="1772070444439" TEXT="Bau-Technik">
<icon BUILTIN="forward"/>
<node COLOR="#435e98" CREATED="1772068227431" ID="ID_361097452" MODIFIED="1772070455630" TEXT="ein Builder-Ausdruck"/>
<node COLOR="#435e98" CREATED="1772068233846" ID="ID_1873632379" MODIFIED="1772070455630" TEXT="ein Lambda, das einen abgeleiteten Typ definiert">
<node CREATED="1772068272529" ID="ID_57203075" MODIFIED="1772068277732" TEXT="es nimmt ein Basis-Objekt"/>
<node CREATED="1772068278549" ID="ID_1359999705" MODIFIED="1772068285633" TEXT="plus einen Property-Wert"/>
<node CREATED="1772068286375" ID="ID_1783360048" MODIFIED="1772068300481" TEXT="und erzeugt eine abgeleitete Klasse mit diesem Property"/>
</node>
<node COLOR="#435e98" CREATED="1772068303510" ID="ID_142658280" MODIFIED="1772070460038" TEXT="Definition und Aufruf dieses Lambdas kann man in einen einzigen Ausdruck packen">
<icon BUILTIN="idea"/>
</node>
<node COLOR="#435e98" CREATED="1772068327705" ID="ID_782344387" MODIFIED="1772070455629" TEXT="und diesen Ausdruck kann man dann mit einem Macro generieren">
<node CREATED="1772068339947" ID="ID_721460446" MODIFIED="1772068350577" TEXT="damit werden neue Feldnamen m&#xf6;glich"/>
<node CREATED="1772068351334" ID="ID_302440640" MODIFIED="1772068360385" TEXT="und man kann auch den generierten Typ dekorieren"/>
</node>
</node>
<node CREATED="1772068366233" ID="ID_858655013" MODIFIED="1772068376142" TEXT="Implementierung">
<node CREATED="1772068377138" ID="ID_160543739" MODIFIED="1772068386687" TEXT="lib/meta/prop-builder.hpp"/>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1772068387673" ID="ID_999468145" MODIFIED="1772068449713" TEXT="PropBuilder_test">
<icon BUILTIN="pencil"/>
<node CREATED="1772068394858" ID="ID_1256049272" MODIFIED="1772068407518" TEXT="Aufruf-Syntax zeigen"/>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1772068408103" ID="ID_452419709" MODIFIED="1772068442634" TEXT="die verwendeten Programmiertechniken dokumentieren">
<icon BUILTIN="pencil"/>
</node>
<node CREATED="1772068421596" ID="ID_1602327531" MODIFIED="1772068435958" TEXT="Beispiel f&#xfc;r ein Policy-based Design darststellen"/>
</node>
</node>
</node>
</node>
<node CREATED="1540682664865" ID="ID_1702211999" MODIFIED="1557498707236" TEXT="Metaprogramming">
<node CREATED="1739742552923" ID="ID_606595417" MODIFIED="1739742563204" TEXT="Typsequenzen und Typlisten">
<node BACKGROUND_COLOR="#ccb59b" COLOR="#6e2a38" CREATED="1739742570360" ID="ID_1552300649" MODIFIED="1739743026716" TEXT="Grunds&#xe4;tzlich: die Loki-Typlisten / Sequenzen werde ich weiterhin nutzen und erhalten">
@ -114374,14 +114437,15 @@ StM_bind(Builder&lt;R1&gt; b1, Extension&lt;R1,R2&gt; extension)
<node CREATED="1771634200296" ID="ID_1781890966" MODIFIED="1771634221593" TEXT="das ist angemessen; es geht um Implementierungs-Technik"/>
<node CREATED="1771634718849" ID="ID_158495787" MODIFIED="1771634743108" TEXT="man k&#xf6;nnte zudem die Anforderungen als Concept dokumentieren"/>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1771807890857" ID="ID_44174155" MODIFIED="1771809182613" TEXT="Experiment zur Policy">
<icon BUILTIN="pencil"/>
<node COLOR="#338800" CREATED="1771807890857" ID="ID_44174155" MODIFIED="1771893611677" TEXT="Experiment zur Policy">
<icon BUILTIN="button_ok"/>
<node CREATED="1771807898504" ID="ID_1896427982" MODIFIED="1771807909962" TEXT="einfachste Form: eine Policy als Baseclass"/>
<node CREATED="1771807911166" ID="ID_1166005757" MODIFIED="1771807924271" TEXT="die Policy selber soll moveable sein"/>
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1771807934449" ID="ID_292154472" MODIFIED="1771807951937" TEXT="Problem: ich brauch die Policy in der Store-Implementierung">
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#435e98" CREATED="1771807934449" ID="ID_292154472" MODIFIED="1771893713330" TEXT="Problem: ich brauch die Policy in der Store-Implementierung">
<arrowlink COLOR="#5ba4a3" DESTINATION="ID_937919882" ENDARROW="Default" ENDINCLINATION="-525;70;" ID="Arrow_ID_487502478" STARTARROW="None" STARTINCLINATION="111;5;"/>
<icon BUILTIN="messagebox_warning"/>
</node>
<node BACKGROUND_COLOR="#fafe99" COLOR="#fa002a" CREATED="1771808870215" ID="ID_1799682600" MODIFIED="1771808894898" TEXT="damit verbundenes Problem: Konfiguration des BufferProviderSetup">
<node BACKGROUND_COLOR="#d7b089" COLOR="#9c0648" CREATED="1771808870215" ID="ID_1799682600" MODIFIED="1771893643654" TEXT="damit verbundenes Problem: Konfiguration des BufferProviderSetup">
<icon BUILTIN="broken-line"/>
<node CREATED="1771808897410" ID="ID_1165872773" MODIFIED="1771808913796" TEXT="den m&#xfc;ssen wir ebenfalls per Policy konfigurieren">
<node CREATED="1771808914975" ID="ID_1601123928" MODIFIED="1771808929187" TEXT="damit wir die Spezialisierung erst im Konstruktor-Rumpf brauchen"/>
@ -114389,16 +114453,13 @@ StM_bind(Builder&lt;R1&gt; b1, Extension&lt;R1,R2&gt; extension)
</node>
<node CREATED="1771808957102" ID="ID_266948865" MODIFIED="1771808988313">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
das macht die sache nun <font size="4" color="#aa0707">extrem ekelhaft und verworren</font>
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<icon BUILTIN="smiley-angry"/>
</node>
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#435e98" CREATED="1771808997763" ID="ID_251025614" MODIFIED="1771809261721" TEXT="irgendwie durchpr&#xfc;geln">
@ -114411,9 +114472,7 @@ StM_bind(Builder&lt;R1&gt; b1, Extension&lt;R1,R2&gt; extension)
<node CREATED="1771809101214" ID="ID_398957418" MODIFIED="1771809112001" TEXT="die Store-Implementierung hat jetzt eine Back-Referenz"/>
<node CREATED="1771809113111" ID="ID_300180321" MODIFIED="1771809161169">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
in der einfachsten Form mu&#223; der Aufrufer herumtricksen,
@ -114422,8 +114481,7 @@ StM_bind(Builder&lt;R1&gt; b1, Extension&lt;R1,R2&gt; extension)
weil die Policy mit <b>drei Lambdas<i>&#160;</i></b>konfiguriert wird...
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<icon BUILTIN="smily_bad"/>
</node>
</node>
@ -114435,29 +114493,23 @@ StM_bind(Builder&lt;R1&gt; b1, Extension&lt;R1,R2&gt; extension)
<icon BUILTIN="idea"/>
<node CREATED="1771857680890" ID="ID_1386032779" MODIFIED="1771857785144" TEXT="wir brauchen eine Setup-Hilfsklasse, um die Backend-Implementierungen zu erzeugen">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
Das ist so vorgegeben durch das Design von BufferProviderSetup (weil ich damit die konkreten Backend-Klassen komplett aus dem Basis-Interface BufferProvider heraushalte)
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1771857787371" ID="ID_1223489355" MODIFIED="1771857849302" TEXT="diese brauchen tats&#xe4;chlich nur w&#xe4;hrend der Dauer des Konstruktur-Aufrufs">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
BufferProviderSetup ruft aus dem Rumpf ihres Konstruktors zwei Setup-Methoden auf; das wars dann, danach wird die Setup-Klasse nicht mehr gebraucht
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1771857855938" ID="ID_468915370" MODIFIED="1771857872747" TEXT="diese Setup-Klasse kann ein Dekorator &#xfc;ber die Policy sein">
<icon BUILTIN="idea"/>
@ -114468,20 +114520,15 @@ StM_bind(Builder&lt;R1&gt; b1, Extension&lt;R1,R2&gt; extension)
<node CREATED="1771857921329" ID="ID_233147109" MODIFIED="1771857966470" TEXT="&#x27f9; also kann die Policy letztlich dorthin geschoben werden, als Mix-in (private Basisklasse)"/>
<node CREATED="1771857999246" ID="ID_142868114" MODIFIED="1771858675941">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
<b><u>Hier wird's dreist</u></b>: Policy-Base per move aus der Setup-Hilfsklasse herausschieben !!!
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
Und zwar, noch dreister, <i>w&#228;hrend</i>&#160;diese Setup-Klasse grade aufgerufen wird, um die Backends zu konfigurieren. Effektiv f&#252;hrt dieser Aufruf dazu, da&#223; die Setup-Klasse ihre eigenen &#187;Eingeweide&#171; ausweidet und weitergibt. <i><font color="#770d2c">Kein Problem, sie stirbt ja sowiso am Ende des Aufrufts. </font></i>
@ -114493,9 +114540,101 @@ StM_bind(Builder&lt;R1&gt; b1, Extension&lt;R1,R2&gt; extension)
Noch genauer betrachtet: eine RValue-Referenz ist ja erst mal nur ein &#187;Angebot&#171;: sie macht etwas f&#252;r move-Konstukrion (oder Zuweisung) zug&#228;nglich. <i>Was konkret beim &#187;move&#171; passiert</i>, legt der Konsument fest. Hier ist der Konsument aber tats&#228;chlich nur an der Basis-Klasse interessiert, d.h. er konstruiert eine Kopie der Basisklasse per move-Konstruktur. Und wiederum, was dabei passiert, bestimmen die Member in dieser Policy. Erwartungsgem&#228;&#223; sind das Lambdas, und die k&#246;nnten obtional tats&#228;chlich etwas binden, was dann per Move-Konstruktion verschoben wird (z.B. einenen unique-PImpl). Sehr sch&#246;n, denn Lambdas werden erst aktiv, wenn sie aufgerufen werden. Werden sie aber nicht, w&#228;hrend der Konstruktion. Zusammengefa&#223;t: <i>solange nicht der Konstruktionsvorgang selbst die Callbacks aufruft,</i>&#160;ist das alles wasserdicht. Die einzige (langfristige) Gefahr besteht darin, da&#223; ein &#252;bereifriger Linter/Compiler in Zukunft hier einen use-after-delete markiert, ohne tats&#228;chlich zu belegen, da&#223; der use auftritt.
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node COLOR="#338800" CREATED="1771858723755" ID="ID_937919882" MODIFIED="1771893682325" TEXT="Test: funktioniert">
<linktarget COLOR="#5ba4a3" DESTINATION="ID_937919882" ENDARROW="Default" ENDINCLINATION="-525;70;" ID="Arrow_ID_487502478" SOURCE="ID_292154472" STARTARROW="None" STARTINCLINATION="111;5;"/>
<icon BUILTIN="button_ok"/>
</node>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1771893568172" ID="ID_465309080" MODIFIED="1771893720003" TEXT="Zweiter Teil der Aufgabe: named-Lambda-Tupel ">
<icon BUILTIN="pencil"/>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1771893733865" ID="ID_60984394" MODIFIED="1771893764443" TEXT="ich m&#xf6;chte ALLES">
<icon BUILTIN="yes"/>
<node CREATED="1771893742904" ID="ID_921712765" MODIFIED="1771893750874" TEXT="einen meta-konfigurierbaren Builder"/>
<node CREATED="1771893769036" ID="ID_434009909" MODIFIED="1771893878715" TEXT="einzelne Layer mit einem &#x3bb;-Attribut von generischem Typ &#x27f9; MACRO"/>
<node CREATED="1771893845362" ID="ID_112946042" MODIFIED="1771893872082" TEXT="UND das &#x3bb; soll aber noch lokal gegeben werden k&#xf6;nnen &#x27f9; Kontext-Binding"/>
</node>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1771893896510" ID="ID_771196335" MODIFIED="1771895013673" TEXT="Ideen sammeln">
<icon BUILTIN="idea"/>
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1771893905186" ID="ID_1241420221" MODIFIED="1771893946302" TEXT="nicht klar ob es geht .... die Teile passen noch nicht recht zusammen (in meinem Kopf)">
<icon BUILTIN="messagebox_warning"/>
</node>
<node CREATED="1771893948001" ID="ID_898834553" MODIFIED="1771894995111" TEXT="einen Builder, der per Macro generierte nested-Classes zusammenh&#xe4;ngt">
<icon BUILTIN="full-1"/>
</node>
<node CREATED="1771893977920" ID="ID_327025336" MODIFIED="1771894999397" TEXT="ein Tuple mit den Lambdas, und einen named accessor-Layer dar&#xfc;ber (per Macro)">
<icon BUILTIN="full-2"/>
</node>
<node CREATED="1771894975460" ID="ID_1705020209" MODIFIED="1771895021404" TEXT="m&#xf6;glicherweise zusammenbinden per Typ-Sequenz-Applikation">
<icon BUILTIN="full-3"/>
</node>
</node>
<node BACKGROUND_COLOR="#fafe99" COLOR="#fa002a" CREATED="1772064586741" ID="ID_1333957843" MODIFIED="1772064712945" TEXT="das Problem ist vor allem syntaktischer Natur">
<icon BUILTIN="broken-line"/>
<node CREATED="1772064634844" ID="ID_1613330952" MODIFIED="1772064634844" TEXT="Typen m&#xfc;ssen deklariert werden"/>
<node CREATED="1772064649488" ID="ID_806205089" MODIFIED="1772064649488" TEXT="Instanzen m&#xfc;ssen definiert werden"/>
<node CREATED="1772064692061" ID="ID_1021370131" MODIFIED="1772064692061" TEXT="Ich will aber nur EIN Macro pro &#xbb;Layer&#xab;">
<icon BUILTIN="yes"/>
<node CREATED="1772064787000" ID="ID_1407594054" MODIFIED="1772064921190" TEXT="eigentlich kann man in C++ keine Typen &#x201e;inline&#x201c; (in einem Ausdruck) deklarieren">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
Das hei&#223;t: ich kann zwar eine Instanz einens anonymen Typs <i>definieren</i>&#160; &#8212; aber das ist dann kein Ausdruck, und kann schon gleich garnicht ein Template sein
</p>
</body>
</html></richcontent>
</node>
<node CREATED="1772064936748" ID="ID_1768539918" MODIFIED="1772064936748" TEXT="aber ich kann einen lokalen Typ aus einer Funktion heraus &#xbb;abwerfen&#xab;">
<icon BUILTIN="idea"/>
</node>
<node CREATED="1772064976990" ID="ID_1522870120" MODIFIED="1772064976990" TEXT="also einen Spalt breit ist die T&#xfc;r offen &#x2014; aber ich bekomme meinen Fu&#xdf; nicht rein...">
<icon BUILTIN="smiley-oh"/>
</node>
<node CREATED="1772065106974" ID="ID_9798987" MODIFIED="1772065141875" TEXT="Ha! C++20 &#x27f9; Lambdas k&#xf6;nnen Template-Parameter haben">
<icon BUILTIN="forward"/>
<node CREATED="1772065144931" ID="ID_1572938824" MODIFIED="1772065185213">
<richcontent TYPE="NODE"><html>
<head/>
<body>
<p>
damit <b>mu&#223;</b>&#160;es gehen
</p>
</body>
</html></richcontent>
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
ich bekomme alle Typen in die Hand und kann damit eine neue Struct generieren
</p>
</body>
</html></richcontent>
</node>
<node COLOR="#bf045e" CREATED="1772065089849" ID="ID_543012886" MODIFIED="1772065208074" TEXT="bekomme es aber seit gestern nicht in den Griff">
<font ITALIC="true" NAME="SansSerif" SIZE="12"/>
</node>
</node>
<node CREATED="1772065296622" ID="ID_1991045470" MODIFIED="1772065345149" TEXT="Ha&#xb2; : kann in C++20 den &#xbb;self&#xab;-Type in einer Member-Funktion abgreifen">
<icon BUILTIN="forward"/>
</node>
<node CREATED="1772065459825" ID="ID_1413726983" MODIFIED="1772065483778" TEXT="&#x27f9; damit kann ich alles in einen einzigen Member-Funktions-Aufruf packen"/>
</node>
<node CREATED="1772065364753" ID="ID_1128216728" MODIFIED="1772065409930" TEXT="Das waren jetzt zwei Tage Knobelei">
<icon BUILTIN="smiley-neutral"/>
</node>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1772065498436" ID="ID_1029922863" MODIFIED="1772066452619" TEXT="Neues generisches Feature: PropBuilder">
<arrowlink COLOR="#2b57f9" DESTINATION="ID_1072763198" ENDARROW="Default" ENDINCLINATION="2965;140;" ID="Arrow_ID_198810341" STARTARROW="None" STARTINCLINATION="-2090;136;"/>
<icon BUILTIN="pencil"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1772066464826" ID="ID_534123438" MODIFIED="1772066473089" TEXT="dokumentiert in PropBuilder_test">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1772066474417" ID="ID_1404760011" MODIFIED="1772066498094" TEXT="dort auch direkt diesen use-Case demonstrieren">
<icon BUILTIN="yes"/>
</node>
</node>
</node>