diff --git a/src/lib/par/domain.hpp b/src/lib/par/domain.hpp index aa8489bbf..a529ea233 100644 --- a/src/lib/par/domain.hpp +++ b/src/lib/par/domain.hpp @@ -41,18 +41,10 @@ #include "lib/meta/trait.hpp" #include "lib/util.hpp" -#include -#include - namespace lib { namespace par { - using util::isNeg; - using std::same_as; - using std::floating_point; - using std::numeric_limits; - /* ===== Type Handler Interface for generic parameter value access ===== */ diff --git a/src/lib/par/param-type.hpp b/src/lib/par/param-type.hpp index a66af258d..acbd57b5e 100644 --- a/src/lib/par/param-type.hpp +++ b/src/lib/par/param-type.hpp @@ -38,10 +38,18 @@ #include "lib/par/domain.hpp" #include "lib/meta/generator.hpp" +#include +#include + namespace lib { namespace par { + using util::isNeg; + using std::same_as; + using std::floating_point; + using std::numeric_limits; + // GCC > 13 warns at class definition when a new overload shadows an inherited virtual function. // While theoretically correct, in practice any call will be dispatched through the base interface, diff --git a/src/lib/par/scale.hpp b/src/lib/par/scale.hpp index 5522e9638..0a31f0f55 100644 --- a/src/lib/par/scale.hpp +++ b/src/lib/par/scale.hpp @@ -30,6 +30,7 @@ #include "lib/error.hpp" #include "lib/par/domain.hpp" +#include "lib/par/type-handler.hpp" #include "lib/util-quant.hpp" #include "lib/format-string.hpp" //#include "lib/util.hpp" @@ -192,9 +193,10 @@ namespace par { template template inline VAL - Scale::join (VAL const& value, SRC const& feed, Scale const& feedScale) const + Scale::join (VAL const& value, SRC const& srcFeed, Scale const& feedScale) const { - UNIMPLEMENTED ("join a feed with a base value, observing Scale constraints"); + VAL feed(preClamp (srcFeed)); + return join (value, feed); } /** diff --git a/src/lib/par/type-handler.hpp b/src/lib/par/type-handler.hpp new file mode 100644 index 000000000..f709ccbdc --- /dev/null +++ b/src/lib/par/type-handler.hpp @@ -0,0 +1,244 @@ +/* + TYPE-HANDLER.hpp - generic type conversion handling support + + 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 type-handler.hpp + ** Generic building blocks to support parameter value conversions and adaptation. + ** The implementation of parameters is built up in layers, starting from the generic + ** interfaces \ref Provision, which describes the fundamental capabilities of a + ** parameter as conceptualised here) and \ref Domain to support type conversions. + ** + ** All extensions beyond this point can be considered implementation-level services. + ** The BaseDomain layer provides the actual type conversion services, based on internal + ** knowledge of the concrete data type handled by this parameter. + ** + ** A \ref TypedEntity descriptor adds storage to capture type properties and allow + ** for an ID registration. In addition, the concrete value type is tagged, marking + ** the allowed value range, clipping behaviour and specifics of the scale (discrete, + ** stepping, continuous, logarithmic...). + ** + ** @todo WIP-WIP-WIP can be considered an initial draft and placeholder, as of 2026 + */ + + +#ifndef LIB_PAR_TYPE_HANDLER_H +#define LIB_PAR_TYPE_HANDLER_H + + +#include "lib/par/spec.hpp" +#include "lib/meta/trait.hpp" +#include "lib/par/domain.hpp" +#include "lib/meta/generator.hpp" + + +namespace lib { +namespace par { + + using util::isNeg; + using std::same_as; + using std::floating_point; + using std::numeric_limits; + + +// GCC > 13 warns at class definition when a new overload shadows an inherited virtual function. +// While theoretically correct, in practice any call will be dispatched through the base interface, +// which in turn is also generated by metaprogramming from a type list. +// See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109740 +_Pragma("GCC diagnostic push") \ +_Pragma("GCC diagnostic ignored \"-Woverloaded-virtual\"") + + + template + struct DomainSetup + { + + template + class TypeHandlerImpl + : public PAR + { + void extractAs (X& targetVal, ValBuff const& valBuff) const override; + void conform (ValBuff& targetBuff, X const& srcVal) override; + }; + + using TypeHandlerChain = meta::InstantiateChained; + }; + + + + template + class BaseDomain + : public DomainSetup::TypeHandlerChain + { + public: + void applyLimit (ValBuff&) override; + void transferTo (ValBuff const& source, Domain& targetDomain, ValBuff& target) const override; + }; + + + /* ===== type conversion details ===== */ + + /** Constraint: type \a SUB can not represent the full domain of type \a BAS + * @todo implicitly also implements a limitation to »numeric« types, + * due to the use of std::numeric_limits + */ + template + concept sub_domain = std::signed_integral != std::signed_integral + or std::numeric_limits::max() < std::numeric_limits::max() + or std::numeric_limits::lowest() > std::numeric_limits::lowest() + ; + + +_Pragma("GCC diagnostic push") \ +_Pragma("GCC diagnostic ignored \"-Wsign-compare\"") +// the following functions are specifically crafted to work around +// the ambiguities caused by automatic signed-to-unsigned promotion; +// furthermore, the automatic promotion allows to perform the comparison. + + template + inline constexpr bool + isLower_safe (X const& x, Y const& y) + { + return isNeg(x) == isNeg(y)? x < y + : isNeg(x); + } + + template + inline constexpr bool + isLarger_safe (X const& x, Y const& y) + { + return isNeg(x) == isNeg(y)? x > y + : isNeg(y); + } +_Pragma("GCC diagnostic pop") + + + /** generic helper to conform into a common value domain + * @tparam SUB the implied target value domain + * @tparam BAS source data type + * @return a value in source data type, + * yet conformed into the target data range + */ + template + inline constexpr BAS + preClamp (BAS const& value) + { + return value; + } + + /** special case: clamp the source value into the target value domain. + * @remark tricky due to automatic conversions between signed/unsigned */ + template requires (sub_domain + and not same_as) + inline constexpr BAS + preClamp (BAS const& rawVal) + { + static_assert (std::is_constructible_v + ,"Value domains must overlap at least partially " + "and values must be cross-constructible within overlap"); + + auto upperBound = numeric_limits::max(); + auto lowerBound = numeric_limits::lowest(); + + return isLarger_safe(rawVal, upperBound)? BAS(upperBound) + : isLower_safe (rawVal, lowerBound)? BAS(lowerBound) + : rawVal; + } + + /** special case: trigger a bool at the 0.5 level */ + template B, floating_point F> + inline constexpr F + preClamp (F const& rawVal) + { + return 0.5 < rawVal? F{1} : F{0}; + } + + + + template + inline void + assignConverted (V& targetVal, X const& srcVal) + { + if constexpr (std::is_assignable_v) + { + targetVal = preClamp (srcVal); + } + else + if constexpr (std::is_constructible_v) + { + targetVal.~U(); + new(&targetVal) V (preClamp (srcVal)); + } + else + static_assert (!sizeof(X), "this type conversion is not supported"); + } + + + template + template + inline void + DomainSetup::TypeHandlerImpl::extractAs (X& targetVal, ValBuff const& valBuff) const + { + VAL const& srcVal = asValue (valBuff); + assignConverted (targetVal, srcVal); + } + + + template + template + inline void + DomainSetup::TypeHandlerImpl::conform (ValBuff& targetBuff, X const& srcVal) + { + VAL& targetVal = asValue (targetBuff); + assignConverted (targetVal, srcVal); + } + + + template + inline void + BaseDomain::applyLimit (ValBuff& valBuff) + { + VAL& val{asValue (valBuff)}; + val = preClamp (val); + } + + + template + inline void + BaseDomain::transferTo (ValBuff const& source, Domain& targetDomain, ValBuff& target) const + { + targetDomain.conform (target, asValue (source)); + targetDomain.applyLimit (target); + } + + + + + + /** + * Service-Interface: represent a specific configuration of parameter value type, + * range and scale. Every ParamType descriptor is also a _prototype_ and has + * an unique identity, ensured by registration, so that it acts as a type identifier. + * @todo RLY? registration?? + * Furthermore, the interface exposes allowed conversion and conforming operations. + */ + template + class TypedEntity + : public BaseDomain + { + public: + }; + + + +}} // namespace lib::par +#endif /*LIB_PAR_TYPE_HANDLER_H*/ diff --git a/tests/lib/par/parameter-scale-test.cpp b/tests/lib/par/parameter-scale-test.cpp index b96f500c4..941fc9857 100644 --- a/tests/lib/par/parameter-scale-test.cpp +++ b/tests/lib/par/parameter-scale-test.cpp @@ -163,7 +163,16 @@ namespace test{ verify_valueJoining() { Scale scale; - } + } + + + /** @test + * @todo WIP 9/26 🔁 define ⟶ implement + */ + void + verify_NominalScale() + { + } }; diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index c063ce820..d71bdc78f 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -85572,8 +85572,52 @@ - - + + + + + + +

