Invocation: attempt to rely on the C++ ''tuple protocol''

Seems like low hanging fruit and would especially allow to use
those storage blocks with ''structural bindings''

Providing the necessary specialisations for `std::get` however turns out to be difficult;
the compiler insists on picking the direct tuple specialisation, since std::tuple is a
protected base class; yet still surprising -- I was under the impression
that the direct overload should be the closest match
This commit is contained in:
Fischlurch 2024-12-11 21:01:25 +01:00
parent 22f4b9dd7e
commit f2f321a3b8
3 changed files with 177 additions and 57 deletions

View file

@ -77,7 +77,9 @@ namespace lib {
: protected StorageLoc
, std::tuple<DATA...>
{
using std::tuple<DATA...>::tuple;
using Tuple = std::tuple<DATA...>;
using Tuple::tuple;
template<typename SPEC>
void linkInto (HeteroData<SPEC>&);
@ -129,7 +131,7 @@ namespace lib {
template<size_t slot>
Elm_t<slot>&
get()
get() noexcept
{
static_assert (slot < size(), "HeteroData access index beyond defined data");
if constexpr (slot < localSiz)
@ -138,6 +140,13 @@ namespace lib {
return accessTail().template get<slot-localSiz>();
}
template<size_t slot>
Elm_t<slot> const&
get() const noexcept
{
return const_cast<HeteroData*>(this)->get<slot>();
}
template<size_t slot>
struct Accessor
{
@ -251,4 +260,75 @@ namespace lib {
}
} // namespace lib
namespace std {// Specialisation to support C++ »Tuple Protocol«
/** determine compile-time fixed size of a HeteroData */
template<typename...DATA>
struct tuple_size<lib::HeteroData<DATA...> >
: std::integral_constant<std::size_t, lib::HeteroData<DATA...>::size()>
{ };
/** expose the type of the I-th element of a HeteroData chain */
template<size_t I, typename...DATA>
struct tuple_element<I, lib::HeteroData<DATA...> >
{
using type = typename lib::HeteroData<DATA...>::template Elm_t<I>;
};
template<size_t I>
struct tuple_element<I, lib::HeteroData<lib::meta::NullType> >
{
static_assert ("accessing element-type of an empty HeteroData block");
};
/** access by reference to the I-th data value held in a HeteroData chain */
template<size_t I, typename...DATA>
constexpr tuple_element_t<I, lib::HeteroData<DATA...>>&
get (lib::HeteroData<DATA...> & heDa) noexcept
{
return heDa.template get<I>();
}
template<size_t I, typename...DATA>
constexpr tuple_element_t<I, lib::HeteroData<DATA...>> const&
get (lib::HeteroData<DATA...> const& heDa) noexcept
{
return heDa.template get<I>();
}
template<size_t I, typename...DATA>
constexpr tuple_element_t<I, lib::HeteroData<DATA...>>&&
get (lib::HeteroData<DATA...>&& heDa) noexcept
{
using ElmType = tuple_element_t<I, lib::HeteroData<DATA...>>;
return forward<ElmType&&> (heDa.template get<I>());
}
template<std::size_t I, typename...DATA>
constexpr std::tuple_element_t<I, lib::HeteroData<DATA...>> const &&
get (lib::HeteroData<DATA...> const && heDa) noexcept
{
using ElmType = tuple_element_t<I, lib::HeteroData<DATA...>>;
return forward<ElmType const&&> (heDa.template get<I>());
}
/** determine compile-time fixed size of a StorageFrame */
template<size_t seg, typename...DATA>
struct tuple_size<lib::StorageFrame<seg,DATA...> >
: std::tuple_size<typename lib::StorageFrame<seg,DATA...>::Tuple>
{ };
/** delegate to the type access of a StorageFrame's underlying tuple */
template<size_t I, size_t seg, typename...DATA>
struct tuple_element<I, lib::StorageFrame<seg,DATA...> >
: std::tuple_element<I, typename lib::StorageFrame<seg,DATA...>::Tuple>
{ };
/** delegate to the element data access of a StorageFrame's underlying tuple */
// template<size_t I, size_t seg, typename...DATA>
// std::tuple_element_t<I, lib::StorageFrame<seg,DATA...>>&
// get (lib::StorageFrame<seg,DATA...>& block)
// {
// return std::get
// };
}// namespace std
#endif /*LIB_HETERO_DATA_H*/

View file

@ -74,6 +74,10 @@ namespace test{
CHECK (0.0 == b2.get<1>());
b2.get<1>() = 3.14;
CHECK (3.14 == b2.get<1>());
CHECK (2 == std::tuple_size_v<Block1::NewFrame::Tuple>); // referring to the embedded tuple type
CHECK (2 == std::tuple_size_v<Block1::NewFrame>); // StorageFrame itself complies to the C++ tuple protocol
CHECK (2 == std::tuple_size_v<Block1>); // likewise for the complete HeteroData Chain
}
@ -92,7 +96,8 @@ namespace test{
auto b2 = Constructor::build (1.61, "Φ");
b2.linkInto(b1);
auto& chain2 = reinterpret_cast<Constructor::ChainType&> (b1);
using Chain2 = Constructor::ChainType;
Chain2& chain2 = reinterpret_cast<Chain2&> (b1);
CHECK (b1.size() == 1);
CHECK (chain2.size() == 3);
@ -109,6 +114,28 @@ namespace test{
CHECK (isSameObject (chain2.get<0>() ,b1.get<0>()));
CHECK (isSameObject (chain2.get<2>() ,std::get<1>(b2)));
CHECK (1 == std::tuple_size_v<Block1::NewFrame::Tuple>); // referring to the embedded tuple type
CHECK (1 == std::tuple_size_v<Block1::NewFrame>);
CHECK (1 == std::tuple_size_v<Block1>);
CHECK (2 == std::tuple_size_v<Block2::Tuple>); // referring to the embedded tuple type
CHECK (2 == std::tuple_size_v<Block2>);
CHECK (3 == std::tuple_size_v<Chain2>);
CHECK ((showType<std::tuple_element_t<0, Chain2>>() == "uint"_expect));
CHECK ((showType<std::tuple_element_t<1, Chain2>>() == "double"_expect));
CHECK ((showType<std::tuple_element_t<2, Chain2>>() == "string"_expect));
CHECK ((showType<std::tuple_element_t<0, Block2>>() == "double"_expect));
CHECK ((showType<std::tuple_element_t<1, Block2>>() == "string"_expect));
CHECK (std::get<0> (chain2) == 42);
// CHECK (std::get<1> (chain2) == "1.618034"_expect); ////////////////////////////TODO somehow the overload for std::tuple takes precedence here
// CHECK (std::get<2> (chain2) == "Φ"_expect);
CHECK (std::get<0> (b2) == "1.618034"_expect);
CHECK (std::get<1> (b2) == "Φ"_expect);
}
};

View file

@ -22359,9 +22359,7 @@
<node CREATED="1480121069464" ID="ID_1707876611" MODIFIED="1557498707225" TEXT="Operationen mitlesen"/>
<node CREATED="1480121074703" ID="ID_1346494338" MODIFIED="1576282358087" TEXT="Notifikations-Schnittstelle">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
...und der Dekorator w&#252;rde die beobachteten Operationen
@ -22914,9 +22912,7 @@
<node CREATED="1480778652819" ID="ID_1335924975" MODIFIED="1518487921082" TEXT="Checks und Verzweigungen im Code"/>
<node CREATED="1480778689191" ID="ID_45333997" MODIFIED="1576282358078" TEXT="Pflanzt sich auf die Nutzer fort">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
..hier das Widget, das ebenfalls
@ -23278,9 +23274,7 @@
</node>
<node CREATED="1666484788093" ID="ID_1631269506" MODIFIED="1666485263714" TEXT="DisplayFrame ist RelativeCanvasHook">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
...und das nutzt explizit die bestehenden Indirektionen aus...
@ -23887,9 +23881,7 @@
</node>
<node CREATED="1565272972192" ID="ID_1941959455" MODIFIED="1565272983357">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
Zeichencode <i>verwendet</i>&#160;diesen
@ -25159,9 +25151,7 @@
<node CREATED="1612024666745" ID="ID_1549387212" MODIFIED="1612024677267" TEXT="ist das jetzt &quot;unter den Teppich&quot;....?"/>
<node CREATED="1612024678071" ID="ID_714221228" MODIFIED="1612024832503" TEXT="ja und nein. Es ist sogar korrekt so">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
Aber der Punkt ist, nach der reinen Lehre sollte eine solche Funktion eine Options-Monade zur&#252;ckgeben. Aber ich wollte stattdessen den guten alten Fallback-Wert. Wenn man <i>das </i>erst mal akzeptiert, dann mu&#223; &quot;man&quot; verdammt noch einmal auch die Wertebereiche ernst nehmen
@ -27264,9 +27254,7 @@
</node>
<node CREATED="1677284865478" ID="ID_1467561719" MODIFIED="1677285153626" TEXT="Contstraints und Expressions sind noch nicht vollst&#xe4;ndig entwickelt">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
Eine Funktion, um eine Linie gem&#228;&#223; Proportion zu teilen, wird zwar oft gew&#252;nscht, ist aber derzeit (2022) noch in Entwicklung. Daher kann man im Moment nur eine feste Basisl&#228;nge als benannter Constraint vorgeben, und dann andere L&#228;ngen per Expression <font face="Monospaced" color="#1418c4">=Constraint.basis * (1+sqrt(5)/2</font>&#160;&#160;daran binden. Au&#223;erdem kann man solche Expressions zwar einmal initial eigeben, dann aber nur noch &#252;ber das XML editieren.
@ -30769,9 +30757,7 @@
<node CREATED="1561139829332" ID="ID_730637336" MODIFIED="1561139839310" TEXT="rein logisch geh&#xf6;rt das Profil ja schon hier her"/>
<node CREATED="1561139846988" ID="ID_565918975" MODIFIED="1561139974752" TEXT="aber der ganze Update-Mechanismus ist ein in sich abgekapselter Belang">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
und macht sich aber hier im Quellcode &#252;ber mehrere Funktionen verteilt breit,
@ -31806,9 +31792,7 @@
<node CREATED="1563035800955" ID="ID_1644301957" MODIFIED="1563035821169" TEXT="vmt. wegen dem Zeitpunkt, an dem der Pfad konstruiert wird"/>
<node CREATED="1563035791620" ID="ID_1471340967" MODIFIED="1563035834509" TEXT="unsch&#xf6;n, aber nicht relevant">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
nur der letzte Node wird gegen pseudo-Klassen gematcht
@ -35862,9 +35846,7 @@
<node CREATED="1477345906723" ID="ID_906895860" MODIFIED="1518487921084" TEXT="&quot;poor man&apos;s reflection&quot;"/>
<node CREATED="1477345939063" ID="ID_984848221" MODIFIED="1576282358035">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
realisiert Vererbung <i>zu fu&#223;</i>
@ -38007,9 +37989,7 @@
</node>
<node CREATED="1617377669085" ID="ID_1519120855" MODIFIED="1617377719039" TEXT="k&#xf6;nnte sp&#xe4;ter mal dynamsiche Auswahl nach Regeln sein (je nach Interaction-System)">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
Maus, Tastenk&#252;rzel, Stift, Hardware....
@ -39133,9 +39113,7 @@
</node>
<node COLOR="#338800" CREATED="1618691023427" ID="ID_1396949384" MODIFIED="1620922896886" TEXT="im Subject in Modell-Koordinaten transformieren">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
funktioniert... Zeit-Werte werden an der Konsole ausgegeben
@ -39516,9 +39494,7 @@
</node>
<node CREATED="1618499304789" ID="ID_636470529" MODIFIED="1618499325642" TEXT="das sollte besser durch Komposition gel&#xf6;st werden">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
Hierarchische Gesten-Controller
@ -39960,9 +39936,7 @@
</node>
<node CREATED="1666963512247" ID="ID_346536876" MODIFIED="1666964491444">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
Gestures
@ -40284,9 +40258,7 @@
<icon BUILTIN="help"/>
<node CREATED="1667155806980" ID="ID_250578278" MODIFIED="1667156041888">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
<b>FSecs</b>&#160;&#8793; <font face="Monospaced">boost::rational&lt;int64_t&gt;</font>
@ -87736,8 +87708,8 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1733766989087" ID="ID_1930665408" MODIFIED="1733868469882" TEXT="typsicheres compiletime-Overlay f&#xfc;r diese">
<icon BUILTIN="yes"/>
<node CREATED="1733767025290" ID="ID_741129447" MODIFIED="1733767039637" TEXT="daraus sollen die Accessoren erzeugbar sein"/>
<node CREATED="1733767103376" ID="ID_36453586" MODIFIED="1733767123425" TEXT="w&#xfc;nschenswert: factory-Funktionen f&#xfc;r bestimmte Storage-Bl&#xf6;cke"/>
<node CREATED="1733767103376" ID="ID_36453586" MODIFIED="1733928886422" TEXT="verkettete factory-Funktionen f&#xfc;r einzelne Storage-Bl&#xf6;cke"/>
<node CREATED="1733767025290" ID="ID_741129447" MODIFIED="1733928908892" TEXT="daraus sind Accessoren f&#xfc;r typsicheren Zugriff erzeugbar"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1733767147610" ID="ID_966052237" MODIFIED="1733868469882" TEXT="Zugriff auf einzelne Datenwerte per Accessor">
<icon BUILTIN="yes"/>
@ -87759,6 +87731,11 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</body>
</html></richcontent>
</node>
<node CREATED="1733928946819" ID="ID_1520880973" MODIFIED="1733928970066" TEXT="Grundlage ist ein strukturierter Typ, der die Block-Kette formal beschreibt"/>
<node CREATED="1733929379183" ID="ID_343414896" MODIFIED="1733929397922" TEXT="f&#xfc;r den Zugriff ist ein force-Cast in diesen Typ notwendig"/>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1733929398835" ID="ID_608606366" MODIFIED="1733929434625" TEXT="&#x27f9; nur der Weg &#xfc;ber Konstructor-Functor &#x27f6; Accessor ist sicher">
<icon BUILTIN="messagebox_warning"/>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1733799750738" ID="ID_532258033" MODIFIED="1733799768652" TEXT="Implementierung ausarbeiten">
@ -88030,8 +88007,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
eben weil wir im HeteroData-Chain selber (also dem TurnoutSystem) keine Metadaten unterbringen wollen &#10233; damit aber drehen sich alle Verifikationen dieser Typ-Struktor im Kreis, auch die size()-Funktion ist ja constexpr und analysiert wieder nur die Typ-Signatur; ob tats&#228;chlich die angebilchen Tupel-Felder im Speicher liegen kann man ohne RTTI nicht pr&#252;fen
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<icon BUILTIN="broken-line"/>
</node>
<node CREATED="1733836480541" ID="ID_1669530928" MODIFIED="1733872649665" TEXT="m&#xf6;glich und aber auch genau hinreichend w&#xe4;re jedoch ein Plausibilit&#xe4;ts-Check">
@ -88099,8 +88075,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
Daten-Record wird markiert mit Template-Parameter <b><font color="#5d0b0b" face="Monospaced"><u>seg</u></font></b>
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<richcontent TYPE="NOTE"><html>
<head/>
<body>
@ -88108,8 +88083,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
Wir z&#228;hlen ganz einfach die Nummer der Segmente mit, die wir anh&#228;ngen. Der erste Storage-Frame wird mit 0 initialisiert, und jedesmal, wenn ein erweiterter Typ konstruiert wird, erh&#246;ht sich diese Segment-Nummer
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1733837352697" ID="ID_1648354214" MODIFIED="1733837394585" TEXT="der bekommt eine Methode linkInto(HeteroData&amp;)"/>
<node CREATED="1733837395855" ID="ID_458018454" MODIFIED="1733883384957" TEXT="und delegiert in eine Funktion checkedTraversal(len, StorageLoc*)">
@ -88173,8 +88147,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
denn die weitere Verkn&#252;pfung wird explizit in einem zweiten Schritt gemacht, und das ist gut so
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<icon BUILTIN="idea"/>
</node>
<node COLOR="#338800" CREATED="1733851922106" ID="ID_421589500" MODIFIED="1733852040199" TEXT="somit reduziert sich die Implementierung auf einige Typedefs">
@ -88267,8 +88240,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
Nicht von der Typ-Info blenden lassen: die wird hier nur einmal im Kreis geschoben. Wir nehmen zwar irgend ein Front-End, m&#252;ssen das aber dann auf den vollen Chain-Typ force-casten (denn das ist das Grundkonzept, das uns Zugriff ohne virtuelle Funktionen erm&#246;glicht). Die einzige <b>runtime-Info</b>&#160; ist die Anzahl der &#187;Hops&#171; zum n&#228;chsten Segment, d.h. die Anzahl an Pointern, denen wir &#252;ber die Kette folgen. <i>Diese</i>&#160;mu&#223; mit der Anzahl der Komponenten im Typ zusammenpassen
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<icon BUILTIN="clanbomber"/>
</node>
<node COLOR="#338800" CREATED="1733872865111" ID="ID_922628201" MODIFIED="1733883463203" TEXT="ja dannn.... diese Funktionalit&#xe4;t kann genauso gut in einer freien Funktion stehen">
@ -88286,6 +88258,47 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
</node>
</node>
<node CREATED="1733946409913" ID="ID_766490392" MODIFIED="1733946421787" TEXT="C++ &#xbb;Tuple-Protocol&#xab;">
<node CREATED="1733946436854" ID="ID_1279626510" LINK="https://en.cppreference.com/w/cpp/utility/tuple/tuple-like" MODIFIED="1733946454891" TEXT="exposition-only concept &#xbb;tuple-like&#xab;"/>
<node CREATED="1733946456354" ID="ID_1814813251" MODIFIED="1733946486589">
<richcontent TYPE="NODE"><html>
<head/>
<body>
<p>
sollte <i>im Prinzip ja</i>&#160;einfach sein...
</p>
</body>
</html>
</richcontent>
<icon BUILTIN="ksmiletris"/>
</node>
<node COLOR="#338800" CREATED="1733946491575" ID="ID_1678748014" MODIFIED="1733946694559" TEXT="tuple_len">
<icon BUILTIN="button_ok"/>
<node CREATED="1733946498997" ID="ID_1699016974" MODIFIED="1733946531556" TEXT="l&#xe4;&#xdf;t sich direkt auf std::integral_constant zur&#xfc;ckf&#xfc;hren"/>
<node CREATED="1733946533117" ID="ID_1111331922" MODIFIED="1733946542098" TEXT="implementiert f&#xfc;r beliebige HeteroData"/>
<node CREATED="1733946542902" ID="ID_139021842" MODIFIED="1733946550153" TEXT="auch gleich implementiert f&#xfc;r StorageFrame"/>
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1733946551317" ID="ID_555431810" MODIFIED="1733946586881" TEXT="Beachte: Template-Spezialisierungen greifen nicht f&#xfc;r abgeleitete Klassen">
<icon BUILTIN="messagebox_warning"/>
<node CREATED="1733946590316" ID="ID_626664530" MODIFIED="1733946600694" TEXT="nach zwemal Nachdenken: ist auch sinnvoll"/>
<node CREATED="1733946621803" ID="ID_333291639" MODIFIED="1733946638813" TEXT="man w&#xfc;rde dann statisch auf der Basis-Klasse operieren"/>
<node CREATED="1733946640233" ID="ID_1111919688" MODIFIED="1733946648532" TEXT="es g&#xe4;be keine Erweiterungspunkte"/>
</node>
</node>
<node COLOR="#338800" CREATED="1733946654687" ID="ID_1461671576" MODIFIED="1733946693210" TEXT="tuple_element">
<icon BUILTIN="button_ok"/>
<node CREATED="1733946659490" ID="ID_668547104" MODIFIED="1733946664745" TEXT="ebenso"/>
<node CREATED="1733946672501" ID="ID_235658194" MODIFIED="1733946690942" TEXT="static_assert f&#xfc;r den Fall eines HetroData&lt;NullType&gt;"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1733946695947" ID="ID_1366785972" MODIFIED="1733946699508" TEXT="std::get">
<icon BUILTIN="flag-yellow"/>
<node CREATED="1733946700889" ID="ID_729503649" MODIFIED="1733946713139" TEXT="im Prinzip klar ... aber m&#xfc;hsam"/>
<node BACKGROUND_COLOR="#fafe99" COLOR="#fa002a" CREATED="1733946713943" ID="ID_1873953043" MODIFIED="1733946732567" TEXT="funktioniert nicht .... Compiler nimmt Overload f&#xfc;r std::tuple">
<icon BUILTIN="broken-line"/>
<node CREATED="1733946765784" ID="ID_1087783493" MODIFIED="1733946780713" TEXT="das bekannte Problem mit Funktion Overloads"/>
<node CREATED="1733946781559" ID="ID_1787763968" MODIFIED="1733946793699" TEXT="es ist dann eben keine partielle Template-Spezialisierung"/>
</node>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1733883489383" ID="ID_155186903" MODIFIED="1733883516460" TEXT="HeteroData_test">
<icon BUILTIN="pencil"/>