Largely this test covers already established functionality.
It is ''somewhat difficult'' to prove that an opaque type is present,
beyond showing that values are truncated or accommodated.
At the beginning of the implementation-draft, I considered a double-dispatch,
but concluded that this would be too much overhead for the standard case;
the drafted visitor-style dispatch was "parked" as `Domain::transferTo`.
It turned out that this implementation is not directly usable, since it
assumes that »the value buffer« is accessible, albeit as opaque ValBuff&.
As the design matured, I decided that the presence of a »Buffer« should better
not be assumed, and thus flipped the high-level-APIs around. But it is
easily possible to implement this double-dispatch scheme anew on top of
the now settled implementation scheme:
- `Disposition::assignValueTo (Disposition&)` invoked on the source side
- implemented in `ParamData<VAL, IFA>::assignValueTo (Disposition&)`,
and there delegating to the templated front-end setter
`Disposition::setVal(X)` (which performs the second dispatch step)
It remains to be seen if such an opaque value assignement is required in practice
- define lower bound, upper bound or both for a limited scale
- setup a cyclic wrapping scale
- mark the scale as logarithmic, binary log or decibels
- mark the parameter as a factor value (important in interplay with log scales)
BUT there is a problem: when the original parameter (used as prototype)
''does already define a scale'', then we need a way to get a clone
of this scale definition -- and this involves type conversion
In-depth analysis indicated that an ''opaque solution'' would be possible,
yet complex to implement in detail
- a Builder interface would be required
- this interface needs to package all given param values as ValBuff
- furthermore, each such value must be accompanied with a BaseTypeID
- the actual builder would then become ''a PImpl''
- it could be either implemented as heap-allocated with unique_ptr
- or be a `PolymorphicValue` itself (in which case the builder front-end
should be non-copyable and used by reference, inline in a builder expression
- in any case, the front-End builder would proxy all calls to the actual builder
- and the latter would return an opaque parameter at the end
This setup would even allow to use ''full type erasure'' on the Parameter front-end,
and might reduce the ''header inclusion tax''
BUT -- that being said, if we just require the user to specify the desired type explicitly,
not only when building a new parameter, but also when cloning a parameter from a prototype,
then the actual implementation could use one simple builder object and all the complexities
outlined above could be skipped.
So the latter is what we do for now -- since this whole effort is rather a
design draft and should enable to explore actual usages of the parameter framework.
At a later point, the elaborate version could still be implemented
...since we do not check beforehand what value was specified;
it seems preferable to do this ''in the parameter implementation'',
as the builder can be expected to get a broad API, while the ParamData
is the natural place to enforce an invariant
This demonstrates how a Scale (descriptor) can be handled
in the simple case where the value type is known.
There is ''no deduplication'' -- every Scale definition
gets a new Slot.
The scale is then retrieved later and used to conform values,
as demonstrated with a simple `maxValue()`
Use the simplest possible example, a parameter with limited `maxVal()`.
Then find out where in the data patch the Scale-conforming operation
must be hooked in.
The decision was made to integrate **everything inline** for maximum performance.
An increased ''include tax'' is incurred, and thus the Parameter-Builder
should be split off into a separate header.
The static Scale-Registry is now part of `TypedEntity` (type-handler.hpp)
and will thus instantiated once per actual parameter value type.
⟹ the Scale-ID is a **runtime value** and **depends** on the BaseType-ID
...yet the actual problem remains that the value type of the
prototype remains a runtime-dynamic hidden implementation trait.
How can we remould the scale hidden within a parameter, while
in the regular setup, the scale is implemented as an immutable
entry placed into a registry?
Can now express the automatic conversion between all allowed `BaseTypes`
by invoking the `TypeHandler` interface directly. The interfaces were streamlined
and storage size is managed automatically at compile time.
`PolymorphicValue` incurs some quite confusing requirements due to
the clumsy way how the instance handling and copy support must be injected.
As pointed out in #1197, with modern C++, this could be much simplified.
An overhaul of `PolymorphicValue` seems urgent thus -- in the meanwhile
we need to introduce more flexibiltiy with base-class template parameters,
so that we can inject the CopySupport interface at usage site of the
complete implementation class chain. Furthermore, the size of the VTable
is added automatically to the storage requirements in `PolymorphicValue`,
so we must subtract it when specifying the payload size by a compile-time
expression based on `sizeof(ParamData<double>)`
With these tweaks, we can now keep the limit of 4 «slots» (32 bytes)
and still reserve 64bit for ID and type flags.
The solution from yesterday works, but is needlessly indirect.
However, if we want to circumvent ''any indirection'' in the
simple standard case, then it turns out to be necessary to
have some kind of ''flag store'' already in the `Disposition`
(the implementation service interface). Because otherwise
it would be impossible to trigger the short-circuit access
without going through some kind of indirection.
Need to define a generic ID-Record with some flexible extension space
that can be used by subclasses; and notably we need to generate some
base-type-IDs on a static level -- which should be possible, since all
allowed BaseTypes are defined as Type-Sequence.
...and in addition check that the defined value type
is consistent with the list of pre-configured `BaseTypes`
Signed-off-by: Ichthyostega <prg@ichthyostega.de>
turns out to be quite tricky, due to the overly convoluted implementation
of PolymorphicValue (which looks quite dated, generally speaking,
and could use a complete overhaul).
- pass the basic value type as template parameter through the Builder
- allow for the ability to define an initial value for the parameter
- invoke the protected constructor of PolymorphicValue, directly passing
a ''type tag'' to specify the implementation data package to be placed
into the embedded data buffer
- `Disposition` is the service-API used by the front-End container
- `ParamData<TY>` is a complete implementation with a inline-value buffer
- The front-End is based on the `PolymorphicValue` container from our support-Lib
While the role of all the relevant interfaces has been determined,
it still remains difficult to establish a sensible relationship.
Which one is the most fundamental interface?
After a lot of mulling, it seems that the `Provision` interface
should be used as foundation, since it describes the very core
of the parameter concept developed here -- even while the actual
form of the operations must be ''somewhat cunningly reversed'',
so that the knowledge regarding the underlying type can be
kept an opaque implementation detail.
The `Domain` interface would then be layered on top, and describes
the capability to transfer values between buffers marked with different
domains (which effectively requires a double-dispatch in the general case).
Last week, I resumed the work that had been suspended
due to my focus on this year’s Froscon talk (on `io_uring`).
I have reconsidered my previous design ideas and sought to
arrive at a coherent design. Nevertheless, these are preliminary,
exploratory design studies that should be regarded as a basis
for future work rather....
Parameters and Automation will certainly play a crucial role eventually.
They will form a network of relationships to cover all further concerns that can not directly be conceptualised as arrangement or processing.
A shift in the setup of the Job-Functor arguments necessitates to introduce a `Parameter` type — ''albeit just as placeholder''
As a follow-up to the decisions regarding Render Job arguments,
it should be noted that the `ProcessKey` in the `TurnoutSystem` becomes redundant;
we'd need some access to ''generic Parameters'' rather