From f2f321a3b86428be2cf289b3751e12e5a6a9e3b0 Mon Sep 17 00:00:00 2001
From: Ichthyostega
Date: Wed, 11 Dec 2024 21:01:25 +0100
Subject: [PATCH] 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
---
src/lib/hetero-data.hpp | 84 +++++++++++++++++++-
tests/library/hetero-data-test.cpp | 29 ++++++-
wiki/thinkPad.ichthyo.mm | 121 ++++++++++++++++-------------
3 files changed, 177 insertions(+), 57 deletions(-)
diff --git a/src/lib/hetero-data.hpp b/src/lib/hetero-data.hpp
index 168b7b909..d6e84c502 100644
--- a/src/lib/hetero-data.hpp
+++ b/src/lib/hetero-data.hpp
@@ -77,7 +77,9 @@ namespace lib {
: protected StorageLoc
, std::tuple
{
- using std::tuple::tuple;
+ using Tuple = std::tuple;
+
+ using Tuple::tuple;
template
void linkInto (HeteroData&);
@@ -129,7 +131,7 @@ namespace lib {
template
Elm_t&
- 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();
}
+ template
+ Elm_t const&
+ get() const noexcept
+ {
+ return const_cast(this)->get();
+ }
+
template
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
+ struct tuple_size >
+ : std::integral_constant::size()>
+ { };
+
+ /** expose the type of the I-th element of a HeteroData chain */
+ template
+ struct tuple_element >
+ {
+ using type = typename lib::HeteroData::template Elm_t;
+ };
+ template
+ struct tuple_element >
+ {
+ 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
+ constexpr tuple_element_t>&
+ get (lib::HeteroData & heDa) noexcept
+ {
+ return heDa.template get();
+ }
+ template
+ constexpr tuple_element_t> const&
+ get (lib::HeteroData const& heDa) noexcept
+ {
+ return heDa.template get();
+ }
+ template
+ constexpr tuple_element_t>&&
+ get (lib::HeteroData&& heDa) noexcept
+ {
+ using ElmType = tuple_element_t>;
+ return forward (heDa.template get());
+ }
+ template
+ constexpr std::tuple_element_t> const &&
+ get (lib::HeteroData const && heDa) noexcept
+ {
+ using ElmType = tuple_element_t>;
+ return forward (heDa.template get());
+ }
+
+
+ /** determine compile-time fixed size of a StorageFrame */
+ template
+ struct tuple_size >
+ : std::tuple_size::Tuple>
+ { };
+
+ /** delegate to the type access of a StorageFrame's underlying tuple */
+ template
+ struct tuple_element >
+ : std::tuple_element::Tuple>
+ { };
+
+ /** delegate to the element data access of a StorageFrame's underlying tuple */
+// template
+// std::tuple_element_t>&
+// get (lib::StorageFrame& block)
+// {
+// return std::get
+// };
+
+}// namespace std
#endif /*LIB_HETERO_DATA_H*/
diff --git a/tests/library/hetero-data-test.cpp b/tests/library/hetero-data-test.cpp
index 75fd5368e..7f25b32e5 100644
--- a/tests/library/hetero-data-test.cpp
+++ b/tests/library/hetero-data-test.cpp
@@ -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); // referring to the embedded tuple type
+ CHECK (2 == std::tuple_size_v); // StorageFrame itself complies to the C++ tuple protocol
+ CHECK (2 == std::tuple_size_v); // 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 (b1);
+ using Chain2 = Constructor::ChainType;
+ Chain2& chain2 = reinterpret_cast (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); // referring to the embedded tuple type
+ CHECK (1 == std::tuple_size_v);
+ CHECK (1 == std::tuple_size_v);
+
+ CHECK (2 == std::tuple_size_v); // referring to the embedded tuple type
+ CHECK (2 == std::tuple_size_v);
+
+ CHECK (3 == std::tuple_size_v);
+ CHECK ((showType>() == "uint"_expect));
+ CHECK ((showType>() == "double"_expect));
+ CHECK ((showType>() == "string"_expect));
+
+ CHECK ((showType>() == "double"_expect));
+ CHECK ((showType>() == "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);
}
};
diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm
index 4c463b80d..cd0699a59 100644
--- a/wiki/thinkPad.ichthyo.mm
+++ b/wiki/thinkPad.ichthyo.mm
@@ -22359,9 +22359,7 @@
-
-
-
+
...und der Dekorator würde die beobachteten Operationen
@@ -22914,9 +22912,7 @@
-
-
-
+
..hier das Widget, das ebenfalls
@@ -23278,9 +23274,7 @@
-
-
-
+
...und das nutzt explizit die bestehenden Indirektionen aus...
@@ -23887,9 +23881,7 @@
-
-
-
+
Zeichencode verwendet diesen
@@ -25159,9 +25151,7 @@
-
-
-
+
Aber der Punkt ist, nach der reinen Lehre sollte eine solche Funktion eine Options-Monade zurückgeben. Aber ich wollte stattdessen den guten alten Fallback-Wert. Wenn man das erst mal akzeptiert, dann muß "man" verdammt noch einmal auch die Wertebereiche ernst nehmen
@@ -27264,9 +27254,7 @@
-
-
-
+
Eine Funktion, um eine Linie gemäß Proportion zu teilen, wird zwar oft gewünscht, ist aber derzeit (2022) noch in Entwicklung. Daher kann man im Moment nur eine feste Basislänge als benannter Constraint vorgeben, und dann andere Längen per Expression =Constraint.basis * (1+sqrt(5)/2 daran binden. Außerdem kann man solche Expressions zwar einmal initial eigeben, dann aber nur noch über das XML editieren.
@@ -30769,9 +30757,7 @@
-
-
-
+
und macht sich aber hier im Quellcode über mehrere Funktionen verteilt breit,
@@ -31806,9 +31792,7 @@
-
-
-
+
nur der letzte Node wird gegen pseudo-Klassen gematcht
@@ -35862,9 +35846,7 @@
-
-
-
+
realisiert Vererbung zu fuß
@@ -38007,9 +37989,7 @@
-
-
-
+
Maus, Tastenkürzel, Stift, Hardware....
@@ -39133,9 +39113,7 @@
-
-
-
+
funktioniert... Zeit-Werte werden an der Konsole ausgegeben
@@ -39516,9 +39494,7 @@
-
-
-
+
Hierarchische Gesten-Controller
@@ -39960,9 +39936,7 @@
-
-
-
+
Gestures
@@ -40284,9 +40258,7 @@
-
-
-
+
FSecs ≙ boost::rational<int64_t>
@@ -87736,8 +87708,8 @@ Date: Thu Apr 20 18:53:17 2023 +0200
-
-
+
+
@@ -87759,6 +87731,11 @@ Date: Thu Apr 20 18:53:17 2023 +0200
+
+
+
+
+
@@ -88030,8 +88007,7 @@ Date: Thu Apr 20 18:53:17 2023 +0200
eben weil wir im HeteroData-Chain selber (also dem TurnoutSystem) keine Metadaten unterbringen wollen ⟹ 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ächlich die angebilchen Tupel-Felder im Speicher liegen kann man ohne RTTI nicht prüfen
-
-
+
@@ -88099,8 +88075,7 @@ Date: Thu Apr 20 18:53:17 2023 +0200
Daten-Record wird markiert mit Template-Parameter seg
-
-
+
@@ -88108,8 +88083,7 @@ Date: Thu Apr 20 18:53:17 2023 +0200
Wir zählen ganz einfach die Nummer der Segmente mit, die wir anhängen. Der erste Storage-Frame wird mit 0 initialisiert, und jedesmal, wenn ein erweiterter Typ konstruiert wird, erhöht sich diese Segment-Nummer
-
-
+
@@ -88173,8 +88147,7 @@ Date: Thu Apr 20 18:53:17 2023 +0200
denn die weitere Verknüpfung wird explizit in einem zweiten Schritt gemacht, und das ist gut so
-
-
+
@@ -88267,8 +88240,7 @@ Date: Thu Apr 20 18:53:17 2023 +0200
Nicht von der Typ-Info blenden lassen: die wird hier nur einmal im Kreis geschoben. Wir nehmen zwar irgend ein Front-End, müssen das aber dann auf den vollen Chain-Typ force-casten (denn das ist das Grundkonzept, das uns Zugriff ohne virtuelle Funktionen ermöglicht). Die einzige runtime-Info ist die Anzahl der »Hops« zum nächsten Segment, d.h. die Anzahl an Pointern, denen wir über die Kette folgen. Diese muß mit der Anzahl der Komponenten im Typ zusammenpassen
-
-
+
@@ -88286,6 +88258,47 @@ Date: Thu Apr 20 18:53:17 2023 +0200
+
+
+
+
+
+
+
+ sollte im Prinzip ja einfach sein...
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+