From c02d0a5a229ef8ea7e9a27add756e385f1273126 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Thu, 17 Sep 2026 22:00:26 +0200 Subject: [PATCH] Parameters: reorganise type conversion dependencies the generic cross-type assignment and -conversion will also be used by the `par::Scale` to join an automation evaluation result with a base value... --- src/lib/par/domain.hpp | 4 +- src/lib/par/param-type.hpp | 105 +---------------------------- src/lib/par/type-handler.hpp | 127 +++++------------------------------ 3 files changed, 20 insertions(+), 216 deletions(-) diff --git a/src/lib/par/domain.hpp b/src/lib/par/domain.hpp index a529ea233..7e8fb0757 100644 --- a/src/lib/par/domain.hpp +++ b/src/lib/par/domain.hpp @@ -35,11 +35,9 @@ #include "lib/par/spec.hpp" -#include "lib/meta/typelist.hpp" -#include "lib/meta/generator.hpp" #include "lib/par/provision.hpp" +#include "lib/meta/generator.hpp" #include "lib/meta/trait.hpp" -#include "lib/util.hpp" namespace lib { diff --git a/src/lib/par/param-type.hpp b/src/lib/par/param-type.hpp index acbd57b5e..0ad630b06 100644 --- a/src/lib/par/param-type.hpp +++ b/src/lib/par/param-type.hpp @@ -36,21 +36,13 @@ #include "lib/par/domain.hpp" +#include "lib/par/type-handler.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, // which in turn is also generated by metaprogramming from a type list. @@ -86,103 +78,10 @@ _Pragma("GCC diagnostic ignored \"-Woverloaded-virtual\"") }; - /* ===== 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"); - } - + /* ===== type conversion virtual dispatch ===== */ template template diff --git a/src/lib/par/type-handler.hpp b/src/lib/par/type-handler.hpp index f709ccbdc..7c8088c0a 100644 --- a/src/lib/par/type-handler.hpp +++ b/src/lib/par/type-handler.hpp @@ -14,18 +14,12 @@ /** @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...). + ** The implementation of parameters is built such as to conceal the actual type of + ** the parameter values -- which requires a virtual dispatch at some point. Yet the + ** underlying type conversion functionality can be defined in a generic way, based + ** on the actual types involved. Simple value-like parameters are based on a fixed + ** collection of number types, both integral and floating-point. Thus the actual + ** conversion is handled by the promotion- and coercion rules of C++ in most cases. ** ** @todo WIP-WIP-WIP can be considered an initial draft and placeholder, as of 2026 */ @@ -37,8 +31,10 @@ #include "lib/par/spec.hpp" #include "lib/meta/trait.hpp" -#include "lib/par/domain.hpp" -#include "lib/meta/generator.hpp" +#include "lib/util.hpp" + +#include +#include namespace lib { @@ -47,54 +43,20 @@ namespace par { using util::isNeg; using std::same_as; using std::floating_point; + using std::signed_integral; 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 ===== */ + /* ===== type conversion implementation 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() + concept sub_domain = signed_integral != signed_integral + or numeric_limits::max() < numeric_limits::max() + or numeric_limits::lowest() > numeric_limits::lowest() ; @@ -122,6 +84,7 @@ _Pragma("GCC diagnostic ignored \"-Wsign-compare\"") _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 @@ -175,7 +138,7 @@ _Pragma("GCC diagnostic pop") else if constexpr (std::is_constructible_v) { - targetVal.~U(); + targetVal.~V(); new(&targetVal) V (preClamp (srcVal)); } else @@ -183,62 +146,6 @@ _Pragma("GCC diagnostic pop") } - 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*/