+ Zwar könnte man auch so argumentieren, daß zunächst der Quell-Feed so adaptiert werden sollte, daß der Basistyp gleich ist... +

+

+ Bei genauerer Betrachtung stellt sich jedoch heraus, daß dies keine gute Idee ist, denn... +

+
    +
  • + zum Einen wäre das jeweils ein weiterer dynamisch hinzugefügter Dekorator (und damit zusätzlicher Overhead), obwohl doch die eigentliche Umwandlung auf Basis bekannter Typen bereits zur Compile-Zeit gemacht werden könnte +
  • +
  • + und außerdem wirft man damit die detailierte Information über den Quell-Feed weg (oder reicht sie dann eben doch weiter, wodurch dieser Schritt dann redundant würde) obwohl eben diese zusätzliche Information durchaus in einen besser angepaßten Rechenweg umgesetzt werden könnte +
  • +
+ + +
+
+ + + + + + + +

+ ...weil wir die Basis-Typen für Parameter entsprechend eingeschränkt haben +

+ + +
+
+ + + + + + + + @@ -85596,6 +85640,12 @@
+ + + + + +
@@ -86059,8 +86109,8 @@ class ScaleRegistry - - + + @@ -87272,8 +87322,7 @@ Prop.1: dist%3 1 2 0 1 2 0 ...da ein Test eigentlich mehr machen sollte, als nur nochmal den Code zu wiederholen; und hier passiert nicht mehr, als ein cap an der boundrary, auf einem komplett offensichtlichen und einfachen Code-Pfad

- - +
@@ -87300,6 +87349,9 @@ Prop.1: dist%3 1 2 0 1 2 0 + + +