...which uncovers further deeply nested problems,
especially when referring to non-copyable types.
Thus need to construct a common type that can be used
both to refer to the source elements and the expanded elements,
and use this common type as result type and also attempt to
produce better diagnostic messages on type mismatch....
This is a subtle and far reaching fix, which hopefully removes
a roadblock regarding a Dispatcher pipeline: Our type rebinding
template used to pick up nested type definitions, especially
'value_type' and 'reference' from iterators and containers,
took an overly simplistic approach, which was then fixed
at various places driven by individual problems.
Now:
- value_type is conceptually the "thing" exposed by the iterator
- and pointers are treated as simple values, and no longer linked
to their pointee type; rather we handle the twist regarding
STL const_iterator direcly (it defines a non const value_type,
which is sensible from the STL point of view, but breaks our
generic iterator wrapping mechanism)
...in an attempt to resolve the deeply nested problems encountered
while building an iterator pipeline for the Dispatcher. It seems
that I was sloppy some years ago and just "bashed them into submission",
thereby mixing up two different meanings of "value_type"
Moreover I seemingly implemented the same helper trait template twice,
so the first step is to switch all usages to meta::TypeBinding
...requires a first attempt towards defining a `JobTiket`.
This turns out quite tricky, due to using those `LinkedElements`
(intrusive single linked list), which requires all added records
actually to live elsewhere. Since we want to use a custom allocator
later (the `AllocationCluster`), this boils down to allocating those
records only when about to construct the `JobTicket` itself.
What makes matters even worse: at the moment we use a separate spec
per Media channel (maybe these specs can be collapsed later non).
And thus we need to pass a collection -- or better an iterator
with raw specs, which in turn must reveal yet another nested
sequence for the prerequisite `JobTickets`.
Anyhow, now we're able at least to create an empty `JobTicket`,
backed by a dummy `JobFunctor`....
...it should have been this way all the time.
Generic code might otherwise be ill guided to assume a conversion
from the Iterator to its value type, while in fact an explicit dereferentiation is necessary
As it turned out, we had two bugs luring in the code base,
with the happy result of one cancelling out the adverse effects of the other
:-D
- a mistake in the invocation of the Itertools (transform, filter,...)
caused them to move and consume any input passed by forwarding, instead
of consuming only the RValue references.
- but util::join did an extraneous copy on its data source, meaning that
in all relevant cases where a *copy* got passed into the Itertools,
only that spurious temporary was consumed by Bug #1.
(Note that most usages of Itertools rely on RValues anyway, since the whole
point of Itertools is to write concise in-line transformation pipelines...)
*** Added additional testcode to prove util::stringify() behaves correct
now in all cases.
...which can be helpful when a function usually returns a somewhat dressed-up iterator,
but needs to return a specific fixed value under some circumstances
- fix some warnings due to uninitialised members
(no real problem, since these members get assigned anyway)
- use a lambda as example function right in the test
- use move initialisation and the new util::join
this becomes more relevant now, since the actual MutationMessage iterators
are implemented in terms of a shared_ptr to IterSource. Thus, when building
processing pipelines, we most definitively want to move that smart-ptr into
the destination, since this avoids touching the shared count and thus avoids
generating unnecessary memory barriers.
obsoleted by C++11
* in most cases, it can be replaced by an explicit conversion operator
* especially for the Lumiera Forward Iterators, we need an implicit conversion
this is the tiny bit of operational functionality needed on top:
whenever we're reconfiguring the predicate, we need to re-trigger
the evaluation (and clear the cached value)
n.b.: I've verified in debugger that the closure is
allocated on the heap and the functors are passed by value
after looking into our various iterator tools,
it seems obvious that our filtering iterator implementation
has almost all of the required behaviour; we only need to
add a hook to rewrite and extend the filtering functor,
which can now nicely done with a lambda closure.
This means all memory management, if necessary, is
pushed into std::function and the automated memory
management for closures provided by the runtime.
this solves the problem how to deal with value access
- for the simple default (string) implementation,
we use a 'key = val' syntax and thus have to split strings,
which means we need to return contents by value
- for the actual relevant use case we have GenNode entries,
which may recursively hold further Records. For dealing
with diff messages over this data struture, its a good
idea to allow for const& value access (otherwise we'd
end up copying large subtrees for trivial operaions)
This is kind of the logic consequence, since we consider our
functional iterator concept still superior and will continue
to rely on it.
For some time now, I've considered to build a generic bridge
function, to use enable_if and metaprogramming to figure out
if some type is a "Lumiera Forward Iterator" automatically.
But since our concept is to some degree a contract regarding
semantics, which never can be captured by any kind of introspection,
such a bridge implementation would be rather heuristic and
bears the danger to trigger on types actually not intended
as iterator at all. So I consider such a solution as dangerous
and we'll settle with just supplying the necessary bridge
functions as free functions injected for ADL on a case by case base