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...
This commit is contained in:
parent
6de07e9a36
commit
c02d0a5a22
3 changed files with 20 additions and 216 deletions
|
|
@ -35,11 +35,9 @@
|
||||||
|
|
||||||
|
|
||||||
#include "lib/par/spec.hpp"
|
#include "lib/par/spec.hpp"
|
||||||
#include "lib/meta/typelist.hpp"
|
|
||||||
#include "lib/meta/generator.hpp"
|
|
||||||
#include "lib/par/provision.hpp"
|
#include "lib/par/provision.hpp"
|
||||||
|
#include "lib/meta/generator.hpp"
|
||||||
#include "lib/meta/trait.hpp"
|
#include "lib/meta/trait.hpp"
|
||||||
#include "lib/util.hpp"
|
|
||||||
|
|
||||||
|
|
||||||
namespace lib {
|
namespace lib {
|
||||||
|
|
|
||||||
|
|
@ -36,21 +36,13 @@
|
||||||
|
|
||||||
|
|
||||||
#include "lib/par/domain.hpp"
|
#include "lib/par/domain.hpp"
|
||||||
|
#include "lib/par/type-handler.hpp"
|
||||||
#include "lib/meta/generator.hpp"
|
#include "lib/meta/generator.hpp"
|
||||||
|
|
||||||
#include <concepts>
|
|
||||||
#include <limits>
|
|
||||||
|
|
||||||
|
|
||||||
namespace lib {
|
namespace lib {
|
||||||
namespace par {
|
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.
|
// 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,
|
// 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.
|
// 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<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>
|
/* ===== type conversion virtual dispatch ===== */
|
||||||
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 VAL, class IFA>
|
||||||
template<typename X, class PAR>
|
template<typename X, class PAR>
|
||||||
|
|
|
||||||
|
|
@ -14,18 +14,12 @@
|
||||||
|
|
||||||
/** @file type-handler.hpp
|
/** @file type-handler.hpp
|
||||||
** Generic building blocks to support parameter value conversions and adaptation.
|
** Generic building blocks to support parameter value conversions and adaptation.
|
||||||
** The implementation of parameters is built up in layers, starting from the generic
|
** The implementation of parameters is built such as to conceal the actual type of
|
||||||
** interfaces \ref Provision, which describes the fundamental capabilities of a
|
** the parameter values -- which requires a virtual dispatch at some point. Yet the
|
||||||
** parameter as conceptualised here) and \ref Domain to support type conversions.
|
** 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
|
||||||
** All extensions beyond this point can be considered implementation-level services.
|
** collection of number types, both integral and floating-point. Thus the actual
|
||||||
** The BaseDomain layer provides the actual type conversion services, based on internal
|
** conversion is handled by the promotion- and coercion rules of C++ in most cases.
|
||||||
** 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
|
** @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/par/spec.hpp"
|
||||||
#include "lib/meta/trait.hpp"
|
#include "lib/meta/trait.hpp"
|
||||||
#include "lib/par/domain.hpp"
|
#include "lib/util.hpp"
|
||||||
#include "lib/meta/generator.hpp"
|
|
||||||
|
#include <concepts>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
|
||||||
namespace lib {
|
namespace lib {
|
||||||
|
|
@ -47,54 +43,20 @@ namespace par {
|
||||||
using util::isNeg;
|
using util::isNeg;
|
||||||
using std::same_as;
|
using std::same_as;
|
||||||
using std::floating_point;
|
using std::floating_point;
|
||||||
|
using std::signed_integral;
|
||||||
using std::numeric_limits;
|
using std::numeric_limits;
|
||||||
|
|
||||||
|
|
||||||
// GCC > 13 warns at class definition when a new overload shadows an inherited virtual function.
|
/* ===== type conversion implementation details ===== */
|
||||||
// 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) const 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) const override;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/* ===== type conversion details ===== */
|
|
||||||
|
|
||||||
/** Constraint: type \a SUB can not represent the full domain of type \a BAS
|
/** Constraint: type \a SUB can not represent the full domain of type \a BAS
|
||||||
* @todo implicitly also implements a limitation to »numeric« types,
|
* @todo implicitly also implements a limitation to »numeric« types,
|
||||||
* due to the use of std::numeric_limits
|
* due to the use of std::numeric_limits
|
||||||
*/
|
*/
|
||||||
template<typename SUB, typename BAS>
|
template<typename SUB, typename BAS>
|
||||||
concept sub_domain = std::signed_integral<SUB> != std::signed_integral<BAS>
|
concept sub_domain = signed_integral<SUB> != signed_integral<BAS>
|
||||||
or std::numeric_limits<SUB>::max() < std::numeric_limits<BAS>::max()
|
or numeric_limits<SUB>::max() < numeric_limits<BAS>::max()
|
||||||
or std::numeric_limits<SUB>::lowest() > std::numeric_limits<BAS>::lowest()
|
or numeric_limits<SUB>::lowest() > numeric_limits<BAS>::lowest()
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -122,6 +84,7 @@ _Pragma("GCC diagnostic ignored \"-Wsign-compare\"")
|
||||||
_Pragma("GCC diagnostic pop")
|
_Pragma("GCC diagnostic pop")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** generic helper to conform into a common value domain
|
/** generic helper to conform into a common value domain
|
||||||
* @tparam SUB the implied target value domain
|
* @tparam SUB the implied target value domain
|
||||||
* @tparam BAS source data type
|
* @tparam BAS source data type
|
||||||
|
|
@ -175,7 +138,7 @@ _Pragma("GCC diagnostic pop")
|
||||||
else
|
else
|
||||||
if constexpr (std::is_constructible_v<V, X const&>)
|
if constexpr (std::is_constructible_v<V, X const&>)
|
||||||
{
|
{
|
||||||
targetVal.~U();
|
targetVal.~V();
|
||||||
new(&targetVal) V (preClamp<V> (srcVal));
|
new(&targetVal) V (preClamp<V> (srcVal));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -183,62 +146,6 @@ _Pragma("GCC diagnostic pop")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
template<typename VAL, class IFA>
|
|
||||||
template<typename X, class PAR>
|
|
||||||
inline void
|
|
||||||
DomainSetup<VAL,IFA>::TypeHandlerImpl<X,PAR>::extractAs (X& targetVal, ValBuff const& valBuff) const
|
|
||||||
{
|
|
||||||
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) const
|
|
||||||
{
|
|
||||||
targetDomain.conform (target, asValue<VAL> (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<typename VAL, class IFA>
|
|
||||||
class TypedEntity
|
|
||||||
: public BaseDomain<VAL,IFA>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}} // namespace lib::par
|
}} // namespace lib::par
|
||||||
#endif /*LIB_PAR_TYPE_HANDLER_H*/
|
#endif /*LIB_PAR_TYPE_HANDLER_H*/
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue