diff --git a/research/try.cpp b/research/try.cpp
index 5de8b837d..4eac80673 100644
--- a/research/try.cpp
+++ b/research/try.cpp
@@ -33,15 +33,7 @@ typedef unsigned int uint;
#include
#include
-using lib::test::showType;
-
-template
-string
-showTypes()
-{
- return "<" + ((showType()+",") + ... + ">");
-}
-
+using lib::test::showTypes;
using std::tuple;
struct B { };
@@ -104,18 +96,21 @@ main (int, char**)
using Het = lib::HeteroData;
Het h1;
SHOW_EXPR(getty(h1));
- SHOW_EXPR(std::get<1>(h1) = 5.5)
+// SHOW_EXPR(std::get<1>(h1) = 5.5)
+ SHOW_EXPR(h1.get<1>() = 5.5)
using Constructor = Het::Chain;
auto h2 = Constructor::build (true, "Ψ");
h2.linkInto(h1);
using Het2 = Constructor::ChainType;
- Het2& chain2 = reinterpret_cast (h1);
+ Het2& chain2 = Constructor::recast (h1);
SHOW_TYPE(Het2)
SHOW_EXPR(getty(chain2));
- SHOW_EXPR(std::get<1>(chain2))
+// SHOW_EXPR(std::get<1>(chain2))
// SHOW_EXPR(std::get<3>(chain2))
+ SHOW_EXPR(chain2.get<1>())
+ SHOW_EXPR(chain2.get<3>())
SHOW_EXPR(gritty<1>(chain2))
SHOW_EXPR(gritty<3>(chain2))
diff --git a/src/lib/hetero-data.hpp b/src/lib/hetero-data.hpp
index d6e84c502..574a6a3c6 100644
--- a/src/lib/hetero-data.hpp
+++ b/src/lib/hetero-data.hpp
@@ -29,6 +29,32 @@
** requires traversal in several steps, which, on the other hand, can be justified by a good
** cache locality of recently used stack frames, thereby avoiding heap allocations altogether.
**
+ ** # Usage
+ ** @warning it is essential to understand where actual storage resides!
+ ** A HeteroData chain is built-up gradually, starting with a front-block
+ ** - the front-block is usually placed at an _anchor location_ and populated with data
+ ** - retrieve a _chain constructor type_ from the _type_ of the front-block,
+ ** i.e `HeteroData::Chain`
+ ** - use this chain constructor to create a follow-up data block elsewhere
+ ** - need to link this data block explicitly into the front
+ ** - get _accessor types_ from the _chain constructor_
+ ** - use these to work with individual data elements _through the front-block._
+ ** @example
+ ** \code
+ ** using Front = lib::HeteroData;
+ ** auto h1 = Front::build (1,2.3);
+ ** using Cons1 = Front::Chain;
+ ** auto b2 = Cons1::build (true, "Ψ");
+ ** b2.linkInto(c1);
+ ** auto& [d1,d2,d3,d4] = Cons1::recast(h1);
+ ** CHECK (d1 == 1);
+ ** CHECK (d2 == 2.3);
+ ** CHECK (d3 == true);
+ ** CHECK (d4 == "Ψ");
+ ** Cons1::Accessor get4;
+ ** CHECK (get4(h1) == "Ψ");
+ ** \endcode
+ **
** @todo WIP-WIP this is the draft of a design sketch regarding the render node network,
** which seems to be still pretty much in flux as of 12/2024
** @see HeteroData_test
@@ -61,17 +87,23 @@
namespace lib {
/**
+ * A setup with chained data tuples residing in distributed storage.
+ * A HeteroData-chain is started from a front-end block and can later be
+ * extended by a linked list of further data blocks allocated elsewhere.
+ * @warning this is a low-level memory layout without storage management.
+ * @see HeteroData_test
*/
template
class HeteroData;
-
+ /** linked list of StorageFrame elements */
struct StorageLoc
: util::NonCopyable
{
StorageLoc* next{nullptr};
};
+ /** individual storage frame in a chain, holding a data tuple */
template
struct StorageFrame
: protected StorageLoc
@@ -86,6 +118,12 @@ namespace lib {
};
+ /**
+ * @internal implementation specialisation to manage a sublist of StorageFrame elements
+ * @tparam seg a type tag to mark the position of StorageFrame elements
+ * @tparam DATA tuple element types residing in the first segment
+ * @tparam TAIL recursive Loki-style type list to describe the rest of the chain
+ */
template
class HeteroData,TAIL>>
: StorageFrame
@@ -103,7 +141,7 @@ namespace lib {
template
using PickType = std::conditional_t, std::tuple_element
, typename _Tail::template PickType>;
-
+ // need to use this helper to prevent eager evaluation on Elm_t
_Tail&
accessTail()
{
@@ -114,7 +152,7 @@ namespace lib {
template
friend class HeteroData; ///< allow chained types to use recursive type definitions
- using Frame::Frame;
+ using Frame::Frame; ///< data elements shall be populated through the builder front-ends
public:
HeteroData() = default;
@@ -128,7 +166,7 @@ namespace lib {
template
using Elm_t = typename PickType::type;
-
+ /** access data elements within _complete chain_ by index pos */
template
Elm_t&
get() noexcept
@@ -174,6 +212,19 @@ namespace lib {
return {initArgs ...};
}
+ template
+ static ChainType&
+ recast (HeteroData& frontChain)
+ {
+ return reinterpret_cast (frontChain);
+ }
+ template
+ static ChainType&
+ recast (HeteroData const& frontChain)
+ {
+ return reinterpret_cast (frontChain);
+ }
+
template
using ChainExtension = typename ChainType::template Chain;
@@ -185,6 +236,9 @@ namespace lib {
};
};
+ /**
+ * @internal implementation specialisation to mark the end of a chain
+ */
template<>
class HeteroData
{
@@ -197,6 +251,12 @@ namespace lib {
using PickType = void;
};
+ /*************************************************************************//**
+ * @remark this is the front-end for regular usage
+ * - create and populate with the #build operation
+ * - data access with the `get` member function (inherited)
+ * - use `HeteroData::Chain` to build follow-up segments
+ */
template
class HeteroData
: public HeteroData, meta::NullType>>
@@ -248,6 +308,22 @@ namespace lib {
}
}
+
+ /**
+ * Attach a new storage frame at the end of an existing HeteroData-chain.
+ * @tparam seg the number of the separate data segment, must match target
+ * @param prefixChain with `seg - 1` existing chained tuple-segments
+ * @remark The core function actually to extend a chain with a new segment,
+ * which should have been built using a suitable nested `HeteroData::Chain`
+ * constructor type. Further segments can be defined working from there,
+ * since each such constructor in turn has a member type `ChainExtension`
+ * @note Always use this strongly typed extension and access path, to prevent
+ * out-of-bounds memory access. The actual HeteroData stores no run time
+ * type information, and thus a force-cast is necessary internally to
+ * access the follow-up data tuple frames. The typing, and especially
+ * the `seg` template parameter used to mark each StorageFrame is
+ * the only guard-rail provided, and ensures safe data access.
+ */
template
template
inline void
@@ -258,10 +334,12 @@ namespace lib {
ENSURE (lastLink == nullptr);
lastLink = this;
}
-
-} // namespace lib
+}// namespace lib
-namespace std {// Specialisation to support C++ »Tuple Protocol«
+
+
+
+namespace std { // Specialisation to support C++ »Tuple Protocol« and structured bindings.
/** determine compile-time fixed size of a HeteroData */
template
@@ -281,33 +359,9 @@ namespace std {// Specialisation to support C++ »Tuple Protocol«
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());
- }
+ // Note: deliberately NOT providing a free get function.
+ // Overload resolution would fail, since it attempts to instantiate std::get(tuple) as a candidate,
+ // which triggers an assertion failure when using an index valid only for the full chain, not the base tuple
/** determine compile-time fixed size of a StorageFrame */
@@ -321,14 +375,9 @@ namespace std {// Specialisation to support C++ »Tuple Protocol«
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
-// };
+
+ // no need to define an overload for std::get
+ // (other than a template specialisation, it will use base-type conversion to std::tuple on its argument;
}// namespace std
#endif /*LIB_HETERO_DATA_H*/
diff --git a/src/lib/test/test-helper.hpp b/src/lib/test/test-helper.hpp
index 4bc0ed053..0b14d164c 100644
--- a/src/lib/test/test-helper.hpp
+++ b/src/lib/test/test-helper.hpp
@@ -263,6 +263,13 @@ namespace test{
+ Case::postfix;
}
+ template
+ string
+ showTypes()
+ {
+ return "<| " + ((showType()+", ") + ... + "|>");
+ }
+
/** helper for investigating a variadic argument pack
* @remark you can pass arguments directly to this function,
diff --git a/tests/library/hetero-data-test.cpp b/tests/library/hetero-data-test.cpp
index 7f25b32e5..8e9e53126 100644
--- a/tests/library/hetero-data-test.cpp
+++ b/tests/library/hetero-data-test.cpp
@@ -49,11 +49,9 @@ namespace test{
virtual void
run (Arg)
{
-// seedRand();
-// checksum = 0;
-
verify_FrontBlock();
verify_ChainBlock();
+// verify_Accessors();
}
@@ -78,6 +76,11 @@ namespace test{
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
+
+ auto& [_,p] = b2; // can use structured bindings...
+ CHECK (p == 3.14);
+ p = 3.14159;
+ CHECK (3.14159 == b2.get<1>());
}
@@ -97,7 +100,7 @@ namespace test{
b2.linkInto(b1);
using Chain2 = Constructor::ChainType;
- Chain2& chain2 = reinterpret_cast (b1);
+ Chain2& chain2 = Constructor::recast (b1);
CHECK (b1.size() == 1);
CHECK (chain2.size() == 3);
@@ -130,12 +133,24 @@ namespace test{
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> (chain2) == "42"_expect); // std::tuple is inaccessible base of HeteroData
+ CHECK (std::get<0> (b2) == "1.618034"_expect);
+// CHECK (std::get<1> (chain2) == "1.618034"_expect); // does not compile due to range restriction for the base tuple
+ // (as such this is correct — yet prevents definition of a custom get-function)
+ auto& [u0] = b1;
+ CHECK (u0 == "42"_expect);
- CHECK (std::get<0> (b2) == "1.618034"_expect);
- CHECK (std::get<1> (b2) == "Φ"_expect);
+ auto& [v0,v1] = b2; // b2 is typed as StorageFrame and thus the tuple base is accessible
+ CHECK (v0 == "1.618034"_expect);
+ CHECK (v1 == "Φ"_expect);
+
+ auto& [x0,x1,x2] = chain2; // Note: structured binding on the fully typed chain uses the get-Member
+ CHECK (x0 == "42"_expect);
+ CHECK (x1 == "1.618034"_expect);
+ CHECK (x2 == "Φ"_expect);
+
+// auto& [z0,z1,z2,z3] = chain2; // Error: 4 names provided for structured binding, while HeteroData... decomposes into 3 elements
+// auto& [z0,z1,z2] = b1; // Error: HeteroData, NullType> >' decomposes into 1 element
}
};
diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm
index 5c557a2bf..0ce969fce 100644
--- a/wiki/thinkPad.ichthyo.mm
+++ b/wiki/thinkPad.ichthyo.mm
@@ -22967,9 +22967,7 @@
-
-
-
+
wenn alle Objekte wirklich auf partiell initialisierten Zustand vorbereitet sind,
@@ -23321,9 +23319,7 @@
-
-
-
+
Da im Clip die Anfrage an die Metrik-Übersetzung x-fach (bei jedem Fokus-Wechsel) erfolgt, dagegen das generelle re-Positionieren auf dem Canvas nur nach einem Strukturwechsel oder Zoom-Wechsel, wäre eine sehr einfache und effektive Optimierung, sich den DisplayMetric-Pointer im individuellen Clip eigens zu speichern. Das wäre allerings ein zusätzlicher »Slot« pro Clip! ... eine Variante mit einer zusätzlichen Pointer-Indirektion würde den DisplayMetric*-Pointer im CanvasHook speichern, und damit einmal pro Track (also wesentlich kleinerer Hebel)
@@ -23914,9 +23910,7 @@
-
-
-
+
...deutet darauf hin, daß in diesem Zusammenhang etwas schiefläuft
@@ -24630,9 +24624,7 @@
-
-
-
+
Ende 2022 habe ich dann doch noch das ElementBoxWidget gebaut. Nun verwenden Clips schon mal dieses, aber die ganze Logik der Clip-Anzeige ist auf später verschoben...
@@ -25887,9 +25879,7 @@
-
-
-
+
...und hält damit sein unterliegendes C-Objekt am Leben
@@ -28043,9 +28033,7 @@
-
-
-
+
...anders als bei den Einzel-Widgets im Timeline-Content, haben wir hier im Header nur ein StaveBracket-Widget pro Track
@@ -30535,9 +30523,7 @@
-
-
-
+
per DUMP-Print verifiziert: Maximalwert der Scrollbar (=hadj) ist identisch mit get_allocated_width
@@ -31787,9 +31773,7 @@
-
-
-
+
window.background box.vertical box[2/3].horizontal widget[2/2] widget paned.vertical widget box.vertical notebook[1/1].frame paned.horizontal.timeline-page box.vertical.timeline.timeline-body fork.timeline
@@ -34736,9 +34720,7 @@
-
-
-
+
das Placement-UI wird z.B. durch Klick auf das Placement-Icon aktiviert — aber es kann auch modal gemacht werden, und dann wird es in ein Property-Box/Grid-UI alloziert
@@ -37126,9 +37108,7 @@
-
-
-
+
und das zu allem Überfluß auch noch ziemlich hart verdrahtet. Man kann nicht einmal frei über die Modifier-Keys verfügen
@@ -38111,9 +38091,7 @@
-
-
-
+
wenn das Dragging funktioniert, sollte es eigentlich rein logisch unmöglich sein, das Widget zu verlassen, weil dieses sich ja mitbewegt. Allerdings sind vielerlei undlückliche Umstände denkbar, z.B. verspätete Reaktion der Software, sehr schnelle Mausbewegungen, limitierte Bewegung am Rand eines Containers.
@@ -38999,9 +38977,7 @@
-
-
-
+
diese verwalten nicht aktiv den Typ und Zustand im Buffer.
@@ -39303,9 +39279,7 @@
-
-
-
+
jede Zeichen-Primitive zeichnet von der aktuellen Position weg
@@ -39469,9 +39443,7 @@
-
-
-
+
Stand: Konzeptstudie erfolgreich
@@ -87640,8 +87612,9 @@ Date: Thu Apr 20 18:53:17 2023 +0200
-
-
+
+
+
@@ -87650,7 +87623,7 @@ Date: Thu Apr 20 18:53:17 2023 +0200
-
+
@@ -87781,13 +87754,13 @@ Date: Thu Apr 20 18:53:17 2023 +0200
-
+
-
-
-
+
+
+
@@ -87850,7 +87823,7 @@ Date: Thu Apr 20 18:53:17 2023 +0200
-
+
@@ -88097,8 +88070,8 @@ Date: Thu Apr 20 18:53:17 2023 +0200
-
-
+
+
@@ -88202,7 +88175,8 @@ Date: Thu Apr 20 18:53:17 2023 +0200
-
+
+
@@ -88221,7 +88195,7 @@ Date: Thu Apr 20 18:53:17 2023 +0200
-
+
@@ -88233,17 +88207,38 @@ Date: Thu Apr 20 18:53:17 2023 +0200
-
-
-
-
+
+
+
+
+
+
+
+ Brauche wirklich alle vier Varianten (&, const&, && und const&&). Und auch für jede einen explizit ausgeschribenen korrekten Return-Typ per std::tuple_element_t
+
+
+
+
+
-
-
-
+
+
+
+
+
+
+
+
+ heißt auch, man kann hier nicht mit SFINAE arbeiten — und exakt das hat sich am Ende auch als das Problem herausgestellt, die Compilation scheitert bevor unser Code überhaupt eingreifen kann
+
+
+
+
+
+
-
+
@@ -88265,6 +88260,7 @@ Date: Thu Apr 20 18:53:17 2023 +0200
+
@@ -88282,7 +88278,7 @@ Date: Thu Apr 20 18:53:17 2023 +0200
-
+
@@ -88301,8 +88297,7 @@ Date: Thu Apr 20 18:53:17 2023 +0200
ja wirklich: der Code geht durch den Compiler und zur Laufzeit kann ich mit dem Debugger in unseren Overload steppen
-
-
+
@@ -88315,7 +88310,19 @@ Date: Thu Apr 20 18:53:17 2023 +0200
-
+
+
+
+
+
+ exakt diese Funktionen unter anderem Namen
+
+
+ ⟹ Fehler im Test-Setup reproduzierbar
+
+
+
+
@@ -88329,8 +88336,7 @@ Date: Thu Apr 20 18:53:17 2023 +0200
durch decltype(auto) ersetzt ⟹ compiliert und unser Overload wird genommen
-
-
+
@@ -88353,14 +88359,13 @@ Date: Thu Apr 20 18:53:17 2023 +0200
Ich verwende ja einen uralten Compiler und eine OS-Version, die schon am Ende der Lebensdauer ist; gut möglich, daß dieses Problem anderswo längst erkannt und behoben wurde — es würde ja bereits genügen, die assertion erst im Body der Funktion zu haben, denn dann könnte die Overload-Resolution arbeiten und daran vorbeikommen. Andererseits ist die Assertion grundsätzlich in Ordnung, denn falls es hier keine custom-Version gäbe, wäre der Aufruf ja tatsächlich gefährlich und müßte verhindert werden
-
-
+
-
+
@@ -88376,8 +88381,8 @@ Date: Thu Apr 20 18:53:17 2023 +0200
unser Overload ist in Ordnung, wird vom Compiler gewählt und funktioniert fehlerfrei.... wenn da nur nicht die Implementierung von std::get wäre, welche bereits bei der Instantiierung mit konkreten Parametern eine Assertion triggert und damit die Compilierung stoppt.
-
-
+
+
@@ -88387,23 +88392,50 @@ Date: Thu Apr 20 18:53:17 2023 +0200
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ damit funktionieren die structured bindings
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
-
-
+
@@ -88418,9 +88450,7 @@ Date: Thu Apr 20 18:53:17 2023 +0200
-
-
-
+
@@ -142843,6 +142873,27 @@ std::cout << tmpl.render({"what", "World"}) << s
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ es genügt daß die Member unter dem Namen direkt zugreifbar sind; sie müssen auch in der gleichen Struct liegen (die aber durchaus eine Basisklasse sein kann). Bit-Felder werden unterstützt, Union-Member jedoch nicht
+
+
+
+
+
+