Commit graph

6705 commits

Author SHA1 Message Date
bf41474004 Library: investigate Scheduler test failures
...which turn out not to be due to the PRNG changes
 * the SchedulerCommutator_test was inadvertently broken 2024-04-10
 * SchedulerStress_test simply runs for 4min, which is not tolerated by our Testsuite setup

see also:
5b62438eb
2024-11-15 02:20:36 +01:00
7ed8486774 Library: rework detection of ''same object''
We use the memory address to detect reference to ''the same language object.''
While primarily a testing tool, this predicate is also used in the
core application at places, especially to prevent self-assignment
and to handle custom allocations.

It turns out that actually we need two flavours for convenient usage
 - `isSameObject` uses strict comparison of address and accepts only references
 - `isSameAdr` can also accept pointers and even void*, but will dereference pointers
This leads to some further improvements of helper utilities related to memory addresses...
2024-11-15 00:11:14 +01:00
766da84a62 Library: fix failed tests(1) -- Rational_test
Problems in `Rational_test` were caused by `#include' reorderings regarding ''rational'' and ''intgral'' numbers.

The actual root cause is the fact that `FSecs` is only a typedef,
which prevents us from providing a string conversion for rational numbers without ambiguity
2024-11-14 05:27:14 +01:00
0b9e184fa3 Library: replace usages of rand() in the whole code base
* most usages are drop-in replacements
 * occasionally the other convenience functions can be used
 * verify call-paths from core code to identify usages
 * ensure reseeding for all tests involving some kind of randomness...

__Note__: some tests were not yet converted,
since their usage of randomness is actually not thread-safe.
This problem existed previously, since also `rand()` is not thread safe,
albeit in most cases it is possible to ignore this problem, as
''garbled internal state'' is also somehow „random“
2024-11-13 04:23:46 +01:00
064484450e Library: adapt some existing usages to the convenience API 2024-11-12 22:35:54 +01:00
2883a8619f Library: investigate usage of rand() and consider replacement
As it turns out, by far margin we mostly use rand() to generate
test values within a limited interval, using the ''modulo trick''
and thus excluding the upper bound.

Looking into the implementation of the distributions in the
libStdC++ shows that ''constructing'' a distribution on-the-fly
is cheap and boils down to checking and then storing the bounds;
so basically there is no need to keep ''cached distribution objects''
around, because for all practical purposes these behave like free functions

What is required occasionally is a non-zero HashValue, and sometimes
an interval of floating-point number or a normal distribution seem useful.

Providing these as free-standing convenience functions,
implicitly accessing the default PRNG.
2024-11-12 21:10:14 +01:00
ce2116fccd Library: option to provide an explicit random seed for tests
* add new option to the commandline option parser
 * pass this as std::optional to the test-suite constructor
 * use this value optionally to inject a fixed value on re-seeding
 * provide diagnostic output to show the actual seed value used
2024-11-12 15:49:15 +01:00
cffa2cd6c2 Library: implement test access to hidden global RandomSequencer
this seems to be the ''classical problem situation''
where a »clean« Dependency-Injection would require to waste storage
for a pointer to the same global resource in each and every distinct test class.

Since the Test-Suite is effectively global — even more so now due to
the reliance on "the" global `RandomSequencer` (PNRG) — we'll have to
bite the bullet and access a global static variable hidden behind teh scenes.
2024-11-10 16:22:46 +01:00
c13d6d45f4 Library: add new API for random seeding
...to the base-class of all tests
 * `seedRand()` shall be invoked by every test using randomisation
   * it will draw a new seed for the implicit default-PRNG
   * it will document this seed value
   * but when a seed was given via cmdline, it will inject that instead
 * `makeRandGen()` will create a new dedicated generator instance,
   attached (by seeding) to the current default-PRNG

It is not clear yet how to pass the actual `SeedNucleus`, which
for obvious reasons must be maintained by the `test::Suite`
2024-11-10 04:40:39 +01:00
92bc044e9e Library: consider how to handle randomness in tests
Using random or pseudo-random numbers as input for tests
can be a very effective tool to spot unintended behaviour in
corner cases, and also helps writing more principled test verifications.
However, investigating failures in randomised tests can be challenging.

A well-proven solution is to exploit the **determinism** of pseudo-random-numbers
by documenting a randomly generated seed, that can be re-injected for investigation.

Up to now, most tests rely on the old library function `rand()`, while
at some places already the C++ standard framework for random number generation
is used, packaged into a custom wrapper. Adding adequate support for
documented seed values seems to be easy to achieve, after switching
existing usages of `rand()` to a suitable drop-in replacement.

After some consideration, I decided ''against'' wiring random generator instances
explicitly, while allowing to do so on occasion, when necessary. Thus
the planned seeding mechanism will rather re-seed a ''implicit default''
generator, which could then be used to construct explicit generator instances
when required (e.g. for multithreaded tests)

As a starting point, this changeset replaces the `randomise()` API call
by a direct access to the ''reseeding functionality'' exposed by the
C++ framework and all default generators. Since we already provide a
dedicated static instance of the plattform entropy source, re-randomisation
can be achieved by seeding from there.

NOTE: there was extended debate in the net, questioning the viability
of the `std::random_seq` -- these arguments, while valid from a theoretical
point of view, seem rather moot when placed into a practical context,
where even 2^32 different generation-paths(cycles) are more than enough
to provide sufficient diffusion of results (unless the goal is really to
engage into Monte-Carlo simulations for scientific research or large model
simulations).

Notable most of the more catchy reprovals raised by Melissa O'Neill
have been refuted by experts of the field, even while being still propagated
at various places in the net, often combined with promoting PCG-Random.
2024-11-10 03:25:45 +01:00
71af21ffd6 Library: clarify name of index-based iterator
Originally, this helper was called `IterIndex`, thereby following a
common naming scheme of iteration-related facilities in Lumiera, e.g.
 * `IterAdapter`
 * `IterExplorer`
 * `IterSource`

However, I myself was not able to recall this name, and found myself
now for the second time unable to find this piece of code, even while
still able to recall vaguely that I had written something of this kind.
(and unable to find it by a text search for "index", for obvious reasons)

So, on a second thought, the original name is confusing: we do not create
an index of / for iterators; rather we are iterating an index. So this
is what it should be called...
2024-11-09 22:43:05 +01:00
7960017403 Invocation: add some test coverage for the basic genrator function
Nothing surprising here...

Writing just some dull tests to avoid biting my nails while watching the US election....
2024-11-06 04:13:49 +01:00
c04a465134 Invocation: add some test-data manipulation functions
This is the first step towards a »Test Domain Ongology« #1372,
which is a systematic arrangement of test-dummy functionality assumed
to mirror the actual media processing functionality present in external libs.

Each media-processing library not only provides functions to crunch data,
but also establishes a framework of entities and classification to determine
what »media« is an how it is structured and can be generated, transformed
and qualified. Since a essential goal for Lumiera is to be **library agnostic,**
it is important to avoid naïvely to take some popular library's choices
as universal truth regarding structure and nature of »media« as such.
Rather, the architecture of the Lumiera Render Engine must be kept
sufficiently open to accommodate the working style of various libraries,
even ones not known today.

To validate this architectural openness, we use a set of test functions
unrelated to any existing library to validate access to and usage of
rendering functionality — followed by further steps to adopt existing
popular libraries like **FFmpeg** or **Gstreamer**, without tilting
the basic structure of the Render Engine one way or the other.
2024-11-05 21:23:13 +01:00
a84dbd7bfb Invocation: develop an abbreviated node spec
showing the Node-symbol and a reduced rendering of
either the predecessor or a collection of source nodes.

For this we need functionality to traverse the node graph depth-first
and collect all leaf nodes (which are the source nodes without predecessor);
such can be implemented with the help of the expandAll() functionality
of `lib::IterExplorer`. In addition we need to collect, sort and deduplicate
all the source-node specs; since this is a common requirement, a new
convenience builder was added to `lib::IterExplorer`
2024-11-05 03:56:38 +01:00
85e2966975 Invocation: implement deduplication of spec strings
* verify hash and identity of the generated `ProcID` records
 * also verify format of the generated Proc-Spec for a `Turnout`
2024-11-04 03:14:41 +01:00
53ac1911e7 Invocation: render a processing-spec for a port 2024-11-04 02:02:58 +01:00
5df93f01fc Invocation: pass symbolic spec through the node builder
...taking into account the prospecive usage context
where the builder expressions will be invoked from within
a media-library plug-in, using std::string_view to pass
the symbolic information seems like a good fit, because
the given spec will typically be assembled from some
building blocks, and thus in itself not be literal data.
2024-11-03 22:55:06 +01:00
4b6d812578 Invocation: provide access to a deduplicated ProcID
...as follow-up to yesterday's decisions
 - each Port will just feature a (stable) reference to a ProcID record
 - which is deduplicated and likewise refers to deduplicated symbolic tags
 - and further spec and hash values are computed on-demand by this entity

__Note__: all functionality belonging to the ''Builder'' can be assumed to run **non-concurrent**
2024-11-03 21:03:34 +01:00
f8642b3459 Invocation: consider how to establish a stable cache key
Building a precise Frame Cache is a tough job, and is doomed to fail
when attempting to tie cache invalidation to state changes. The only
viable path is to create a system of systematic tagging of processing
steps, and use this as foundation for chained hash values, linked
in accordance to the actual processing structure.

This is complicated by the secondary concern of maintaining memory efficacy
for the render node model, which can be expected to grow to massive scale.
And even while this invocation can not be fully devised right now,
an attempt can be made to build a foundation that is not outright
wasteful, by detaching the logical information from the specific
weaving pattern used for implementation, and by minimising the
representation in memory and computing the compound information
on-demand....
2024-11-03 03:06:54 +01:00
96d30cb5e7 Invocation: further considerations regarding diagnostics
Requirement analysis indicates that a »Node ID« is rather tangential
to the core operation of calculating media; the only infliction point
seems to be the generation of ''systematic cache keys.''

A spec — especially for the `Turnout` however is very relevant for
diagnostics, error reporting and unit testing. So we are in the
difficult situation where rather elaborate functionality is
required only for a secondary concern, and moreover the
node data structure imposes a critical memory leverage.
2024-11-02 02:57:35 +01:00
aab8278579 Invocation: Analysis regarding node and turnout identity
The immediate next goal is to verify properties of render nodes
generated by the builder framework; two kinds of validations
can be distinguished
 * structural aspects of the wiring
 * the fact that processing functionality is invoked in proper order

Looking into the structural aspects brings about the necessity
to identify the actual processing function bound into some functor.
Some recapitulation of goals and requirements revealed, that this
can not be a merely technical identity record — because the intention
is to base the ''cache key'' on chained processing node identities,
so that the key is stable as long as the user-visible results will be
equivalent. And while structural data can be aggregated, at the
core this information must be provided by the scheme embedded
into the domain ontology, which is tasked with invoking the
builder in order to implement a ''specific processing-asset''
2024-11-01 03:51:53 +01:00
9022a69a71 Invocation: simplest render-node test PASS
Review the achievements from the last days and map out the further path
for test-driven build-up of a render-node network and invocation.

Notably ''several layers of prototyping'' are in the works now;
it is important to understand the purpose of each such round of
prototyping and to draw the necessary conclusions after closing out.

The next topic to investigate relates to the ''identity'' of nodes and
ports within nodes; this entails to generate a ''symbolic spec'' that
can be verified and used as base for a systematic hash-ID and cache-key...
2024-10-27 02:45:15 +02:00
c29c10fd62 Invocation: runtime error checks for auto-wiring
Since it would in fact be possible to access and write beyond the configured storage,
simply by using the builder API without considering consistency,
it seems advisable to use explicit runtime checks here, instead of
only assertions, and to throw an exception when violating bounds.

Moreover, unsuccessfully attempted to better arrange the functionality
between PortBuilder and WeavingBuilder; seemingly we have an rather tight
coupling here, and also the expectations regarding the processing function
seem to be too tight (but that's the reason why it's an prototype...)
2024-10-26 04:11:36 +02:00
d91d0b5926 Invocation: provide functionality to connect lead ports explicitly
...which then also allow to fill in the missing parts for the
default 1:1 wiring scheme, which connects each »input slot«
of the processing function with the corresponding ''lead node''
2024-10-25 18:13:55 +02:00
43373e11e7 Invocation: prepare mechanism for default input wiring
The intention is to offer an automatic 1:1 association
between the »input parameter slots« of the processing function
and the ''lead nodes,'' thereby always using the same default
port, corresponding to the current port number under construction.

Unfortunately, the preceding refactoring removed the information
necessary for a simple implementation, as the port array is now
built up late, in the final build() function...
2024-10-25 05:10:16 +02:00
0144049f9d Invocation: reconsider data access and allocator usage from Builder
The next step is to round out the first prototypical implementation,
which requires access to ''lead node ports'' and thereby generally
places focus on the interplay of ''data builders'' within the ongoing
build process. While the prototype still uses the fall-back to simple
heap allocation, notably the intended usage will require to wire-through
the connection to a single `AllocationCluster`. This poses some
challenge, since further ''data builders'' will be added step-wise,
implying that this wiring can not be completed at construction time.

Thus it seems indicated to slightly open-up the internal allocator
policy base template used by `lib::SeveralBuilder` to allow for some
kind of ''cross building'' based on a shared compatible base allocator
type, so that the allocation policy wiring can be passed-on from an
existing `SeveralBuilder`
2024-10-23 16:27:09 +02:00
554a64e212 Invocation: solve passing of the function definition
- the chaining constructor is picked reliably when the
  slicing is done by a direct static_cast

- the function definition can be passed reliably in all cases
  after it has been ''decayed,'' which is done here simply by
  taking it by-value. This is adequate, since the function
  definition must be copied / inlined for each invocation.

With these fixes, the simplest test case now for the first time
**runs through without failure**
2024-10-22 05:59:00 +02:00
df37fec500 Invocation: switch WeavingBuilder to produce the result via λ
This change allows to disentangle the usages of `lib::SeveralBuilder`,
so that at any time during the build process only a single instance is
actively populated, all in one row — and thus the required storage can
either be pre-allocated, or dynamically extended and shrinked (when
filling elements into the last `SeveralBuilder` currently activated)

By packaging into a λ-closure, the building of the actual `Port`
implementation objects (≙ `Turnout` instances) is delayed until the
very end of the build process, and then unloaded into yet another
`lib::Several` in one strike. Temporarily, those building functor
objects are „hidden“ in the current stack frame, as a new `NodeBuilder`
instance is dropped off with an adapted type parameter (embedding the
λ-type produced by the last nested `PortBuilder` invocation, while
inheriting from previous ones.

However, defining a special constructor to cause this »chaining«
poses some challenge (regarding overload resolution). Moreover,
since the actual processing function shall be embedded directly
(as opposed to wrapping it into a `std::function`), further problems
can arise when this function is given as a ''function reference''
2024-10-22 03:20:50 +02:00
0de7905444 Invocation: introduce pattern data holder into NodeBuilder
- add an unspecified data holder type into the NodeBuilder
- establish ability to cross-build a `NodeBuilder` with adapted data holder
2024-10-21 04:40:51 +02:00
b4aee6fba8 Invocation: work out solution for a precisely fitting allocation
Conduct in-depth analysis to handle a secondary, implementation-related
(and frankly quite challenging) concern regarding the placement of node
and port connectivity data in memory. The intention is for the low-level
model to use a custom data structure based on `lib::Several`, allowing for
flexible and compact arrangement of the connectivity descriptors within
tiled memory blocks, which can then later be discarded in bulk, whenever
a segment of the render graph is superseded. Yet since the generated
descriptors are heterogeneous and, due to virtual functions, can not be
trivially copied, the corresponding placement invocations on the
data builder API must not be mixed, but rather given in ordered strikes
and preceded by a dimensioning call to pre-reserve a bulk of storage

However, doing so directly would jeopardise the open and flexible nature
of the node builder API, thereby creating a dangerous coupling between
the implementation levels of the node graph and of prospective library
wrapper plug-ins in charge of controlling details of the graph layout.

The solution devised here entails a functional helper data structure
created temporarily within the builder API stack frames; the detailed
and local type information provided from within the library plug-in
can thereby be embedded into opaque builder functors, allowing to
delay the actual data generation up until the final builder step,
at which point the complete number and size requirements of
connectivity data is known and can be used for dimensioning.
2024-10-20 21:52:00 +02:00
634df743f0 Invocation: investigate Problems with allocation-growing
This investigation was set off by a warning regarding an
unused argument in `SeveralBuilder`, using `AllocationPolicy::moveElem()`
This warning is correct and easy to fix, but (luckily) it brought my
attention to the fact that a `SeveralBuilder<Port>` can not grow dynamically,
which is somewhat mitigated by the default policy to pre-allocate several
elements, which would work to some degree but waste a lot of memory.

This points to a deeper problem with the implementation pattern used for
all those Builders: they create their product by-value, which must then
be moved into the intended target location.
And doing so is **extremely dangerous**, given that our very goal is to
build a complex data structure internally connected by direct references
and ideally also allocated with a high degree of memory locality.

Unfortunately I do not see any favourable alternative yet;
Ideally all products should be `NonCopyable` — but then, the builder
implementation scheme would become even more complicated and less intuitive
and additionally the client code would need to pre-declare the number of
expected Leads and Ports (not clear if this is even feasible)
2024-10-14 18:29:29 +02:00
4a963c9fee Invocation: draft how the 1:1-fallback wiring could work
...and as expected, this turns up quite some inconsistencies,
especially regarding usage of the »buffer types«.

Basically, the `PortBuilder` is responsible for the high-level functionality
and thus must ensure the nested `WiringBuilder` is addressed and parameterised
properly to connect all »slots« of the processing function.
 - can use a helper function in the WiringBuilder to fill in connections
 - but the actual buffer types passed over these connectinos are totally
   unchecked at that level, and can not see yet how this danger can be
   mitigated one level above, where the PortBuilder is used.
 - it is still unclear what a »buffer type« actually means; it could
   be the pointer type, but it could also imply a class or struct type
   to be emplaced into the buffer, which is a special extension to the
   `BufferProvider` protocol, yet seems to be used here rather to transport
   specific data types required by the actual media handling library (e.g. FFmpeg)
2024-10-14 04:07:47 +02:00
4df4ff2792 Invocation: consider minimal test setup and verification
__Analysis__: what kind of verifications are sensible to employ
to cover building, wiring and invocation of render nodes?
Notably, a test should cover requirements and observable functionality,
while ''avoiding direct hard coupling to implementation internals...''

__Draft__: the most simple node builder invocation conceivable...
2024-10-13 03:49:01 +02:00
bb9d0107cd Invocation: setup preliminary engine-context
* decision how to provide a default service for tests
   while also allow for configuration of more specific services
 * as starting point for the prototype: use the `TrackingHeapBlockProvider`
   (simply because this is the only implementation available and tested)
2024-10-12 04:17:39 +02:00
4a83f97c9e Invocation: draft setup for engine context and config
Prototyping and analysis revealed that some aspects of the render node wiring
refers to effectively global services and can thus be taken out of the picture
by relying on classical ''Dependency Injection''

Consequently, `EngineCtx` needs a default implementation, which brings up
a simplistic fall-back version of those services in support for prototyping.
Moreover, dedicated lifecycle functionality must be provided to bring up
and shut down the actual service instances intended for operational use.
2024-10-11 16:57:10 +02:00
a02873a015 Invocation: pass the actual processing function
...need to pass a binding for the actual processing function
in a way that it acts as a ''prototype'' — since the `Feed`,
i.e. the ''Invocation Adapter'' must be generated for each
invocation anew within the current stack frame
(so to avoid spurious heap allocations)
2024-10-11 04:39:40 +02:00
9490192ef8 Invocation: integrate WeavingBuilder into PortBuilder
...seems that the former is well suited to serve as detail builder
used internally by the latter to provide a simplified standard adaptation
for a given processing function.

The integration can be achieved to layer a specialised detail builder class
on top, which can be entered only by specifying the concrete function or lambda
to wrap for the processing; the further builder-API-functions to control
the wiring in detail become thus only accessible after the function type
is known; this allows to place the detail builder as member into the
enclosing port builder and thus to allocate everything within the current
stack frame invoking the builder chain.
2024-10-11 02:45:51 +02:00
5f0b8b8a81 Invocation: usage analysis for prototype
...after having determined the several levels of prototyping
currently employed, an important step ahead could be achieved
by analysing the intended and implied usage context of this
builder scheme, while still assuming the simplifications
related to prototyping.

It can be assumed that
 * the Level-2 builder object is ''somehow provided''
 * the invocation happens from within a media-handling lib-plugin
 * alongside with the desired `ProcAsset` spec, an `ExpectationContext`
   will be provided, allowing to pass-through additional semantic tags

The implementation in the lib-plugin is then able to draw from specific
knowledge related to the **Domain Ontology** for ''especially for this library''
and provide the necessary wrappers and parameter mapping information.

⟹ the **Level-2**-builder should thus expose an API to
 * set up a straight forward mapping, based on a given wrapper functor
   to delegate to the actual library invocation
 * allow optionally to override some of the input connections
 * alternatively allow to use a complete `InvocationAdapter`,
   including a `FeedManifold`, as provided directly by the library-plugin
2024-10-10 21:35:02 +02:00
bad6751aae (after extended break)
...caused by personal circumstances
...attempt to understand the context I was working on

 * Integration is driven by the `NodeLinkage_test`
 * the near-term goal is to ''get any node built'' — simplified
 * the outline of the `NodeBuilder` and `PortBuilder` is settled
 * the task at hand is how to fill in the definition of a `Port`
 * which in turn ''requires prototyping'' — to establish a kind of weaving-pattern
 * the immediate next thing to do is to ''build an `InvocationAdapter` within the »test-ontology«''
2024-10-09 03:48:18 +02:00
409f2f20b8 Invocation: introduce engine-context for dependency injection
...by relying on DI for some effectively global services, notably
the cache provider, the API for building and wiring render nodes
can be simplified to cover only the actual node connectivity
2024-08-03 03:21:59 +02:00
38dd3a738e Invocation: introduce optional output buffer
Doing so directly seems to be a better solution than to inject an OutputBufferProvider;
the latter will still be needed, yet will not be part of the regular weaving pattern,
but used directly at top-level to obtain the output `BuffHandle`, which is then
passed to the `Port::weave()` call
2024-07-31 19:20:44 +02:00
4bda550f68 Invocation: investigate ways to configure the output-info
...still not convinced that this is a good design,
since it seems to subvert the general design to treat one special case.
However, I can't see a good way to address this special case directly
2024-07-30 23:44:55 +02:00
a4ff2081b9 Invocation: explore variants to pass output connection
There might be one specific output result buffer at top level
for each invocation, which must be delivered into a prepared
output sink. This amounts to one special case, cross-cutting
an otherwise completely generic data flow scheme.

After considering several solutions, it seems most straight-forward
to configure a specific `OutputBufferProvider` to serve as a proxy for
the `OutputSlot` / `DataSink` provided at top-level to the Render-Job.

As an asside, this analyis reveals that the result-slot number does
not belong into the `FeedManifold`, which is dynamic (on the stack);
rather, it's a fixed value configured as part of the `WeavingPattern`
2024-07-30 20:05:48 +02:00
9a23aa773b Invocation: analyse usage of buffer metadata entries
Code clean-up: mark all buffers with a dedicated tagging type


The point in question is: if we work the LocalTag into the type-hash,
could it be possible to miss an existing entry in the metadata registry?
This could cause two entries to be locked for a single buffer address,
leading to data corruption.

As far as I can see, in the current usage this would not happen,
but unfortunately this problem can not be ruled out, since the BufferProvider
API and protocol is designed to be open for various usage patterns.

However, the same potentially disastrous pattern could also materialise
when registering two different buffer types, and then locking each
for the same buffer location.
2024-07-28 19:29:27 +02:00
72c7386435 Invocation: apply a consistent ordering on the chained hash calculation
...this seems to be a tricky aspect; we use hash-chaining to create
derived entries, which may cause the identity of an entry to depend
on the order of specialisation. Looked through the possible code paths,
but these seem to be quite complicated; I see the lurking danger of
creating a second entry (with a different hash), and then in worst case
even locking/unlocking a given buffer twice....
2024-07-27 23:53:27 +02:00
6d7a814495 Invocation: settle upon a way to mark the output buffer
...this is a surprisingly tricky issue, since it undercuts the
generic and recursive implementation of buffer handling;

fortunately I've foreseen such demands may arise down the road
and I've reserved an »Local Key« (now renamed into `LocalTag`),
whose meaning is implementation defined and interpreted by
the specific `BufferProvider`
2024-07-27 17:17:02 +02:00
ea183086ca Invocation: Prototyping to clarify buffer type marking
Requirement analysis shows that the ''actual buffer provider'' to use
constitutes yet another independent degree of freedom, which conceivably
must be handled by the Builder internals rather than by the Domain Ontology.

Thus the simple solution to use a `BuffDescr` to mark the type must be augmented
to also allow configuration of the underlying `BufferProvider`, which generates
the descriptor and can later be invoked with this descriptor to ''lock  an actual Buffer.''

In some cases, setup of the buffer types could even be more complicated and require
access to the actual (runtime) invocaton context; such extreme cases however
could be rendered as an extension of the scheme established here,
by storing the (up to now transient) constructor functors persistently.
Which leads to the decision not to care for those extremely complicated
corner cases right now, and thus to construct all buffer descriptors
in the `build()` call
2024-07-24 20:29:37 +02:00
42f8f8d5af Invocation: Prototyping how to setup invocation wiring
...still fighting to find a suitable API to define
how inputs and outputs are connected and mapped to function parameters.

The solution drafted here uses the reshaped `DataBuilder` (≙`lib::SeveralBuilder`)
to add up connections for each »slot«, disregarding the possibility of permutations.
Similar to `NodeBuilder`, a policy template is used to pass down the setup
for an actual custom allocator.
2024-07-24 03:52:44 +02:00
352ef31ab0 Invocation: further condense the allocator-selector definition
After applying all the preceding refactorings, it turns out that
the `DataBuilder` defined here ''is essentially `lib::SeveralBuilder`'',
only with a different arrangement of the type parameters, due to the
specific usage context here.

It is thus possible to replace all the interim / helper / rebinding templates
by simple templated typedefs. The only tangible difference is that for
usage in the Builder, a ''selector policy'' is passed as a simple type argument,
which in practice wires the concrete allocator information down into each
sub-builder created during the ongoing construction of a node structure.
2024-07-23 02:38:06 +02:00
1705c40dc2 Invocation: rearrange SeveralBuilder policy
redefine the policy for `lib::SeveralBuilder` to be a template-template parameter.
In fact it should have been this way from start, yet defining this kind of
very elaborate code bottom-up lets you sometime miss the wood for the trees

So to restate: `lib::SeveralBuilder` takes a ''policy template,''
which then in turn will be instantiated with the same types `I` (interface)
and `E` (element type) used on `SeveralBuilder` itself. Obviously, there can be
further types involved and thus additional type parameters may be necessary,
notably the ''Allocator'' — yet these are better injected when ''defining''
the policy template itself.

The default binding for this policy template is defined as `allo::HeapOwn`,
which causes the builder to allocate the storage extents through the standard
heap allocator, and for the created `lib::Several` to take full ownership of
embedded objects, invoking their destructors when falling out of scope.
2024-07-17 01:43:17 +02:00