Parameters: establish layered implementation order

Finally able to settle upon a layering for the implementation facilities

 Provision:: represents the core idea of a parameter
 Domain:: assumes the ability to handle typed values
 Disposition:: complete implementation capabilities

 BaseDomain:: establish type conversion functionality
 TypedEntity:: provide a type and ID descriptor and scale configuration
 ParamData:: full implementation based on an embedded value buffer
This commit is contained in:
Fischlurch 2026-09-01 20:43:12 +02:00
parent 0ebb2afc15
commit f452cd268d
5 changed files with 224 additions and 186 deletions

View file

@ -32,7 +32,19 @@
namespace lib {
namespace par {
/**
* Implementation-Service: complete Parameter functionality.
* Includes the ability to access / set a »value«, attach automation,
* identify and convert underlying value data types and handle
* trait- and identity tags and the specifics of the value scale.
*/
class Disposition
: public Domain
{
public:
};
/**
@ -40,9 +52,13 @@ namespace par {
* with domain and scale, and a value provision. Can be generated through a builder
* notation and stored within the Parameter object (as a container).
*/
class Disposition
: public ParamType
template<typename V>
class ParamData
: public TypedEntity<V, Disposition>
{
V val_{};
Provision* src_{nullptr};
public:
};

View file

@ -115,177 +115,5 @@ namespace par {
// 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<typename V>
struct DomainSetup
{
template<typename X, class BAS>
class TypeHandlerImpl
: public BAS
{
void extractAs (X& targetVal, ValBuff const& valBuff) override;
void conform (ValBuff& targetBuff, X const& srcVal) override;
};
using TypeHandlerChain = meta::InstantiateChained<BaseTypes::List, TypeHandlerImpl, Domain>;
};
template<typename V>
class BaseDomain
: public DomainSetup<V>::TypeHandlerChain
{
public:
void applyLimit (ValBuff&) override;
void transferTo (ValBuff const& source, Domain& targetDomain, ValBuff& target) 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<typename SUB, typename BAS>
concept sub_domain = std::signed_integral<SUB> != std::signed_integral<BAS>
or std::numeric_limits<SUB>::max() < std::numeric_limits<BAS>::max()
or std::numeric_limits<SUB>::lowest() > std::numeric_limits<BAS>::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<typename X, typename Y>
inline constexpr bool
isLower_safe (X const& x, Y const& y)
{
return isNeg(x) == isNeg(y)? x < y
: isNeg(x);
}
template<typename X, typename Y>
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<typename SUB, typename BAS>
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<typename SUB, typename BAS> requires (sub_domain<SUB,BAS>
and not same_as<SUB,bool>)
inline constexpr BAS
preClamp (BAS const& rawVal)
{
static_assert (std::is_constructible_v<BAS,SUB const&>
,"Value domains must overlap at least partially "
"and values must be cross-constructible within overlap");
auto upperBound = numeric_limits<SUB>::max();
auto lowerBound = numeric_limits<SUB>::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<same_as<bool> B, floating_point F>
inline constexpr F
preClamp (F const& rawVal)
{
return 0.5 < rawVal? F{1} : F{0};
}
template<typename V, typename X>
inline void
assignConverted (V& targetVal, X const& srcVal)
{
if constexpr (std::is_assignable_v<V&, X const&>)
{
targetVal = preClamp<V> (srcVal);
}
else
if constexpr (std::is_constructible_v<V, X const&>)
{
targetVal.~U();
new(&targetVal) V (preClamp<V> (srcVal));
}
else
static_assert (!sizeof(X), "this type conversion is not supported");
}
template<typename V>
template<typename X, class BAS>
inline void
DomainSetup<V>::TypeHandlerImpl<X,BAS>::extractAs (X& targetVal, ValBuff const& valBuff)
{
V const& srcVal = asValue<V> (valBuff);
assignConverted (targetVal, srcVal);
}
template<typename V>
template<typename X, class BAS>
inline void
DomainSetup<V>::TypeHandlerImpl<X,BAS>::conform (ValBuff& targetBuff, X const& srcVal)
{
V& targetVal = asValue<V> (targetBuff);
assignConverted (targetVal, srcVal);
}
template<typename V>
inline void
BaseDomain<V>::applyLimit (ValBuff& valBuff)
{
V& val{asValue<V> (valBuff)};
val = preClamp<V> (val);
}
template<typename V>
inline void
BaseDomain<V>::transferTo (ValBuff const& source, Domain& targetDomain, ValBuff& target)
{
targetDomain.conform (target, asValue<V> (source));
targetDomain.applyLimit (target);
}
}} // namespace lib::par
#endif /*LIB_PAR_DOMAIN_H*/

View file

@ -14,8 +14,17 @@
/** @file param-type.hpp
** Specification of the value domain and scale of a parameter.
** A ParamType descriptor implicitly also encodes the concrete value type, the
** allowed value range, clipping behaviour and specifics of the scale (discrete,
** 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
@ -27,22 +36,197 @@
#include "lib/par/domain.hpp"
#include "lib/meta/generator.hpp"
namespace lib {
namespace par {
// 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<typename VAL, class IFA>
struct DomainSetup
{
template<typename X, class PAR>
class TypeHandlerImpl
: public PAR
{
void extractAs (X& targetVal, ValBuff const& valBuff) override;
void conform (ValBuff& targetBuff, X const& srcVal) override;
};
using TypeHandlerChain = meta::InstantiateChained<BaseTypes::List, TypeHandlerImpl, IFA>;
};
template<typename VAL, class IFA>
class BaseDomain
: public DomainSetup<VAL, IFA>::TypeHandlerChain
{
public:
void applyLimit (ValBuff&) override;
void transferTo (ValBuff const& source, Domain& targetDomain, ValBuff& target) 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<typename SUB, typename BAS>
concept sub_domain = std::signed_integral<SUB> != std::signed_integral<BAS>
or std::numeric_limits<SUB>::max() < std::numeric_limits<BAS>::max()
or std::numeric_limits<SUB>::lowest() > std::numeric_limits<BAS>::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<typename X, typename Y>
inline constexpr bool
isLower_safe (X const& x, Y const& y)
{
return isNeg(x) == isNeg(y)? x < y
: isNeg(x);
}
template<typename X, typename Y>
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<typename SUB, typename BAS>
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<typename SUB, typename BAS> requires (sub_domain<SUB,BAS>
and not same_as<SUB,bool>)
inline constexpr BAS
preClamp (BAS const& rawVal)
{
static_assert (std::is_constructible_v<BAS,SUB const&>
,"Value domains must overlap at least partially "
"and values must be cross-constructible within overlap");
auto upperBound = numeric_limits<SUB>::max();
auto lowerBound = numeric_limits<SUB>::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<same_as<bool> B, floating_point F>
inline constexpr F
preClamp (F const& rawVal)
{
return 0.5 < rawVal? F{1} : F{0};
}
template<typename V, typename X>
inline void
assignConverted (V& targetVal, X const& srcVal)
{
if constexpr (std::is_assignable_v<V&, X const&>)
{
targetVal = preClamp<V> (srcVal);
}
else
if constexpr (std::is_constructible_v<V, X const&>)
{
targetVal.~U();
new(&targetVal) V (preClamp<V> (srcVal));
}
else
static_assert (!sizeof(X), "this type conversion is not supported");
}
template<typename VAL, class IFA>
template<typename X, class PAR>
inline void
DomainSetup<VAL,IFA>::TypeHandlerImpl<X,PAR>::extractAs (X& targetVal, ValBuff const& valBuff)
{
VAL const& srcVal = asValue<VAL> (valBuff);
assignConverted (targetVal, srcVal);
}
template<typename VAL, class IFA>
template<typename X, class PAR>
inline void
DomainSetup<VAL,IFA>::TypeHandlerImpl<X,PAR>::conform (ValBuff& targetBuff, X const& srcVal)
{
VAL& targetVal = asValue<VAL> (targetBuff);
assignConverted (targetVal, srcVal);
}
template<typename VAL, class IFA>
inline void
BaseDomain<VAL,IFA>::applyLimit (ValBuff& valBuff)
{
VAL& val{asValue<VAL> (valBuff)};
val = preClamp<VAL> (val);
}
template<typename VAL, class IFA>
inline void
BaseDomain<VAL,IFA>::transferTo (ValBuff const& source, Domain& targetDomain, ValBuff& target)
{
targetDomain.conform (target, asValue<VAL> (source));
targetDomain.applyLimit (target);
}
/**
* Interface: represent a specific configuration of parameter value type,
* 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.
*/
class ParamType
: public Domain
template<typename VAL, class IFA>
class TypedEntity
: public BaseDomain<VAL,IFA>
{
public:
};

View file

@ -19,6 +19,7 @@
#include "test/run.hpp"
//#include "test/test-helper.hpp"
#include "lib/par/domain.hpp"
#include "lib/par/param-type.hpp"
#include "test/diagnostic-output.hpp"////////////TODO
//#include <utility>
@ -168,6 +169,9 @@ namespace test{
template<typename X>
using FullDomain = BaseDomain<X, Domain>;
/** @test base function to access the contents of an opaque buffer
* with known type, and convert into another target type.
* - the base interface #Domain represents an opaque type
@ -183,8 +187,8 @@ namespace test{
void
verify_valueAccess()
{
BaseDomain<int> domInt;
CHECK (sizeof(BaseDomain<int>) <= sizeof(void*)); // only the VTable
FullDomain<int> domInt;
CHECK (sizeof(FullDomain<int>) <= sizeof(void*)); // only the VTable
// use the generic interface for access...
Domain& dom{domInt};
@ -221,7 +225,7 @@ namespace test{
void
verify_valueSetting()
{
BaseDomain<int> domInt;
FullDomain<int> domInt;
Domain& dom{domInt};
int val_in_buff = 0;
@ -259,9 +263,9 @@ namespace test{
int64_t i64_val{0};
bool boo{false};
BaseDomain<double> ddom;
BaseDomain<int64_t> idom;
BaseDomain<bool> bdom;
FullDomain<double> ddom;
FullDomain<int64_t> idom;
FullDomain<bool> bdom;
Domain& d1{ddom};
Domain& d3{bdom};

View file

@ -84451,6 +84451,11 @@
<node CREATED="1788222535392" ID="ID_1282201343" MODIFIED="1788222543419" TEXT="Disposition ist immer noch ein Interface"/>
<node CREATED="1788222544142" ID="ID_1947215779" MODIFIED="1788222568288" TEXT="es wird per PolymorphicValue auf das Parameter-Objekt exponiert"/>
<node CREATED="1788222571539" ID="ID_1256386409" MODIFIED="1788222603029" TEXT="die ganze konkrete Implementierung setzt darauf auf (und bleibt damit opaque, und variierbar)"/>
<node CREATED="1788293527789" ID="ID_1979935695" MODIFIED="1788293563082" TEXT="Konsequenz &#x27f9; mu&#xdf; dieses Interface generisch zusammenstellen">
<icon BUILTIN="yes"/>
<node CREATED="1788293568866" ID="ID_131380857" MODIFIED="1788293583910" TEXT="es mu&#xdf; zwingend oberhalb der getypten Implementierung liegen"/>
<node CREATED="1788293586698" ID="ID_1746668987" MODIFIED="1788293628728" TEXT="Inhaltlich geh&#xf6;rt es aber n&#xe4;her zum ParamData-Record"/>
</node>
</node>
<node CREATED="1788279465992" ID="ID_511934631" MODIFIED="1788279480380" TEXT="Insgesamt entsteht hier eine Konkretisierungs-Kette">
<node CREATED="1788279482831" ID="ID_1252818882" MODIFIED="1788279536466" TEXT="wir gehen vom Kontrakt zum Service">
@ -84466,9 +84471,10 @@
<node CREATED="1788279539424" ID="ID_1639736337" MODIFIED="1788279543780" TEXT="Bedeutung der Stufen">
<node CREATED="1788279549319" ID="ID_120197629" MODIFIED="1788279575207" TEXT="Provision: die Grundidee eines Parameters wie hier konzeptionalisiert"/>
<node CREATED="1788279579772" ID="ID_1387667429" MODIFIED="1788279619787" TEXT="Domain: Zusicherung einer Typ-konformen Handhabung"/>
<node CREATED="1788280039452" ID="ID_1355845527" MODIFIED="1788295973732" TEXT="Disposition: der vollst&#xe4;ndige Parameter-Implementierungs-Service"/>
<node CREATED="1788279649921" ID="ID_829245549" MODIFIED="1788279664995" TEXT="BaseDomain: Festlegung der Eigenschaften der Datenbasis"/>
<node CREATED="1788279667230" ID="ID_1645392786" MODIFIED="1788279840724" TEXT="TypedEntity: Konformit&#xe4;t und Darstelluns-F&#xe4;higkeiten"/>
<node CREATED="1788280039452" ID="ID_1355845527" MODIFIED="1788280070438" TEXT="Disposition: der komplette Parameter-Service"/>
<node CREATED="1788295927324" ID="ID_53887849" MODIFIED="1788295946230" TEXT="ParamData: der komplette Implementierungs-Record"/>
</node>
</node>
</node>