Commit graph

529 commits

Author SHA1 Message Date
fc8dd9408a Parameters: establish requirements for a descriptor registry
Since the design established thus far calls for a fixed size container
and a size-constrained stack of virtual interfaces and implementation within,
it is clear that more diverse parts of the type and scale handling must be
forwarded into a second layer, maintained within a registry and keyed by ID.

Taking the prospective use into account, a two-level structure with a
fixed-size top-level index table and dynamically added extents seems to
cover all requirements, and allows for a lightweight, atomic-protected
read-access path.
2026-09-27 01:38:03 +02:00
da9b40df03 Parameters: resolve type precision problems with scale-joinig
The selection of the intermediary working type, used for exponential
and logarithmic functions when combining values from different scales,
was implemented too simplistic.

Following the example of the `NumberJoinType`, it seems preferrable
to spell out the decision logic explicitly. This allows to pick `long doubl`
as working type especially in cases where 64bit integral parameters are
involved, either as source or destination.

With these final tweaks, the `verify_valueJoining()` test is not complete,
presumably with path coverage of the conversion functions.

The Scale descriptor and operational logic is now defined to the point
that it can be integrated into the general Parameter implementation machinery.
2026-09-22 18:39:09 +02:00
fd5548b61b Parameters: verify use of a long-double target scale
Scrutinising the bounds of numeric precision.
As it turns out, most cases work as expected,
including the massive errors incurred by input-values given as float.

However: a logarithmic combination integral -> integral runs in loss of precision
So the selection logic for the intermediary working type is still overly simplistic!
2026-09-22 01:43:45 +02:00
00b0aa0c5b Parameters: expand logarithmic scale patch coverage
The control logic for the various combinations of linear and logarithmic scales is nasty,
and the added path coverage indeed unveiled several bugs.

One of these issues, an unjustified limitation on the input value type of `asLogarithm`,
convinced me to refactor the conversion functions into free functions,
since they effectively ''always span the relation of two scales'' --
writing them as member function introduces an unnecessary asymetry.
2026-09-21 22:10:18 +02:00
206f9635c9 Parameters: cover logarithmic scale conversions
this feature relies on the common logarithmic math,
yet with the additional twist that decibels need an additional factor of 10...
2026-09-21 05:42:33 +02:00
92b7b05ea2 Parameters: solve mixed signed / unsigned computation
There are some corner cases where the C++ number promotion rules can hurt us badly.
Devised a type selection logic to pick a suitable common numeric type explicitly,
using `std::common_type<T1,T2>` in most cases, yet handling explicitly the
case when the computation would wrap into unsigned; in such a mixed sign integral case,
we either use int64_t (when its precision suffices), or long double,
which can hold 64bit of mantissa without precision loss.
2026-09-21 01:05:53 +02:00
c0cff8abe6 Parameters: implementation for metric conversions
LIN -> LIN yet either additive or multiplicative
LOG -> LIN
LIN -> LOG
and different kinds of logarithms

all computations using a common intermediary numeric type,
preferably a suitably sized floating-point
2026-09-20 06:15:38 +02:00
0cbe9d7dd6 Parameters: prepare for a generic Scale-join operation
The `join()`-operation allows to combine a value pulled from automation
to be combined with a local ''base value'' stored in the parameter.

When both the source and the target parameter use the same Scale,
this operation is simple; it becomes more tricky once they don't --
because then linear / logarithmic and type conversions must be handled.

I now decided to provide a generic implementation in the Scale (descriptor),
while the enclosing parameter shall handle the dispatch when connecting
to a opaque source with another Scale. And this generic implementation
relies on the same technique as was used for the parameter type conversions,
thus it should be factored out into a library header.
2026-09-17 23:38:02 +02:00
9221fbeef1 Parameters: document and verify integration into cyclic Scale
- verify using an unsigned integer, for a typical ''angle scale''
- also verify a high-res floating-point scale, and demonstrate rounding issues
2026-09-17 01:17:40 +02:00
549216dba3 Parameters: integrate the cyclic-wrap function
a much more complete and robust implementation,
but requires a precision limit to be pre-computed in the floating-point case
2026-09-16 19:42:55 +02:00
6eb6ec48da Library: better short-hand name for long double
As with all those C++ base types, unfortunately they are giving you
only rough size guarantees. For Lumiera, I defined short hand names
some time ago, notably because the explicit long X and short X qualifiers
do not work with ''constructor syntax'' for direct conversions.

