From e0fa980f4c2a7506ce97aa9149661398ecfef865 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 2 Sep 2026 21:03:13 +0200 Subject: [PATCH] Parameters: introduce ID-Record into the impl-interface The solution from yesterday works, but is needlessly indirect. However, if we want to circumvent ''any indirection'' in the simple standard case, then it turns out to be necessary to have some kind of ''flag store'' already in the `Disposition` (the implementation service interface). Because otherwise it would be impossible to trigger the short-circuit access without going through some kind of indirection. Need to define a generic ID-Record with some flexible extension space that can be used by subclasses; and notably we need to generate some base-type-IDs on a static level -- which should be possible, since all allowed BaseTypes are defined as Type-Sequence. --- src/lib/meta/typelist-util.hpp | 20 +++ src/lib/par/disposition.hpp | 3 + src/lib/par/domain.hpp | 12 +- src/lib/par/parameter.hpp | 6 +- src/lib/par/spec.hpp | 74 ++++++++ src/lib/polymorphic-value.hpp | 2 +- src/lib/util-quant.hpp | 10 +- tests/lib/par/parameter-test.cpp | 5 + wiki/thinkPad.ichthyo.mm | 293 +++++++++++++++++++++++++++++-- 9 files changed, 390 insertions(+), 35 deletions(-) create mode 100644 src/lib/par/spec.hpp diff --git a/src/lib/meta/typelist-util.hpp b/src/lib/meta/typelist-util.hpp index 2cd13a9ea..bbc38ef3f 100644 --- a/src/lib/meta/typelist-util.hpp +++ b/src/lib/meta/typelist-util.hpp @@ -115,6 +115,26 @@ namespace meta { { }; + /** + * Metafunction: build an ID-Type based on the position in the list + * @remark when the type is not in the list, TypeIdxID ≔ 0 + */ + template + struct TypeIdxID + : std::integral_constant + { }; + + template + struct TypeIdxID, idx> ///< @return 1-based position in case of match + : std::integral_constant + { }; + + template + struct TypeIdxID, idx> ///< else recurse to check next element + : TypeIdxID + { }; + + /** * Build a list of const types from a given typelist. diff --git a/src/lib/par/disposition.hpp b/src/lib/par/disposition.hpp index 17301530a..b35e387cd 100644 --- a/src/lib/par/disposition.hpp +++ b/src/lib/par/disposition.hpp @@ -26,6 +26,7 @@ #define LIB_PAR_DISPOSITION_H +#include "lib/par/spec.hpp" #include "lib/par/provision.hpp" #include "lib/par/param-type.hpp" @@ -43,6 +44,8 @@ namespace par { class Disposition : public Domain { + IDRecord idr_; + public: template X extract() const; diff --git a/src/lib/par/domain.hpp b/src/lib/par/domain.hpp index 2cdbdc5f5..43bc72a3d 100644 --- a/src/lib/par/domain.hpp +++ b/src/lib/par/domain.hpp @@ -34,6 +34,7 @@ #define LIB_PAR_DOMAIN_H +#include "lib/par/spec.hpp" #include "lib/meta/typelist.hpp" #include "lib/meta/generator.hpp" #include "lib/par/provision.hpp" @@ -76,17 +77,6 @@ namespace par { }; - /** - * Fixed collection of elementary types supported in parameters - */ - using BaseTypes = meta::Types; /** build a generic visitor interface for all types in list */ using TypeHandlerInterface = meta::InstantiateChained; diff --git a/src/lib/par/parameter.hpp b/src/lib/par/parameter.hpp index c2e7b5a24..41f28d2cb 100644 --- a/src/lib/par/parameter.hpp +++ b/src/lib/par/parameter.hpp @@ -31,7 +31,6 @@ #include "lib/par/disposition.hpp" #include "lib/polymorphic-value.hpp" -#include "lib/meta/typelist-util.hpp" #include "lib/util.hpp" #include @@ -92,12 +91,9 @@ namespace par { }; - template + template requires par_basetype class Parameter::Builder { - static_assert (meta::isInList() - ,"Only some fixed base data types are supported as Parameter value type."); - VAL initVal_{}; public: diff --git a/src/lib/par/spec.hpp b/src/lib/par/spec.hpp new file mode 100644 index 000000000..3975e08c3 --- /dev/null +++ b/src/lib/par/spec.hpp @@ -0,0 +1,74 @@ +/* + SPEC.hpp - parameter trait specification + + Copyright (C) + 2026 Hermann Vosseler + +  **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 spec.hpp + ** Common definition and specification record for parameters. + ** While most functionality of parameters is indirected in some way, allowing + ** for future extension, some basic traits are marked as flag, so that frequently + ** used code-paths might take a shortcut. Notably the disposition interface, which + ** describes the full implementation service of a parameter container, explicitly + ** reserves one «slot» of storage for flags, type markers and an ID record. + ** + ** @todo WIP-WIP-WIP can be considered an initial draft and placeholder, as of 2026 + */ + + +#ifndef LIB_PAR_SPEC_H +#define LIB_PAR_SPEC_H + + +#include "lib/symbol.hpp" +#include "lib/meta/typelist.hpp" +#include "lib/meta/typelist-util.hpp" +#include "lib/meta/typeseq-util.hpp" + + + +namespace lib { +namespace par { + + /** + * Fixed collection of elementary types supported in parameters + */ + using BaseTypes = meta::Types; + + static constexpr uint BASETYPE_CNT = meta::count(); + + template + static constexpr bool isBaseType = meta::isInList::value; + + /** @note Only some fixed base data types are supported as Parameter value type. */ + template + concept par_basetype = isBaseType; + + template + using BaseTypeID = meta::TypeIdxID; + + + struct IDRecord + { + + }; + + + +}} // namespace lib::par +#endif /*LIB_PAR_SPEC_H*/ diff --git a/src/lib/polymorphic-value.hpp b/src/lib/polymorphic-value.hpp index e3fad2293..941512eae 100644 --- a/src/lib/polymorphic-value.hpp +++ b/src/lib/polymorphic-value.hpp @@ -516,7 +516,7 @@ namespace lib { static PolymorphicValue build (ARGS&&... args) { - IMP* type_to_build_in_buffer; + IMP* type_to_build_in_buffer{}; return PolymorphicValue (type_to_build_in_buffer, std::forward(args)...); } diff --git a/src/lib/util-quant.hpp b/src/lib/util-quant.hpp index a3c40b052..d8114e590 100644 --- a/src/lib/util-quant.hpp +++ b/src/lib/util-quant.hpp @@ -171,7 +171,7 @@ namespace util { * @todo 3/2024 seems we have solved this problem several times meanwhile /////////////////////////////////TICKET #1360 sort out floating-point rounding and precision */ inline constexpr bool - almostEqual (double d1, double d2, unsigned int ulp =2) noexcept + almostEqual (double d1, double d2, uint ulp =2) noexcept { using std::fabs; return fabs (d1-d2) < DBL_EPSILON * fabs (d1+d2) * ulp @@ -224,8 +224,12 @@ namespace util { return logB; } - - + static_assert (-1 == ilog2(-5)); + static_assert ( 0 == ilog2( 1)); + static_assert ( 5 == ilog2(42)); + + + /** * Exponentiation with integral exponent (counterpart to #ilog2). * @param base the value to be raised diff --git a/tests/lib/par/parameter-test.cpp b/tests/lib/par/parameter-test.cpp index 33b7596a4..8de7a421e 100644 --- a/tests/lib/par/parameter-test.cpp +++ b/tests/lib/par/parameter-test.cpp @@ -77,7 +77,12 @@ namespace test{ void simpleUsage() { +SHOW_EXPR(BUFF_SIZ); CHECK (sizeof(Parameter) <= 4 * sizeof(void*)); +SHOW_EXPR(BaseTypeID::value) +SHOW_EXPR(BaseTypeID::value) + ParamData dip(55); +SHOW_EXPR(sizeof(dip)) Parameter par = Parameter::forType().build(); CHECK (0 == par.getVal()); diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 8eb9679e5..c6a92ede1 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -61468,6 +61468,20 @@ + + + + + + + + +

+ Das Bindeglied steckt in typeseq-util.hpp ⟶ es wurde einfach möglich durch die Umstellung auf variadische Typ-Sequenzen (beim Übergang auf C++20) +

+ +
+
@@ -61491,6 +61505,39 @@ + + + + + + +

+ constexpr indexOfType<T,TS...>() +

+

+ +

+

+ bisher wenig verwendet, wenn aber, dann für einen Index-basierten Zugriff ⟹ es muß eine static-Assertion geben, daß der gesuchte Typ auch wirklich in der Liste ist (es wäre brandgefährlich, im sonst-Fall ein Sentinel zurückzugeben, z.B. -1) +

+ +
+
+ + + + + + +

+ der Trick war, den gefundenen Index in einem zusätzlichen non-type-Argument hochzuzählen, und bei Match von dort zu entnehmen. Warum? nur so kann man einen ganz anderen Wert liefern, wenn man über das Ende der Liste hinausläuft; die naive Implementierung mit Tail-Rekursion addiert ja den Index auf dem Rückweg hoch und erlaubt daher keinen alternativen Ausgang +

+ +
+
+
+
+
@@ -84024,8 +84071,9 @@ - + + @@ -84133,7 +84181,20 @@ - + + + + + + + +

+ ich will mich ja grade nicht darauf festlegen, daß es einen »Buffer« gibt +

+ +
+
+
@@ -84492,7 +84553,12 @@ - + + + + + + @@ -84525,35 +84591,232 @@ - - + + + + + + + + + + - + + + + + +

+ ...denn hier habe ich ja (per Template-Parameter) schon einen Typ in der Hand, muß also nur noch den zugehörigen TypeHandler aufrufen (eine Indirektion) +

+ +
+
+ + + + +

+ ...der deckt nur den (theoretischen) Fall ab, daß wir eine Zuweisung zwischen zwei Parametern machen (für die beide der Datentyp opaque ist). +

+ +
+
+ + + + +

+ zumal ich dann auch noch per force-downcast auf den Buffer zugreifen müßte +

+ +
+ +
+
+ + - - - +

auch im Hinblick darauf, daß ich später mal (möglichst mit dem gleichen generischen Code) auch komplexe Parameter-Typen handhaben möchte, wie Vektoren, Farb-Tripel und ganze Masken-Parametersätze...

- -
+
+ + + + + + + + +

+ Der Umstand, daß man da so ein Marker-Template inmischen muß, und dann die Fallunterscheidung, wann der PolymorphicValue-Container tatsächlich die schlanke lineare Vererbungskette nutzen kann, und wo der Copy-Adapter aufgesetzt wird.... +

+

+ Ich hab das Zeug gebaut, und schon ein paar mal verwendet, aber ich blick' da einfach nicht mehr durch — jedes mal muß man sich wieder durch die ganzen Templates durchfressen (und das für etwas, was es eigentlich mit modernem C++ gar nicht mehr bräuchte) +

+ +
+ +
+ + +
+ + + + + + + + + + +

+ ...zumal ich einen weiteren gewaltigen Zielkonflikt schon »riechen« kann: die Ansprüche komplexer Datentypen sind mit einem leichtgewichtigen Standardfall schwer vereinbar. +

+ +
+
+
+ + + + + + + +

+ ich muß es aber anfassen +

+ +
+ + + + +

+ Ich habe gesagt: alles was sich vorher regeln läßt, wird in einer Art Compiler optimal konfiguriert. Und alles Übrige sucht mich jetzt als »Parameter« (oder gar Konfiguration und Regelwerk) heim. +

+ +
+
+ + + + +

+ ....an den Parameter wird deutlich, daß möglicherweise meine »Lösung« mit den Segmenten (die ohnehin mehr eine Ausrede ist) so ohne Weiteres gar nicht tragfähig sein könnte — und wenn sich das realisieren sollte, dann stehe ich unter Handlungszwang (heißt, ich kann die Architektur nicht länger so offen halten, wie ich es gerne hätte, um den weiterreichenden Ideen die notwendige Luft zu verschaffen) +

+ +
+
+ + + + +

+ Das Thema »Automation« ist eines der wichtigsten Themen, die bestehende Applikationen immer wieder versäumen. Für mich ist die Lösung so offensichtlich klar, daß ich es immer in die Zukunft verschoben habe. Nun läuft mir aber die Zeit davon, dieses Projekt hat keine endlose Zukunft (wenn es sie je gehabt hätte....). Ich brauche also für alle wirklich wichtigen Themen eine Architektur-Lösung  — also eine geprägte Struktur, die man ohne Zerstörung nicht übergehen kann. +

+ +
+
+
+
+ + + + + + + + + + + + + +

+ Hier mache ich ein ganz grundsätzliches Argument... +

+
    +
  • + Parameter sollen universell sein ⟹ also müssen sie laufzeit-dynamisch flexibel gemacht werden +
  • +
  • + eine Konfiguration zur Laufzeit erfordert stets eine Storage (auch die VTable) +
  • +
  • + wenn ich unter der Schwelle eines Funktionspointer-Dispatches bleiben möchte, kann ich nur in von Vornherein festgelegte Code-Alternativen verzweigen, per Flag (oder Compile-Time-Branch) +
  • +
+

+ Theoretisch ließen sich beide Indirektionsmechanismen über eine Dispatch-Tabelle verbinden; das wäre aber eine durchaus fragwürdige Storage-Optimierung (die ganz sicher unter das Verdikt »premature« fällt, sofern nicht anders aufgezeigt) +

+ +
+
+
+ + + + + +

+ unter unsäglichen Schmerzen könnte ich daraus drei »Slots« machen ... und was bringt mir das? Wenn ich ultimative Effizienz brauche, sollte ich kein universelles Konstrukt wie einen Parameter verwenden. Siehe die Activity-Language im Scheduler, wo ich durch geeignete Speicher-Anordnung die durchaus massiv vorhandene Indirektion auf aktzeptable Cache-Kohärenz gebracht habe. +

+ +
+
+ + + + +

+ also echt jetzt! warum beiße ich mich an der Hinsicht dermaßen fest? alle anderen Hinsichten, die ich durchdacht (und bereits gelöst) habe, sind viel wichtiger (z.B. die Automation). Irgendwie versuche ich, hier einen Sieg vor mir selber zu erringen, wobei es doch im Moment gar nichts zu gewinnen gibt. +

+ +
+ +
+
+ + + + + + + + + +

+ zunächst dachte ich noch, ich müßte eine statische Registrierungs-Tabelle aufbauen, ggfs. mit einem Class-Lock. Da ich mich nun aber darauf festgelegt habe, die Basis-Typen statisch festzulegen, in einer Typsequenz, wandert das alles in die Metaprogramming-Ecke +

+ +
+
+
+
+
+
@@ -84639,8 +84902,8 @@ - - + + @@ -185996,12 +186259,12 @@ std::cout << tmpl.render({"what", "World"}) << s - +

- ...also für template templtate parameter; + ...also für template template parameter;

für diese muß man allerdings stets die umständlichere requires-Syntax verwenden