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:
parent
0ebb2afc15
commit
f452cd268d
5 changed files with 224 additions and 186 deletions
|
|
@ -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:
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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*/
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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};
|
||||
|
|
|
|||
|
|
@ -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 ⟹ muß dieses Interface generisch zusammenstellen">
|
||||
<icon BUILTIN="yes"/>
|
||||
<node CREATED="1788293568866" ID="ID_131380857" MODIFIED="1788293583910" TEXT="es muß zwingend oberhalb der getypten Implementierung liegen"/>
|
||||
<node CREATED="1788293586698" ID="ID_1746668987" MODIFIED="1788293628728" TEXT="Inhaltlich gehört es aber nä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ä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ät und Darstelluns-Fä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>
|
||||
|
|
|
|||
Loading…
Reference in a new issue