But I choose the name `f128` at that time -- which might raise ill-guided
expectations, since the `long double` is not that long on relevant linux 64bit
platforms; it can just hold 64bit of mantissa at least however.

As replacement, I'll use `lflp` now, because it has also 4 letters,
looks somewhat weird (and thus catches attention) and points into the
right direction. I considered `lfloat` as alternative, but that looks
too much like `float`, visually...
2026-09-21 01:05:53 +02:00
303edcb6a1 Library: verification for all floating-point types
finally I was able to come up with a rather concise test
that works with float, double and long double; especially
the aliasing due to rounding could be demonstrated, and
the precision limit is made plausible.

Interestingly, `std::fabs()` really works precise enough
so that a very large positive domain can be implemented
and tested with exact match
2026-09-16 04:23:55 +02:00
d0e6998b9c Library: investigate what could be tested with floating-point
The floating-point variant turns out to be difficult to test,
since it is never ''actually exact'' -- due to the nature of floating-points.

We can not expect reasonable behaviour at the domain boundaries,
even while in some cases the computation works (and in others it
goes infinite and is sanitised to `minVal`).

The limit for rejection is itself rather a heuristic;
thus it is difficult to show "wrong" behaviour beyond the limit,
while the limit itself is typically already so large that it is
hard to handle in test code -- at least when expecting visible results.
2026-09-15 21:53:56 +02:00
ce980d64bf Library: verify the new cyclicWrap library function
The implementation should cover all integral data types,
both signed and signed, over the entire value domain.

Verify that by testing the bounds and the complex computation path
2026-09-15 01:37:38 +02:00
29a39a0f7f Library: extend text coverage for util::floorwrap
These two helpers ''just happen to work'' in the full domain, and also for unsigned types.
I can not recall that I actually was aware of that, but it's nice.

Thus add some test cases to cover the bounds of the domain
2026-09-14 16:34:15 +02:00
c3c103e1a9 Parameters: mark out the base operations of a Scale
Scales are defined as descriptor records and will be used as const.
Besides some information functions, a Scale
 * allows to conform a value to be compliant with the Scale's constraints
 * can ''join'' and thus apply a ''feed'' on top of a ''base value'';
   this is the foundation for applying an automation function, while
   handling multiplicative and logarithmic cases properly
2026-09-12 00:39:11 +02:00
59fbb8e6b0 Parameters: consider what to conceptualise as a Scale
It is clear by now that a `Scale` will act as a refinement of a full-domain value type.
However, in order to develop the definition and builder notation for parameters,
a much more detailed understanding of scales is necessary -- including the question
where to draw a line between the complex structured `ParamSet` and a simple
`Parameter` that is conceived to be number-value-like.
2026-08-10 01:28:41 +02:00
5fbe08ebe0 Parameters: sketch a prototype-builder API
...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?
2026-09-06 01:39:09 +02:00
a4ae568601 Parameters: mapping out the next steps to draft
...I do ''not intend to go into a parameter deep-dive'' yet,
but I want to set out the basic capabilities to be expected...
2026-09-06 00:50:53 +02:00
f8c1222197 Parameters: work out a solution for an optimised code-path
Overall, this is a questionable endeavour...
 - I want a framework that can be extended flexibly
 - I am concerned about the performance penalty of that flexibility

What makes this situation problematic is the fact that I am deep in
prototyping work here, and have not the slightest idea as to what would be
the actual usage context. So I am lacking a point of reference, yet still
I am engaged into a very tricky (premature) optimisation...

Anyway. I have now found a way to integrate an optimised access code-path
in a safe way, while retaining the general case with flexible type conversion:
 - whenever a BaseDomain<X> is used, a corresponding type-ID is marked into the ID-Record
 - this allows when it is possible to side-step any type conversion,
 - in which case we can also forcibly side-step the virtual dispatch,
   and invoke directly the value-accessors of the default implementation,
   reaching into the embedded value buffer.
