/* PARAMETER.hpp - generic parameter values Copyright (C) 2008,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 parameter.hpp ** A generic typed value that can be discovered and automated. ** The actual data type of the parameter is an opaque implementation detail, ** yet value ranges and conversions are handled automatically. It is thus ** both a descriptor and an opaque container. Parameter elements can be copied, ** yet the presence of some specific parameter at a given point in the model ** constitutes an identity -- and the parameter descriptor can be tagged ** with that identity. ** ** @todo this is a draft and placeholder, as of 2026 */ #ifndef LIB_PAR_PARAMETER_H #define LIB_PAR_PARAMETER_H #include "lib/error.hpp" #include "lib/par/disposition.hpp" #include "lib/polymorphic-value.hpp" #include "lib/format-string.hpp" #include "lib/util.hpp" #include namespace lib { namespace par { namespace err = lumiera::error; namespace { static constexpr size_t PAYLOAD_SIZ = sizeof(ParamData) - sizeof(void*); ////////////////////////TICKET #1197 : this is a mess! PolymorphicValue adds the ADMIN_OVERHEAD (but our payload has already a VTable) using ParameterImplAPI = Disposition; using CopySupportMarker = polyvalue::CopySupport; using ParamContainer = PolymorphicValue; } using util::_Fmt; using util::unConst; /** * Descriptor and container to hold a generic parameter. * The value can be fixed or supplied by an automation function. */ class Parameter : protected ParamContainer { public: template VAL getVal() const; template void setVal (VAL&&); Parameter& assignValueFrom (Parameter const&); /* === Builder API === */ template class Builder; template static Builder forType() { return {}; } template static Builder> forValue (VAL&& initVal) { using PayloadType = std::decay_t; return forType() .withValue (std::forward (initVal)); } template static Builder from (Parameter const& prototype) { return {prototype.getVal() // note: may convert value type ,unConst(prototype).getPayload().getScaleID()}; } protected: template Parameter (IMP* typeTag, ARGS&&... args) : PolymorphicValue (typeTag, std::forward(args)...) { } }; template requires par_basetype class Parameter::Builder { VAL initVal_{}; uint scaleID_{0}; Scale scale_{}; public: Builder() = default; Builder (VAL initVal, uint scaleID) : initVal_{initVal} , scaleID_{scaleID} { } // default copy operations Parameter build() { using ImplPackage = ParamData; //////////////////////////////////////////TICKET #1197 : remove the necessity to inject the CopySupportMarker ImplPackage* typeTag{nullptr}; return scaleID_? Parameter{typeTag, initVal_, scaleID_} // pass-through scale unmodified : Parameter{typeTag, initVal_, std::move (scale_)}; } template Builder&& withValue (X&& initVal) { initVal_ = std::forward (initVal); return std::move(*this); } Builder&& maxVal (VAL upperBound) { if (scale_.minVal and *scale_.minVal > upperBound) throw err::Invalid{_Fmt{"invalid parameter build: maxVal(%s) must be above defined minVal(%s)"} % upperBound % *scale_.minVal}; maybeCloneScale(); scale_.maxVal = upperBound; return std::move(*this); } Builder&& minVal (VAL lowerBound) { if (scale_.maxVal and *scale_.maxVal < lowerBound) throw err::Invalid{_Fmt{"invalid parameter build: minVal(%s) must be below defined maxVal(%s)"} % lowerBound % *scale_.maxVal}; maybeCloneScale(); scale_.minVal = lowerBound; return std::move(*this); } Builder&& limited (VAL lowerBound, VAL upperBound) { if (lowerBound > upperBound) throw err::Invalid{_Fmt{"invalid parameter build: minVal(%s) <= maxVal(%s)"} % lowerBound % upperBound}; maybeCloneScale(); scale_.maxVal = upperBound; scale_.minVal = lowerBound; return std::move(*this); } Builder&& cyclicWrap (VAL lowerBound, VAL upperBound) { if constexpr (std::is_same_v) throw err::Logic {"attempt to configure a bool-parameter for cyclicWrap"}; if (lowerBound >= upperBound) throw err::Invalid{_Fmt{"invalid parameter build: cyclicWrap requires open interval minVal(%s) < maxVal(%s)"} % lowerBound % upperBound}; maybeCloneScale(); scale_.maxVal = upperBound; scale_.minVal = lowerBound; if constexpr (std::is_floating_point_v) { // ensure that the float-precision can actually handle the given interval scale_.cyclicLim = util::limit_cyclicWrap (upperBound - lowerBound); if (abs(*scale_.maxVal > *scale_.cyclicLim) ) throw err::Invalid{_Fmt{"invalid parameter build: maxVal(%s) for cyclicWrap is too large to handle precisely (limit: %s)"} % upperBound % *scale_.cyclicLim}; } else { // limit must be set but actual value does not matter (ints are precise) scale_.cyclicLim = std::numeric_limits::max(); } initVal_ = util::limited(lowerBound, initVal_, upperBound); return std::move(*this); } Builder&& useLogScale() { maybeCloneScale(); scale_.metric = NAT; return std::move(*this); } Builder&& useBinLogScale() { maybeCloneScale(); scale_.metric = BIN; return std::move(*this); } Builder&& useDecibelScale() { maybeCloneScale(); scale_.metric = DEC; return std::move(*this); } Builder&& markAsFactor() { maybeCloneScale(); scale_.neutral = VAL(1); return std::move(*this); } private: void maybeCloneScale() { if (not scaleID_) return; UNIMPLEMENTED ("extract and possibly type-convert the scale definition"); scaleID_ = 0; // use explicitly defined scale instead } }; template inline VAL Parameter::getVal() const { static_assert (isBaseType, "only preconfigured parameter BaseTypes are supported"); return unConst(this)->getPayload().extract(); } template inline void Parameter::setVal (VAL&& changedVal) { static_assert (isBaseType, "only preconfigured parameter BaseTypes are supported"); getPayload().setVal (changedVal); } /** transfer value with type conversion between two opaque parameters. * @remark this requires a double dispatch, defected back from the target */ inline Parameter& Parameter::assignValueFrom (Parameter const& srcParam) { unConst(srcParam).getPayload().assignValueTo (getPayload()); return *this; } }} // namespace lib::par #endif /*LIB_PAR_PARAMETER_H*/