2026-09-05 01:29:51 +02:00
41adaa18ef Parameters: streamline automatic type conversion
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.
2026-09-03 01:42:18 +02:00
535e372827 Parameters: resolve PolymorpicValue setup
`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.
2026-09-03 00:10:37 +02:00
e0fa980f4c Parameters: introduce ID-Record into the impl-interface
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.
2026-09-03 00:02:16 +02:00
f6cc5fe1d9 Parameters: simple testcase PASS (see #1431)
Add value access implementation of the Provision interface
by direct cast and access to the embedded value in `ParamData<VAL>`;
obviously this is an indirect dispatch, based on the actual type `VAL`

The test demonstrates the basic machinery built thus far
 - have an embedded container with inline storage
 - dispatch using type information encoded into the VTable
2026-09-02 01:48:51 +02:00
fca0c08b22 Parameters: now able to define the complete implementation layout
- `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
2026-09-01 23:15:09 +02:00
9665c5d320 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
2026-09-01 22:55:56 +02:00
3bd64935f3 Parameters: reorganise TypeHandler interface
The original design (based on Alexandrescu) is more elegant,
but has the downside to bloat the VTable size due to the mix-in
arrangement used to generate the interface.

The ongoing design has revealed a tight constraint on element size
for the parameters; thus the change to a linearised inheritance chain.

The overall purpose remains the same: the ability to generate a
double-dispatch type conversion, so that the actual value type of the
parameter can be treated as an implicit implementation detail.
2026-09-01 20:46:40 +02:00
1f29d202b4 Parameters: look for a suitable arrangement of interfaces
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).
2026-09-01 01:53:43 +02:00
d582b95e7c Parameters: detailed the prototype design sketch (see #1431)
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....
2026-08-31 00:24:37 +02:00
4cdf01bc72 Parameters: better API for opaque transfer
The type-independent data transfer is based on a double-dispatch,
which is implemented as visitor pattern; so the operation must start
from the source parameter's VTable and be ''deflected back'' to the receiver

This completes the base operations required to establish a »Domain« (data type)
Adding more test coverage to demonstrate usage patterns...
2026-07-25 21:52:51 +02:00
c60e7d35c6 Parameters: reconsider where to use the visitor-pattern
...turns out as better to code two distinct low-level operations,
albeit implemented by delegation to a single generic assignment implementation.

The visitor patter thus becomes relocated to the high-level interface,
allowing to assign one opaque parameter to another one
2026-07-24 19:44:20 +02:00
a37e99a643 Parameters: attempt to base all access on a single implementation
However then the problem is that we know ''our domain'' but not the ''other domain''...

... and thus this forces one direction to pass through a double dispatch
2026-07-23 01:46:27 +02:00
e48c03e35d Parameters: verify the clamping operation
- systematic testing of boundary values for various numeric types
 - verify even clamping of an uint-128 when targeting float
 - verify signed / unsigned of different rank
 - demonstrate the clamping now integrated into `extractAs()`

As an aside, move the `isNeg()` function (safe test for negative numbers)
into util.hpp and fix related bugs with unsigned there.
2026-07-22 23:10:02 +02:00
e3b6b87e93 Parameters: look for ways to properly clamp signed / unsigned
Signed and unsigned integral types have a partial overlap of definition domains,
yet due to the automatic promotion rules it is impossible to write
a domain clamping function directly, since `std::numeric_limits` returns
the domain boundaries in the given type, and `int(-1) < uint(0)`
evaluates to false, due to int being is promoted to uint.

The attempt to circumvent this problem here relies on a check if the
comparison crosses the zero point, and otherwise relies explicitly
on the promotion to the number with the larger domain.
2026-07-22 01:17:13 +02:00
11bb3bce73 Parameters: draft a family of pre-conditioning operations
- we need a generic operation to clamp to the smaller value domain
 - furthermore we need a virtual function to apply a type specific limiter

__TODO__: this draft does not handle properly signed vs. unsigned types
2026-07-21 01:34:37 +02:00
7d35790e11 Parameters: implement simple value extraction for base types
...so this is not yet a visitor, rather the building blocks required
to be able to extract values and to assign and conform values

And my intention is to build something akin to a set of conversion rules
2026-07-20 01:10:19 +02:00
82b7acac57 Parameters: pick up work on type handling experiments
...after an extended break where I focused on learning IO_URING,
which is the topic of my upcoming talk at Froscon-2026;
(I built a prototype to read and display video with async IO)

Today ''I have re-read my design notes'' and the analysis seems to hold up well;
furthermore I solved the coding problem how to generate type handlers for a list of types.
2026-07-19 22:48:50 +02:00
532408a305 Parameters: design sketch for base-domain and conversion
While it is unfortunate that I seem to get deeper and deeper into that topic...
 - it should be noted that data size matters most (pervasive use expected)
 - the design is leaning towards multiple functional closures
 - both together suggest to use a classical virtual interface, without much ado
 - yet parameters must be copyable values, to allow for evaluation graphs and snapshots
 - so this turns out as yet another use case for ''polymorphic values''
 - furthermore, fluid usage requires flexible conversions between result values
 - given the situation with numeric value types and strings, this implies ''to build a visitor''

🠲 ''putting that work aside for the moment, since I need to focus on my IO_URING research (for FrOSCon 26)''
2026-07-06 01:17:31 +02:00
5df0252cc1 Parameters: preliminary requirement analysis
In the current stage, a design-draft is required that is sufficiently adequate
to be used as code placeholder and to evolve into the full specification eventually.

 - we need a single parameter container type with value semantics
 - parameters must support a selection of basic value types, with conversions
 - the value range can be limited, and a parameter can be associated
   with a ''Scale'' (stepped, continuous, logarithmic)
 - a parameter can be linked to a functional source
 - parameters are (virtually) copyable
2026-07-03 19:55:55 +02:00
36684ff092 Parameters: start a design draft
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''
2026-07-01 01:50:26 +02:00
eeabbb4f60 Fix test breakage
- some tests are notoriously flaky
- increase resource limits for the testsuite to allow stronger stress tests
- provide a "quick" variant of BufferProviderLoad_test with lowered requirements

Furthermore, investigate test scaffolding in `DummyBuilderContext`,
which was broken due to the clean-up in `DiagnosticOutputSlot`.

It seems that only some placeholder instances are required here,
so that we might be successful with just instantiating the `UnimplementedConnection`

__TODO__: need to supply slightly more implementation into the latter,
so that we're able at least to ''create'' such a connection stub.
2026-06-01 01:47:15 +02:00
cd7581ba76 BufferProvider: investigate tricky race at thread shutdown
...even while this situation is unlikely, it needs to be considered:
A worker thread might terminate unexpectedly, due to an error;
all thread-local objects will be destroyed automatically — while the
`EngineBufferManager` might be processing further allocations requested
by that very thread, and dispatch them towards an already destroyed `LocalMemPool`

This turns out to be a nasty problem, since the whole setup was created
to allow the workers to operate independently from each other; any handshake
or registration/deregistration mechanism would require a lock of sorts,
thereby defeating the whole purpose of the setup.

The best solution I could find is to rely on a instance marker hash,
that can be checked with an atomic read prior to each dispatch of a
memory allocation. The destructor of the local pool would then be able
to ''disengage'', by invalidating that hash.

To evaluate this solution, I developed this mechanism
as a free-standing library component, called `LiveMark`
2026-05-21 01:47:11 +02:00
7448b81d49 Library: improve initial population of ScopedCollection
This is a collection with unique-ownership,
and the ability to create all content objects in ''RAII style''
from the constructor call, using a ''population functor''.

It is often used to maintain a collection of resource handles,
and especially to start a series of threads (for testing).

With the variadic λ-captures from C++20, it is possible to rewrite
the most frequently used population functors to be easier to use.
Notably we can now bind constructor arguments, that will be passed
to each of the created elements — a prominent example being the
thread function when launching several threads. Furthermore,
I added a constructor variant that consumes a Lumiera Iterator.
Adding this kind of generic variants is possible now with C++20,
since overload resolution takes concepts into account.
2026-05-14 22:46:06 +02:00
3a491855e7 Library: introduce a concept for the »Lumiera Forward Iterator«
Well, finally.

Lumiera Iterators were conceived as a concept from the very beginning,
and this idea dates back even before C++11. Furthermore, over time some
''trait templates'' have been developed, to detect if some template argument
might match the known pattern of functions associated with a Lumiera Iterator.

Meanwhile this concept is used pervasively in the whole code base, and also
the traits templates proved to be reliable. It therefore seems that the time is ripe
to gradually introduce a genuine **concept** `lum_iter`

I'll take a defensive approach here, since any mistake would lead to
a massive breakage of compilation. The new concept is defined simply
by delegating to to the tried-and-tested trait template, which is
also renamed `can_IterForEach` ⟼ `can_LumieraIter`

One immediate benefit is that the compiler is now able to select
overloads based on that concept, which allows to drop the tricky
selection of variants based on the SFINAE technique and `enable_if`.

For validation, one very heavily used case of that pattern was switched
in `lib/format-util.hpp', which notably lies in the code-path used by `util::join`
2026-05-14 16:32:54 +02:00
9bd3e550dc BufferProvider: improve configuration of thread-local dispatch
Since thread_local implies a static setup, we need a way how to
build a new service instance. Usually this would be a default construction,
yet there might be services with special init requirements.

Using an explicit template parameter for this setup configuration
allows to make the primary template parameter just the service itself,
which makes the default usage much clearer.
2026-04-18 16:25:14 +02:00
a06ef95e94 BufferProvider: skeleton for thread-local service dispatch
- Use a ''monostate'' as access point to thread-local service instances
 - rely on a configuration-context to setup the actual service
 - investigate runtime overhead using a multithreaded microbenchmark
2026-04-18 01:10:27 +02:00
a0d83b4fe8 Library: investigate design for an opaque inline functor
This is a design study to find out what would be required
to build an opaque function wrapper, similar to `std::function`,
yet without using any heap memory ever.

Such a building block would allow for some interesting options
for a well structured design of elaborate data structures.

This changeset provides an implementation draft.

__Two important findings to note__:
 * it is impossible to get away with just a single function pointer
 * because at the very least we need to invoke the destructor of the
   embedded delegate. Thus something akin to a "manager" is required.
 * this implies that the component would not be so efficient as I hoped,
   since it requires two pointers and requires to emit code for each
   type of delegate.
 * by making the component **NonCopyable**, I avoided the complexities
   of virtual copy support. In case the latter is desired, it would be
   much simpler to switch to a classical OO approach with a VTable
   and use our `PolymorphicValue` wrapper to handle copy and cloning.
2026-04-08 01:53:53 +02:00
0760525ea5 Library: deep dive into type reconciliation and tuple handling
This commit is the aggregated result of extended research
that is only loosely related to current development topics.

One theme was the construction of nested data structures using tuples,
which faces some difficulties due to tuple manipulations being limited
almost entirely to compile-time processing. However, the barrier between
compile-time and runtime can be overcome with the help of a dispatcher
table -- which leads to the secondary problem to handle the diverse
tuple elements with a common, generic functor. Such a functor can be
constructed, if and only if those element types are ''reconcilable''

The STDLIB provides two metafunctions, `std::common_type` and
`std::common_refernce`, which however pose some challenges for the
task at hand, since they include additional processing. In the end,
a custom version of the ''basic type reconciliation'' was implemented
and tested, and then used as foundation to build a dispatcher table
for runtime-indexed access and iteration over tuples.

As an aside, several deeply hidden bugs in our library code were resolved,
and the tuple-helpers, tuple string rendering and associated utilities
were regrouped and modernised.

As result of this research work, the following components are provided:
- meta/common-result
- meta/fun-trampoline
- meta/tuple-index-adaptor
2026-04-07 06:32:23 +02:00
a835a796b2 Library: investigate further ramifications and clean-up Itertools
The detailed analysis of corner cases indicated a lot of hidden dangers
when stretching the `IterExplorer` design beyond its limits.

By deliberate choice, this design was kept open so that advanced cohesive
implementation structures can be built. It is even possible ''and tolerated''
to rely on ''references'' to some degree -- assuming however that the
referred memory location remains stable.

As it turned out, these basic assumptions can be broken by a combination
of read-ahead processing (as is necessary for grouping and filtering),
together with adaptor objects embedded as value, especially when
the resulting pipeline object is then moved or copied during evaluation.

While my general attitude towards such extremely clever implementation
constructs is "don't shoot yourself into your foot unless you need to" --
it seems prudent to clean-up some crappy code from the first attempts
towards an iterator pipeline -- notably some of the »Itertools«.
2026-04-01 01:49:36 +02:00
40d3167d1a Library: integrate automatic adapters to flatten iterable sources
This expands the processing scheme developed thus far, so that
an arbitrary nested iterable can be handled, as long as it can be
adapted by the generic mechanisms provided by IterExplorer.

Notably there is now a type pattern match to distinguish various cases,
even spanning the ''rather esoteric'' possibility that an underlying
iterator produces STL collections by-value.

By examining these special cases, the limitations of the `IteratorExplorer`
design were highlighted, and a number of tricky bugs could be fixed.
2026-03-28 23:27:23 +01:00