...seems basically sane now.
Just we still need to wrap it one more time into IterableDecorator;
which means the overall scheme how to build and package the whole pipeline
is not correct yet.
Maybe it is not possible to get it packaged all into one single class?
on closer investigation it turned out that the logic of the
first design attempt was broken altogether. It did not properly
support backtracking (which was the reason to start this whole
exercise) and it caused dangling references within the lambda
closure once the produced iterator pipeline was moved out
into the target location.
Reasoning from first principles then indicated that the only sane
way to build such a search evaluation component is to use *two*
closely collaborating layers. The actual filter configuration
and evaluation logic can not reside and work from within the
expander. Rather, it must sit in a layer on top and work in
a conventional, imperative way (with a while loop).
Sometimes, functional programming is *not* the natural way
of doing things, and we should then stop attempting to force
matters against their nature.
this is an rather obvious extension to the TreeExplorer framework.
In some cases, client code wants to define its own very specific
processing layers, beyond of what can be done with filters and
transformers. Obviously, writing such a custom layer requires
intimate knowledge about the internals of TreeExplorer
the actual use case prompting this extension is IterChainSearch;
it turned out that the original design can not be implemented,
unless the resulting object is non-copyable (which violates
the basic traits of a TreeExplorer based pipeline).
Up to now, we had a very simplistic configuration option just
to search for a match, and we had the complete full-blown reconfiguration
builder option, which accepts a functor to work on and reconfigure the
embedded Filter chain.
It occurred to me that in many cases you'd rather want some intermediary
level of flexibility: you want to replace the filter predicate entirely
by some explicitly given functor, yet you don't need the full ability
to re-shape the Filter chain as a whole. In fact the intended use case
for IterChainSearch (which is the EventLog I am about to augment with
backtracking capabilities) will only ever need that intermediate level.
Thus wer're adding this intermediary level of configurability now.
The only twist is that doing so requires us to pass an "arbitrary function like thing"
(captured by universal reference) through a "layer of lambdas". Which means,
we have to capture an "arbitrary thingie" by value.
Fortunately, as I just found out today, C++14 allows something which comes
close to that requirement: the value capture of a lambda is allowe to have
an intialiser. Which means, we can std::forward into the value captured
by the intermediary lambda. I just hope I never need to know or understand
the actual type this captured "value" takes on.... :-)
with the augmented TreeExplorer, we're now able to get rid of the
spurious base layer, and we're able to discard the filter and
continue with the unfiltered sequence starting from current position.
build a special feature into the Explorer component of TreeExplorer,
causing it to "lock into" the current child sequence and discard
all previous sequences from the stack of child explorations
There is an asymetry, insofar the base layer configuration is
evaluated immediately, causing the MutableFilter to be reconfigured
and forwarded to the first match.
to the contrary, when configuring an additional layer, we just
add it to the chain, but then need to iterate once to cause
this configuration actually to be unfolded onto the stack
...which just turns the pipeline into exhausted state,
instead of raising an Assertion failure
The point is, expandChildren() does not guard itself,
since it _requires_ an non-empty iterator as precondition.
Thus, any function downstream, which invokes expandChildren(),
has to check and guard this call apropriately.
In the concrete case at hand we just stop adding further constraints
when the pipeline is already in exhausted state
...the solution built thus far was logically broken, since it retained the unfiltered
source sequence within the base layer. Thus it would backtrack into this unfiltered
sequence eventually.
The idea was to build a special treatment for attaching the first filter condition;
in fact the first one does not need to be added to the chain, but rather should be
planted directly into the base layer.
WIP: this is a solution draft, but does not work yet
- when attaching the base layer, the filter is pulled twice
- an overconstrained filter raises an Assertion failure
(instead of just transitioning into exhausted state)
So we have now a reworked version of the internals of TreeExplorer in place.
It should be easier to debug template instantation traces now, since most
of the redundancy on the type parameters could be remove. Moreover, existing
pipelines can now be re-assigned with similarily built pipelines in many cases,
since the concrete type of the functor is now erased.
The price tag for this refactoring is that we have now to perform a call
through a function pointer on each functor invocation (due to the type erasure).
And seemingly the bloat in the debugging information has been increased slightly
(this overhead is removed by stripping the binary)
Here the design trardeoff becomes clearly visiblie
- on the plus side, we removed that spurous redundant info
from the template parameter, and we simplified functor rebinding
- but as a tradeoff, we now always have two std::function objects
nested into each other, which also means that at least the outer
object resides on the heap and /inevitably/ calls through a
function pointer, even in case the target function is a lambda,
simply because some type erasure happened, and the call site
does not know the actual type anymore
...step by step switch over to the new usage pattern.
Transformer should be the blueprint for all other functor usages.
The reworked solutions behaves as expected;
we see two functor invocations; the outer functor, which does
the argument adaptation, is allocated in heap memory
This does not touch the existing code-path,
but the idea is to use the _FunTraits directly from within the
constructor of the respective processing layer, and to confine the
knowledge of the actual FUN functor type to within that limited context.
Only the generic signature of the resulting std::function need to be
encoded into the type of the processing component, which should help
to simplify the type signatures
...which still needs to be the *concrete* signature of the funcition to pass,
but we'll attempt to loosen that requirement in the next refactoring steps
...and in preparation start with some renamings...
The overall goal is to simplify the type signatures and thereby
to make the generates pipelines more assignment compatible.
The debugging experience form the last days indicated that the
current design is not maintainable on the long run. Both the
template instantiation chains and the call stacks are way to
complicated and hard to understand and diagnose
It is essential that we pass the current state of the filter
into the expand functor, where it needs to be copied (once!)
to create a child state, which can then be augmented.
This augmented state is then pushed onto a stack, to enable backtracking.
Due to the flexible adapters and the wrapping into the TreeExplorer builder,
we ended up performing several spurious copies on the current state
...based on a monadic tree expansion: we define a single step,
which takes the current filter configuration and builds the next
filter configuration, based on a stored chain of configuration functions
The actual exhausting depth-first results just by the greedy application pattern,
and uses the stack embedded in the "Explorer" layer of TreeExplorer
..this resolves the most challenging part of the construction work;
we use the static helper functions to infer a type and construct a suitable
processing pipeline and we invoke the same helper to initialise the base class
in the ctor.
Incidentally... we can now drop all the placeholder stubs,
since we now inherit the full iterator and child explorer API.
The test now starts actually to work... we get spam and sausage!
TODO: now actually fill in the expand functor such as to pick the
concrete filter step in the chain from a sequence of preconfigured
filter bindings
...now matters start to get really nasty,
since we have to pick up an infered type from a partially built pipeline
and use it to construct the signature for a functor to bind into the more elaborate complete pipeline
this is a tricky undertaking, since our treeExplore() helper constructs
a complex wrapped type, depending on the actual builder expressions used.
Solution is to use decltype on the result of a helper function,
and let the _DecoratorTraits from TreeExplorer do the necessary type adaptations
when adapting a functor, the wrapper automatically decides
if this functor was meant to be used in "monadic style" (value -> new monad)
or in "manipulate state-core style" (iter& -> iter)
Unfortunately, in some cases functions accepting a partially built pipeline
will be classified in the wrong way, due to the fact that IterableDecorator
has a templated wildcard constructor and thus seems to accept the conversion
value_type -> IterableDecorator. This causes the "monadic style" to be chosen
erroneously, and leads to a template instantiation failure just at the point when
the generated functor will be used.
The solution is to explicitly *rule out* the monadic usage style
in all those cases, where the *argument* of the functor to bind can
in fact be directly constructed / converted from the source iterator
or state core. Because, if that is the case
- it is superfluous explicitly to dereference the source iterator;
the functor can do the same
- chances are that the "manipulation style" was intended
(as was the case in the concrete example at hand here)
...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
Need to be more careful when eating trailing spaces after a std::string.
Because the full-blown type is a template, sometimes the compiler adds a spurious
additional space behind the closing angle bracket, due to the now obsolete
"maximum munch rule" of C++98 -- to prevent closing angle brackets to become '>>'.
But in other cases, some type adornments or other language identifiers follow the
string type, like e.g. 'const'. In those cases, the trailing space must be retained.
We solve this by a look-ahead assertion in the regular expression:
consume the trailing space _only_ if a non-word character follows (like '>').
The intention is to augment the iterator based (linear) search
used in EventLog to allow for real backtracking, based on a evaluation tree.
This should be rather staight forward to implement, relying on the
exploreChildren() functionality of TreeExplorer. The trick is to package
the chained search step as a monadic flatMap operation
while this is basically a drop-in replacement,
it marks the switch to the monadic evaluation technology,
which is prerequisite for building real backtracking into the search.
we did an unnecessary copy of the argument, which was uncovered
by the test case manipulating the state core.
Whew.
Now we have a beautiful new overengineered solution
outift the Filter base class with the most generic form of the Functor
wrapper, and rather wrap each functor argument individually. This allows
then to combine various kinds of functors
...this solution works, but has a shortcoming:
the type of the passed lambdas is effectively pinned to conform
with the signature of the first lambda used initially when building the filter.
Well, this is the standard use case, but it kind of turns all the
tricky warpping and re-binding into a nonsense excercise; in this form
the filter can only be used in the monadic case (value -> bool).
Especially this rules out all the advanced usages, where the filter
collaborates with the internals of the source.
while this is basically just code code cosmetics,
at least it marks this as a very distinct special case,
and keeps the API for the standard Filter layer clean.
a quite convoluted construct built from several nested generic lambdas.
When investigated in the debugger, the observed addresses and the
invoked code looks sane and as expected.
The intention is to switch from the itertools-based filter
to the filter available in the TreeExplorer framework.
Thus "basically" we just need to copy the solution over,
since both are conceptually equivalent.
However...... :-(
The TreeExplorer framework is designed to be way more generic
and accepts basically everything as argument and tries to adapt apropriately.
This means we have to use a lot of intricate boilerplate code,
just to get the same effect that was possible in Itertools with
a simple and elegant in-place lambda assignment
Fillter needs to be re-evaluated, when an downstream entity requests
expandChildren() onto an upstream source. And obviously the ordering
of the chained calls was wrong here.
As it turns out, I had discovered that necessity to re-evaluate with
the Transformer layer. There is a dedicated test case for that, but
I cut short on verifying the filter in that situation as well, so
that piece of broken copy-n-paste code went through undetected.
This is in fact a rather esoteric corner case, because it is only
triggered when the expandChildren() call is passed through the filter.
When otoh the filter sits /after/ the entity generating the expandChildren()
calls, everything works as intended. And the latter is the typical standard
usage situation of an recursive evalutation algorithm: the filter is here
used as final part to drive the evaluation ahead and pick the solutions.
There is a bug or shortcoming in the existing ErrorLog matcher implementation.
It is not really difficult to fix, however doing so would require us to intersperse
yet another helper facility into the log matcher. And it occurred to me, that
this helper would effectively re-implement the stack based backtracking ability,
which is already present in TreeExplorer (and was created precisely to support
this kind of recursive evaluation strategies).
Thus I intend to switch the implementation of the EventLog matcher from the
old IterTool framework to the newer TreeExplorer framework. And this intention
made me re-read the code, fixing several comments and re-thinking the design
seemingly my quick-n-dirty implementation was to naiive.
We need real backtracking, if we want to support switches
in the search direction (match("y").after("x").before("z")
Up to now, I have cheated myself around this obvious problem :-/
this is a (hopefully just temporary) workaround to deal with static initialisation
ordering problems. The original solution was cleaner from a code readability viewpoint,
however, when lib::Depend was used from static initialisation code, it could
be observed that the factory constructor was invoked after first use.
And while this did not interfer with the instance lifecycle management itself,
because the zero-initialisation of the instance (atomic) pointer did happen
beforehand, it would discard any special factory functions installed from such
a context (and this counts as bug for my taste).
seemingly this code was brittle: GCC-7 treats int64_t as long,
which leads to preferring the template specialisation over the
explicit version of the operator* -- which means the template
instantiation invokes itself.
usually the ID is hard coded, but when re-throwing errors, it might be
from "somewhere else", which means it is possibly a NULL ptr.
In those cases we fall back to the cannonical ID of the error class.
Basically the mocking mechanism just switches the configuration
and then waits for the service to be accessed in order to cause acutual
instantiation of the mock service implementation. But sometimes we want
to prepare and rig the mock instance prior to the first invocation;
in such cases it can be handy just to trigger the lazy creating process
while switching various services to the new framework,
I noticed the requirement to create a service handle in not-yet-started mode
and then start it explicitly, maybe even from another thread. Thus I introduced
a no-arg default ctor for that purpose, but overlooked that the forwarding ctor
might also need zero arguments for default constructible service implementation
classes. Thus I've now introduced a marker ENUM for disambiguation
SingletonRef was only invented because lib::Depend (or lib::Singleton at that time)
offered only on-demand initialisation, but could not attach to an external service.
But this is required for calling out at the implementation side of a
Lumiera Interface into the actual service implementation.
The recently created DependInject::ServiceInstance now fulfils this task way better
and is seamlessly integrated into the lib::Depend front-end
Actually this is on the implementation side only.
Since Layer-Separation-Interfaces route each call through a binding layer,
we get two Service-"Instances" to manage
- on the client side we have to route into the Lumiera Interface system
- on the implementation side the C-Language calls from the Interface system
need to get to the actual service implementation. The latter is now
managed and exposed via DependInject::ServiceInstance
...still using the FAKE implementation, not a real rules engine.
However, with the new Dependency-Injection framework we need to define
the actual class from the service-provider, not from some service-client.
This is more orthogonal, but we're forced to install a Lifecycle-Hook now,
in order to get this configuration into the system prior to any use
This is borderline yet acceptable;
A service might indeed depend on itself circularly
The concrete example is the Advice-System, which needs to push
the clean-up of AdviceProvicions into a static context. From there
the deleters need to call back into the AdviceSystem, since they have
no wey to find out, if this is an individual Advice being retracted,
or a mass-cleanup due to system shutdown.
Thus the DependencyFactory now invokes the actual deleter
prior to setting the instance-Ptr to NULL.
This sidesteps the whole issue with the ClassLock, which actually
must be already destroyed at that point, according to the C++ standard.
(since it was created on-demand, on first actual usage, *after* the
DependencyFactory was statically initialised). A workaround would be
to have the ctor of DependencyFactory actively pull and allocate the
Monitor for the ClassLock; however this seems a bit overingeneered
to deal with such a borderline issue
...and package the ZombieCheck as helper object.
Also rewrite the SyncClassLock_test to perform an
multithreaded contended test to prove the lock is shared and effective
Static initialisation and shutdown can be intricate; but in fact they
work quite precise and deterministic, once you understand the rules
of the game.
In the actual case at hand the ClassLock was already destroyed, and
it must be destroyed at that point, according to the standard. Simply
because it is created on-demand, *after* the initialisation of the
static DependencyFactory, which uses this lock, and so its destructor
must be called befor the dtor of DependencyFactory -- which is precisely
what happens.
So there is no need to establish a special secure "base runtime system",
and this whole idea is ill-guided. I'll thus close ticket #1133 as wontfix
Conflicts:
src/lib/dependable-base.hpp
When some dependency or singleton violates Lumiera's policy regarding destructors and shutdown,
we are unable to detect this violation reliably and produce a Fatal Error message.
This is due to lib::Depend's de-initialisating being itself tied to template generated
static variables, which unfortunately have a visibility scope beyond the translation unit
responsible for construction and clean-up.
- state-of-the-art implementation of access with Double Checked Locking + Atomics
- improved design for configuration of dependencies. Now at the provider, not the consumer
- support for exposing services with a lifecycle through the lib::Depend<SRV> front-end
...which declare DependencyFactory as friend.
Yes, we want to encourrage that usage pattern.
Problem is, std::is_constructible<X> gives a misleading result in that case.
We need to do the instantiation check within the scope of DependencyFactory
ideally we want
- just a plain unique_ptr
- but with custom deleter delegating to lib::Depend
- Depend can be made fried to support private ctor/dtor
- reset the instance-ptr on deletion
- always kill any instance
all these tests are ported by drop-in replacement
and should work afterwards exactly as before (and they do indeed)
A minor twist was spotted though (nice to have more unit tests indeed!):
Sometimes we want to pass a custom constructor *not* as modern-style lambda,
but rather as direct function reference, function pointer or even member
function pointer. However, we can not store those types into the closure
for later lazy invocation. This is basically the same twist I run into
yesterday, when modernising the thread-wrapper. And the solution is
similar. Our traits class _Fun<FUN> has a new typedef Functor
with a suitable functor type to be instantiated and copied. In case of
the Lambda this is the (anonymous) lamda class itself, but in case of
a function reference or pointer it is a std::function.
- polish the text in the TiddlyWiki
- integrate some new pages in the published documentation
Still mostly placeholder text with some indications
- fill in the relevant sections in the overview document
- adjust, expand and update the Doxygen comments
TODO: could convert the TiddlyWiki page to Asciidoc and
publish it mostly as-is. Especially the nice benchmarks
from yesterday :-D
This solution is considered correct by the experts.
Regarding the dependency-configuration part, we do not care too much about performance
and use the somewhat slower default memory ordering constraint
This is essentially the solution we used since start of the Lumiera project.
This solution is not entirely correct in theory, because the assignment to the
instance pointer can be visible prior to releasing the Mutex -- so another thread
might see a partially initialised object
_not_ using the dependency factory, rather direct access
- to a shared object in the enclosing stack frame
- to a heap allocated existing object accessed through uniqe_ptr
The Lumiera thread-wrapper accepts the operation to be performed
within the new thread as a function object, function reference or lambda.
Some of these types can be directly instantiated in the threadMain
function, and thus possibly inlined altogether. This is especially
relevant for Lambdas. OTOH, we can not instantiate function references
or bound member functions; in those cases we fall back to using a
std::function object, possibly incurring heap allocations.
...written as byproduct from the reimplementation draft.
NOTE there is a quite similar test from 2013, DependencyFactory_test
For now I prefer to retain both, since the old one should just continue
to work with minor API adjustments (and thus prove this rewrite is a
drop-in replacement).
On the long run those two tests could be merged eventually...
This is a complete makeover of our lib::Depend and lib::DependencyFactory templates.
While retaining the basic idea, the configuration has been completely rewritten
to favour configuration at the point where a service is provided rather,
than at the point where a dependency is used.
Note: we use differently named headers, so the entire Lumiera
code base still uses the old implementation. Next step will be
to switch the tests (which should be drop-in)
Most dependencies within Lumiera are singletons and this approach remains adequate.
Singletons are not "EVIL" per se. But in some cases, there is an explicit
lifecycle, managed by some subsystem. E.g. some GUI services are only available
while the GTK event loop is running.
This special case can be integrated transparently into our lib::Depend<TY> front-end,
which defaults to creating a singleton otherwise.
especially std::find is relevant here.
I consider this only a preliminary solution and need to think it over
more in detail. But, judging from the description given in
http://en.cppreference.com/w/cpp/iterator
and
http://en.cppreference.com/w/cpp/concept/InputIterator
...the standard concept of "input iterator" seems to be closest to our
own concept, albeit it requires things like post increment, and a
reference_type convertible to value_type -- requirements we do not
necessarily support with our more limited "Lumiera Forward Iterator"
seems to be the most orthogonal way to strip adornments from the SIG type
Moreover, we want to move the functor into the closure, where it will be stored anyay.
From there on, we can pass as const& into the binder (for creating the partially closed functor)
...but not yet able to get it to compile.
Problem seems to be the generic lambda, which is itself a template.
Thus we need a way to instantiate that template with the correct arguments
prior to binding it into a std::function
been there, seen that recently (-> TreeExplorer, the Expander had a similar problem)
yet some more trickery to get around this design problem.
I just do not want to rework IterSource right now, since this will be
a major change and require more careful consideration.
Thus introduce a workaround and mark it as future work
Using this implementation, "child expansion" should now be possible.
But we do not cover this directly in Unit test yet
...but not yet switched into the main LocationQuery interface,
because that would also break the existing implementation;
recasting this implementation is the next step to do....
...which basically allows us to return any suitable implementation
for the child iterator, even to switch the concrete iteration on each level.
We need this flexibility when implementing navigation through a concrete UI
...at least when using a wrapped Lumiera Iterator as source.
Generally speaking, this is a tricky problem, since real mix-in interfaces
would require the base interface (IterSource) to be declared virtual.
Which incurres a performance penalty on each and every user of IterSource,
even without any mix-in additions. The tricky part with this is to quantify
the relevance of such a performance penalty, since IterSource is meant
to be a generic library facility and is a fundamental building block
on several component interfaces within the architecture.
...yet I do not want to move all of the traits over into the
publicly visible lib::iter_explorer namespace -- I'm quite happy
with these traits being clearly marked as local internal details
surprise: the standard for-Loop causes a copy of the iterator.
From a logical POV this is correct, since the iterator is named,
it can not just be moved into the loop construct and be consumed.
Thus: write a plain old-fashioned for loop and consume the damn thing.
So the top-level call into util::join(&&) decides, if we copy or consume
several extensions and convenience features are conceivable,
but I'll postpone all of them for later, when actual need arises
Note especially there is one recurring design challenge, when creating
such a demand-driven tree evaluation: more often than not it turns out
that "downstream" will need some information about the nested tree structure,
even while, on the surfice, it looks as if the evaluation could be working
completely "linearised". Often, such a need arises from diagnostic features,
and sometimes we want to invoke another API, which in turn could benefit
from knowing something about the original tree structure, even if just
abstracted.
I have no real solution for this problem, but implementing this pipeline builder
leads to a pragmatic workaround: since the iterator already exposes a expandChildren(),
it may as well expose a depth() call, even while keeping anything beyond that
opaque. This is not the clean solution you'd like, but it comes without any
overhead and does not really break the abstraction.
...so sad.
The existing implementation was way more elegant,
just it discarded an exahusted parent element right while in expansion,
so effectively the child sequence took its place. Resolved that by
decomposing the iterNext() operation. And to keep it still readable,
I make the invariant of this class explicit and check it (which
caught yet another undsicovered bug. Yay!)
instead of building a very specific collaboration,
rather just pass the tree depth information over the extended iterator API.
This way, "downstream" clients *can* possibly react on nested scope exploration
...and there is a point where to stop with the mere technicalities,
and return to a design in accordance with the inner nature of things.
Monads are a mere technology, without explicatory power as a concept or pattern
For that reason
- discard the second expansion pattern implemented yesterday,
since it just raises the complexity level for no given reason
- write a summary of my findings while investigating the abilities
of Monads during this design excercise.
- the goal remains to abandon IterExplorer and use the now complete
IterTreeEplorer in its place. Which also defines roughly the extent
to wich monadic techniques can be useful for real world applications
...it can sensibly only be done within the Expander itself.
Question: is this nice-to-have-feature worth the additional complexity
of essentially loading two quite distinct code paths into a single
implementation object?
As it stands, this looks totally confusing to me...
At that time, our home-made Tuple type was replaced by std::tuple,
and then the command framework was extended to also allow command invocation
with arguments packaged as lib::diff::Record<GenNode>
With changeset 0e10ef09ec
A rebinding from std::tuple<ARGS...> to Types<ARGS> was introduced,
but unfortunately this was patched-in on top of the existing Types<ARGS...>
just as a partial specialisation.
Doing it this way is especially silly, since now this rebinding also kicks
in when std::tuple appears as regular payload type within Types<....>
This is what happened here: We have a Lambda taking a std::tuple<int, int>
as argument, yet when extracting the argument type, this rebinding kicks in
and transforms this argument into Types<int, int>
Oh well.
this leads to either unfolding the full tree depth-first,
or, when expanding eagerly, to delve into each sub-branch down to the leaf nodes
Both patterns should be simple to implement on top of what we've built already...
IterSource should be refactored to have an iteration control API similar to IterStateWrapper.
This would resolve the need to pass that pos-pointer over the abstraction barrier,
which is the root cause for all the problems and complexities incurred here
...but for now the price is that we need to punch a hole into IterAdapter.
And obviously, this is all way to tangled and complex on implementation level.
this was a design decision, but now I myself run into that obvious mistake;
thus not sure if this is a good design, or if we need a dedicated operation
to finish the builder and retrieve the iterable result.
as it turned out, when "inheriting" ctors, C++14 removes the base classes' copy ctors.
C++17 will rectify that. Thus for now we need to define explicitly that
we'll accept the base for initialising the derived. But we need do so
only on one location, namely the most down in the chain.
Since this now requires to import iter-adapter-stl.hpp and iter-source.hpp
at the same time, I decided to drop the convenience imports of the STL adapters
into namespace lib. There is no reason to prefer the IterSource-based adapters
over the iter-adapter-stl.hpp variants of the same functionality.
Thus better always import them explicitly at usage site.
...actual implementation of the planned IterSource packaging is only stubbed.
But I needed to redeclare a lot of ctors, which doesn't seem logical
And I get a bad function invocation from another test case which worked correct beforehand.
We need a way for higher layers to discard their caching and re-evaluate,
once some expansion layer was invoked to replace the current element with
its (functionally defined) "children" -- otherwise the first child will
remain obscured by what was there beforehand.
Solution is to pass such manipulation calls through the full chain of
decorators, allowing them to refresh themselves when necessary. To achieve
that technially, we add a base layer to absorb any such call passed down
through the whole decorator chain -- since we can not assume that the
parent, the original source core implements those manipualation calls
like expandChildren()
due to switching from ADL extension points to member functions,
we now need to detect a "state core" type in a different fashion.
The specific twist is that we can not spell out the full signature
in all cases, since the result type will be formed as a consequence
of this type detection. Thus there are now additional detectors to
probe for the presence of a specific function name only, and the
distinction between members and member functions has been sharpened.
Considering the fact that we are bound to introduce yet another iteration control function,
because there is literally no other way to cause a refresh within the IterTreeExplorer-Layers,
it is indicated to reconsider the way how IterStateWrapper attaches to the
iteration control API.
As it turns out, we'll never need an ADL-free function here;
and it seems fully adequate to require all "state core" objects to expose
the API as argument less member function. Because these reflect precisely
the contract of a "state core", so why not have them as member functions.
And as a nice extra, the implementation becomes way more concise in
all the cases refactored with this changeset!
Yet still, we stick to the basic design, *not* relying on virtual functions.
So this is a typical example of a Type Class (or "Concept" in C++ terminology)
- always layer the TreeExplorer (builder) on top of the stack
- always intersperse an IterableDecorator in between adjacent layers
- consequently...
* each layer implementation is now a "state core"
* and the source is now always a Lumiera Iterator
This greatly simplifies all the type rebindings and avoids the
ambiguities in argument converison. Basically now we can always convert
down, and we just need to pick the result type of the bound functor.
Downside is we have now always an adaptation wrapper in between,
but we can assume the compiler is able to optimise such inline
accessors away without overhead.
...yet this seems like a rather bad idea,
it breeds various problems and requires arcane trickery to make it fly
==> abandon this design
==> always intersperse an IterableDecorator between each pair of Layers
...especially relevant in the context of TreeExplorer,
where the general understanding is that the "Data Source" (whatever it is)
will be piggy-backed into the pipeline builder, and this wrapping is
conceived as being essentially a no-op.
It is quite possible we'll even start using such pipeline builders
in concert with move-only types. Just consider a UI-navigator state
hooked up with a massive implementation internal pointer tree attached
to all of the major widgets in the UI. Nothing you want to copy in passing by.
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.
Obsoletes and replaces the ad-hoc written type rebindings from
iter-adapter and friends. The new scheme is more consistent and does
less magic, which necessitates an additional remove_pointer<IT> within
the iterator adaptors. Rationale is, "pointer" is treated now just as
a primitive type without additional magic or unwrapping, since it is
impossible to tell generically if the pointer or the pointee was
meant to be the "value"
Oh well.
This kept me busy a whole day long -- and someone less stubborn like myself
would probably supect a "compiler bug" or put the blame on the language C++
So to stress this point: the compiler behaved CORRECT
Just SFINAE is dangerous stuff: the metafunction I concieved yesterday requires
a complete type, yet, under rather specific circumstances, when instantiating
mutually dependent templates (in our case lib::diff::Record<GenNode> is a
recursive type), the distinction between "complete" and "incomplete"
becomes blurry, and depends on the processing order. Which gave the
misleading impression as if there was a side-effect where the presence
of one definition changes the meaning of another one used in the same
program. What happened in fact was just that the evaluation order was
changed, causing the metafunction to fail silently, thus picking
another specialisation.
- we do strip references
- we delegate to nested typedefs
Hoever, we do *not* treat const or pointers in any way special --
if the user want to strip or level these, he has to do so explicitly.
Initially it seemed like a good idea to do something clever here, but
on the long run, such "special treatment" is just good for surprises
...automatically whenever those are present.
Up to now, we hat that as base case, which limited usage to those cases
where we already know such nested definitions are actually present
attempt to re-use the same traits as much as possible
NOTE: new code not passing compiler yet, but refactored old code
does, and still passes unit test
this is a subtle change which, given all interfaces were used in a logically
consistent way, should not cause any observable change to the yielded elements.
But it changes runtime behaviour, insofar now the evalutaion is initiated
lazily, when first requesting a result type. Prior to this change, the
constructor immediately issued a call to the yield() extension point,
which presumably has the side-effect of preparing the core and initiating
any embedded evaluation, in order to get at the first result; it might
even detect an empty state.
Given the fact that all access operations on the iterator front-end perform
an empty check (and possibly throw at that point), this call is redundant.
surprising behaviour encountered while covering more cases
...obviously the return type of ExpandFunctor::operator()
was inferred as value, even while the invoked functor, from which
this type was deduced, clearly returns a reference.
Solution is simple not to rely on inference, moreover since we know
the exact type in the enclosing scope, thanks to the refactoring which
made this ExpandFunctor a nested class
NOTE:
as it turned out, this is not a compiler bug,
but works as defined by the language:
on return type inference, the detected type is decayed,
which usually helps to prevent returning a reference to a temporary
...while this implementation works now, it is still very complex and intricate.
I am still doubtful this is a good approach, but well, we need to try that route....
but possible only for the iterator -> iterator case
Since we can not "probe" a generic lambda, we get only one shot:
we can try to bind it into a std::function with the assumed signature
This is a consequence of the experiments with generic lambdas.
Up to now, lib::meta::_Fun<F> failed with a compilation error
when passing the decltype of such a generic lambda.
The new behaviour is to pick the empty specialisation (std::false_type) in such cases,
allowing to guard explicit specialisations when no suitable functor type
is passed
Basically we want to support two distinct cases, just by slightly adapting
the invocation of the expansion functor:
Case-1: classical monadic flatMap:
the Functor accepts a value yielded by the source iterator
and builds a new "expaneded" iterator
Case-2: manipulation of opaque implementation state
the Functor knows internal details of the source iterator
and thus takes the source iterator as such as argument,
performs some manipulation and then builds a new sub-iterator
A soulution to reconcile those two distinct cases can be built
with the help of a generic lambda
this solution makes me feel somewhat queasy..
stacking several adaptors and wrappers and traits on top of each other.
Well, it type checks and passes the test, so let's trust functional programming
The plan is to use a monad-like scheme, but allow for a lot of leeway
with respect to the src and value types of the expand functor.
A key idea is to allow for a *different* state core than used in the source
...but does not work as intended:
* just forming an IterStateWrapper does not trigger SFINAE cleanly in all cases
* IterStateWrapper can be formed, even when some of the extension points are missing;
this will be uncovered only later, when actually using one of the operations
but beyond that, the basic type selection logic can work this way
Here, the tricky question remains, how to relate this evalutaion scheme
to the well known monadic handling of collections and iterators.
It seems, we can not yet decide upon that question, rather we should
first try to build a concrete implementation of the envisioned algorithm
and then reconsider the question later, to what extent this is "monadic"
This can be seen as a side track, but the hope is
by relying on some kind of monadic evaluation pattern, we'll be
able to to reconcile the IterExplorer draft from 2012 with the requirement
to keep the implementation of "tree position" entirely opaque.
The latter is mandatory in the use case here, since we must not intermingle
the algorithm to resolve UI-coordinates in any way with the code actually
navigating and accessing GTK widgets. Thus, we're forced to build some kind
of abstraction barrier, and this turns out to be surprisingly difficult.
...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
up to now, we allowed only initialisation with a precisely matching type.
But this special case seems worth supporting, since it typically occurs
within the "object builder" syntax based on Rec::Mutator
Explicitly assuming that those functions are called solely from IterAdapter
and that they are implemented in a typical standard style, we're able to elide
two redundant calls to the checkPoint() function. Since checkPoint typically performs
some non-trivial checks, this has the potential of a significant performance improvement
- we check (and throw ITER_EXHAUST) anyway from operator++, so we know that pos is valid
- the iterate() function ensures checkPoint is invoked right after iterNext,
and thus the typical standard implementation of iterNext need not do the same
...since that is what it meant to be.
To allow this chance, I've now added a default ctor to lib::Literal,
defaulting to the Symbol::EMPTY (the interned empty string)
The class Literal is used as a thin wrapper to mark the fact that
some string parameter or value is assumed to be given *literally*
For the contract this indicates
- that storage is somewhere
- storage is not owned and managed by Literal
- yet storage guaranteed to exist during the whole lifetime of the program
- Literal can not be altered
- Literal is transparently convertible to const char *
Currently I am in the course of building some path abstraction, and for that
task it makes sense to hold an array of Literals (instead of pointers), just
because it expresses the intent way more clear. I do not see anything in the
above mentioned contract to prohibit a default constructed Literal, with the
empty string being the most obvious choice.
Note: there is the class Symbol, which derives from Literal. Symbol takes
arbitrary strings, but *interns* them into a static symbol table.
...under the assumption that the content is normalised,
which means
- leading NULL is changed to Symbol::EMPTY
- missing elements in the middle are marked as "*"
- trailing NULL in extension storage is handled by adjusting nominal extension size
after various fruitless attempts to rely somehow on the array variant of unique_ptr,
I ended up with a hand coded version of an heap allocated array, managed automatically
as it turned out, the solution from yesterday works only with uniform argument lists,
but not with arbitrarily mixed types. Moreover the whole trickery with the
indices was shitty -- better use a predicate decision on template argument level.
This simple solution somehow just didn't occur to me...
...still somewhat unsatisfactory, because
- no clear compile error message when invoking pickArg with insufficient arguments
- the default initialisation case in SelectVararg is duplicated and messy
some time ago we abandoned our own tuple type in favour of std::tuple
Since then, the helpers and ported utilities provide some generic helpers
to deal with variadic argument sequences, especially to build index sequences,
which in turn can be used to "pick" individual arguments from a variadic parameter pack.
The expectation is for this part of the support library gradually to grow and
in parts to replace the existing type sequence processing helpers. The expectation
is that we'll retain the basic type sequence, lib::meta::Types, but retrofit it
to rely on variadic arguments
since the adoption of C++11, we gradually transition our metaprogramming helpers
to support and rely on variadic template parameters. For the time being,
we just augment existing facilities when it comes in handy, yet some more
heavyweight lifting and overall clean-up remains to be done eventually.
we allow assignment to the element embedded within the wrapper.
Yet obviously we need specific implementations for assignment
to the container itself. Thus we define the templated
assignment operator such as to render the explicit specialisation
a better match than anything generated from the templated
operator
basically DiffMessage has a "take everything" ctor, which happens
to match on type DiffMessage itslef, since the latter is obviously
a Lumiera Forward Operator. Unfortunately the compiler now considers
this "take everyting" ctor as copy constructor. Worse even, such a
template generated ctor qualifies as "best match".
The result was, when just returing a DiffMessage by value form a
function, this erroneous "copy" operation was invoked, thus wrapping
the existing implementation into a WrappedLumieraIterator.
The only tangible symptom of this unwanted storage bloat was the fact
that our already materialised diagnostics where seemingly "gone". Indee
they weren't gone for real, just covered up under yet another layer of
DiffMessage wrapping another Lumiera Forward Iterator
by moving, we can avoid the generation of up to 3 additional shared copies
of the DataHandle. The whole invocation now works without touching any shared count
and thus without incurring a memory barrier...
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.
...allows us to get rid of quite some boost-includes
Incidentally, "our own" implementation is equivalent to both the
boost implementation and the implementation from C++14
It is just a bit more concise to write.
since we do not want to increase the footprint, we're bound to reuse
an existing VTable -- so IterAdapter itself is our only option.
Unfortunately we'll need to pass that through one additional
decoration layer, which is here the iterator; to be able to
add our string conversion there, we need to turn that into
a derived class and add a call to access the underlying
container, which gets us into element type definition mess....
now this highlights the unsettled decision still the more,
as can be seen by all that unnecessary copying. Basically we move the
Diff into the lambda-closure, from there into an anonymous instance,
from there into the embedded Buffer in MutationMessage, which again
just happens to sit in the closure storage when the action is invoked.
And all of this copying just to move the DiffMessage for consumption
into the TreeMutator...
thus by #1066 we should really get rid of the MutationMessage class altogether!
actually I do not know much regarding the actual situation when,
within the Builder run, we're able to detect a change and generate
a diff description. However, as a first step, I'll pick IterSrouce
as a base interface and use a "generation context", which is to be
passed by shared-ptr
again surprising how such fundamental bugs can hide for years...
Here the reason is that IterAdapter leaves the representation of "NIL" to
its instantiation / users; some users (here in for example the ScopedCollection)
can choose to allow for different representations of "NIL", but the comparison
provided by IterAdapter just compares the embedded pos by face value.
seems like most usages will want to expose this kind of diagnostics for unit testing
and in fact the queue or stack nature is the primary nature of this entity,
while iterability comes as additional trait
- concept for a first preliminary implementation of dispatch into the UI thread
- define an integration effort to build a complete working communication chain
...again to make it work with GCC-5,
also to allow more leeway using various compilers
Explanation: we use a helper function to abbreviate the
demangled type names to make diagnostic ouput more readable.
Obviously such a function needs to be adjusted to the
way concrete compilers generate their type output; GCC-5
slightly differs to GCC-4.9 here, so I've made the regular
expressions a bit more flexible
we have a catch-all template operator to get a string converted
or pretty printed output from "any object". Unfortunately
this overload counts equivalent to another overload by
the IO manipulators. Solution is to define both operarators
similar in the first argument, thus turing the overload
for the IO manipulators into the more specific overload
due to the explicitly given second argument
this seems like an obvious functionality and basically harmless,
since commands are designed to be inherently stateful, which is reflected
in all the internal storage holders to expos an assignment operator
(even while the actual implementation is based on placement new instead
of assigning values into the storage, and thus even supports immutable
values). The only possible ramification is that argument values must
be default constructible
in accordance to the design changes concluded yesterday.
- in the standard cases we now check the global registry first
- automatically create anonymous clone copy from global commands
- reorganise code internally to use common tail implementation
this might turn into lock contention problem, but better optimise
a correct implementation than fix a fast yet broken one.
Hint: SessionCommandFunction_test demonstrates that the
symbol table can be corrupted by creating Symbol instances
in parallel without proper locking. So yes, this is for real.
since Symbol instance are now backed by a symbol table,
we can use a much faster hash function by just hashing the
pointer into the symbol table, since the Symbol string content
is already checked at initialisation.
Up to now, we tolerated null pointers in Literal instances.
But we can not tolerate passing a null cString to Symbol initialisation.
Rather, hereby we introduce a dedicated "bottom" Symbol, a valid "null object"
For this task, I've also investigated to use boost::operators
This would only incur a negligible penalty on build times and executable sizes,
however, I don't consider the boost based solution to improve readability,
since many of these comparisons are tricky or subtly different.
Moreover, since boost::operators needs to be mixed-in, the initialisation
of Symbol objects becomes difficult, not to mention the additional base class
information visible in the debugger when inspecting Symbol or Literal objects
For that reason, I decided *against* using Boost here and coded up
all the operators in all combinations manually
...which means, from now on identical input strings
will produce the same Symbol object (embedded pointer).
TODO: does not handle null pointers passed in as c-String properly
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 changeset fixes a huge pile of problems, as indicated in the
error log of the Doxygen run after merging all the recent Doxygen improvements
unfortunately, auto-linking does still not work at various places.
There is no clear indication what might be the problem.
Possibly the rather unstable Sqlite support in this Doxygen version
is the cause. Anyway, needs to be investigated further.
it is not *that* hard to behave in a somewhat sane manner here.
And even more: this *is* basically the symbol table implementation we need.
Thus we only need to build the right front-end now...
...otherwise our log will be flooded with command definition messages soon
NOTE: to see all command definitions happening, set into environment:
NOBUG_LOG='command:TRACE
...since there is not any test coverage for this trait, which
turned out to be quite deeply rooted in the system by now and
handles several rather subtle special cases
...and move the tail-call of the template instantiation into try.cpp
This experiment clearly shows the discrepancy now:
- binding a member pointer directly into a function object will expand the argument list
- but binding a similar lambda into a function object won't
(it is not necessary due to the context capture)
The result is that we need to drop support for one of those cases,
and it is clear that the member poiter will be the looser...
As a first step towards a gradual rework of our function metaprogramming helpers,
this change prepends a generic case for all kinds of functors to our existing
solution, which up to now was entirely based on explicit specialisations.
C++11 supplied the new language construct 'decltype(EXPR)', which allows us
to capture any class with an function operator, which also includes the Lambdas.
The solution was proposed 2011 on StackOverflow
http://stackoverflow.com/questions/7943525/is-it-possible-to-figure-out-the-parameter-type-and-return-type-of-a-lambda/7943765#7943765
We used it already with success within our TreeMutator.
But obviously the goal should be to unite all the function trait / metaprogramming helpers,
which unfortunately is a more expensive undertaking, since it also involves
to get rid of the explicit specialisations and retrofit our Types<XXX...> helper
to rely on variadic templates rather than on loki-style typelists.
This first step here is rather conservative, since we'll still rely on our
explicit specialisations in most cases. Only the Lambdas will go through the
new, generic case, and from there invoke the specialisation for member functions.
The latter need to be rectified as well, which is subject of the next changeset...
Writing and debugging such tests is always an interesting challenge...
Fortunately this exercise didn't unveil any problem in the newly written
code, only some insidious problems in the test fixture itself. Which
again highlights the necessity, that each *command instance* needs
to be an independent clone from the original *command prototype*,
since argument binding messages and trigger messages can appear
in arbitrary order.
This is a little bit of functionality needed again and again;
first I thought to use the TypedCounter, but this would be overkill,
since we do not actually need different instances, and we do not need
to select by type when incrementing the counter. In fact, we do not
even need anything beyond just allocating a number.
So I made a new class, which can be used RAII style
this was a spin-off activity from writing the SessionCommand
function(integration) test, where I noted that we can't just
capture "a time value" as command memento
basically this is not necessary, since the compiler figures out
to use the conversion to target type when attempting to resolve
an equality comparison. But it helps to avoid ambiguities in cases
where several conversion paths do exist, e.g. when comparing string
with C-string
explicitly observed with the debugger that the call path is sane;
the code looks innocuous, but it is quite magic how the compiler
picks precisely the right ctors and inserts conversions apropriately
From a purely logical viewpoint, it looked sensible to require an actual
value for an offset, especially since our time values are immutable.
But this has the unfortunate consequence that we'd be unable to use
an offset value as parameter for any command, since we store the arguments
as tuple and the tuple type has a default constructor. We might be able
to get around that problem, but such looks brittle to me; it is just
plain surprising for anyone not familiar with the internals of the
command system.
For that reason, I've now added a default ctor to the Offset type
...since the session loop will be notified on any change via the
interface, adding a command will activate the loop, and the builder
timeout is handled separately via the dirty state. So there is no
need to spin around the loop in idle state.
As a aside, timeout waiting on a condition variable can be intentional
and should thus not be logged as an error automatically. It is up to the
calling context to decide if a timeout constitutes an exceptional situation.
It is always a trade-off performance vs. readability.
Sometimes a single-threaded implementation of self-contained logic
is preferable to a slightly more performant yet obscure implementation
based on our threadpool and scheduler.
Did a full review of state and locking logic, seems airtight now.
- command processing itself is unimplemented, we log a TODO message for now
- likewise, builder is not implemented
- need to add the deadlock safeguard #1054
And yes, this warning is for real, while the compiler has no way
to decide if there is actual danger lurking. A type with internal
linkage (e.g. defined in an anonymous namespace) will be treated
by the linker as a separate entity on each encounter (i.e. in
each distinct compilation unit). When multiple translation units
start collaborating on such a type, they *might* be referring
to different memory locations, while semantically the intention
is to refer to the same location.
And since we're dealing with a library facility here, *we* have
likewise now power to ensure proper usage, so we better be cautious.
after reading some related code, I am leaning towards a design
to mirror the way command messages are sent over the UI-Bus.
Unfortunately this pretty much abandons the possibility to
invoke these operations from a client written in C or any
other hand made language binding. Which pretty much confirms
my initial reservation towards such an excessively open
and generic interface system.
...the sheer amount of mechanical replacements scattered all over these
files might be a vivid indication, that the design of the interface system
is subobptimal ;-)
Phew, convoluted.
And I was doubtful that we need to support multiple typed child collection
Well, we get three such collections already in the first real world example...
reason is, only files with a @file comment will be processed
with further documentation commands. For this reason, our Doxygen
documentation is lacking a lot of entries.
HOWTO:
find src -type f \( -name '*.cpp' -or -name '*.hpp' \) -not -exec egrep -q '\*.+@file' {} \; -print -exec sed -i -r -e'\_\*/_,$ { 1,+0 a\
\
\
/** @file §§§\
** TODO §§§\
*/
}' {} \;
Damn sideeffect of the suppport for move-only types: since we're
moving our binding now into place /after/ construction, in some cases
the end() iterator (embedded in RangeIter) becomes invalid. Indeed this
was always broken, but didn't hurt, as long as we only used vectors.
Solution: use a dedicated init() hook, which needs to be invoked
*after* the TreeMutator has been constructed and moved into the final
location in the stack buffer.
unintentionally we used copy construction in the builder expression,
wenn passing in the CollectionBinding to the ChildCollectionMutator.
The problem is that CollectionBinding owns a shaddow buffer, where
the contents of the target collection are moved temporarily while
applying the diff. The standard implementation of copy construction
would cause a copy of that shaddow buffer, which boils down to
a copy of the storage of the target collection.
If we want to support move-only types in the collection, most notably
std::unique_ptr, we can thus only use the move constructor. Beyond that
there is no problem, since we're only ever moving elements, and new
elements will be move constructed via emplace() or emplace_back()
actually this is a pragmatic extension for some special use cases,
and in general rather discurraged, since it contradicts the
established diff semantics. Yet with some precaution, it should
be possible to transport information via an intermediary ETD
Map -> ETD -> Map
for the record: while it is indeed sweet-and-simple to support Ref::THIS
here, it is near impossible to represent it in general, in a setup with
multiple "onion-layers". The reason is, we'd have to incorporate such
special treatment into the /selector predicate/, which in turn undermines
the ability to pick the right onion layer to handle a given diff verb,
since "Ref::THIS" is a generic marker and we have no other data to base
the decision in the selector on.
Up to now, InPlaceBuffer used to default construct an instance of the
Interface class, and then you'd need to invoke the `create()` function
to actually create the desired subclass. This is not only inefficient,
but rules out the use of abstract interfaces / base classes.
Unfortunately, there is no way in C++ to specify an explicit template argument list
on ctor calls, so we resort to the trick of passing an additional dummy marker argument
yay! this piece of code has served its purpose:
it was the blueprint to build a way better design and implementation,
which can now cover this "generic tree" case as a special case as well
this adds kind of an extension point to diff::Record<GenNode>::Mutator,
which is then actually defined (implemented) within the diff framework.
This allows the TreeDiffTraits automatically to use this function
to get a TreeMutator for a given Rec::Mutator. Which in turn allows
the generic version of DiffApplicator automatically to attach and
bind to a Record<GenNode>
together this allows us to ditch the explicit specialisation
and dedicated, hand-written implementation of DiffApplication
to GenNode in favour of using the TreeMutator and friends.
this is a subtle change in the semantics of the diff language,
actually IMHO a change towards the better. It was prompted by the
desire to integrate diff application onto GenNode-trees into the
implementation framework based on TreeMutator, and do away with
the dedicated implementation.
Now it is a matter of the *selector* to decide if a given layer
is responsible for "attributes". If so, then *all* elements within
this layer count as "attribute" and an after(Ref::ATTRIBS) verb
will fast forward behind *the end of this layer*
Note that the meta token Ref::ATTRIBS is a named GenNode,
and thus trivially responds to isNamed() == true
needed to use a forward function declaration within the
lambda for recursive scope mutator building, since otherwise
everything is inline and thus the compilation fails when it
comes to deducing the auto return type of the builder.
Other than that, the whole mechanics seem to work out of the box!
previously they where included in the middle of tree-mutator.hpp
This was straight forward, since the builder relies on the classes
defined in the detail headers.
However, the GenNode-binding needs to use a specifically configured
collection binding, and this in turn requires writing a recursive
lambda to deal with nested scopes. This gets us into trouble with
circular definition dependencies.
As a workaround we now only *declare* the DSL builder functions
in the tree-mutator-builder object, and additionally use auto on
all return types. This allows us to spell out the complete builder
definition, without mentioning any of the implementation classes.
Obviously, the detail headers have then to be included *after*
the builder definition, at bottom of tree-mutator.hpp
This also allows us to turn these implementation headers into
completely normal headers, with namespaces and transitive #includes
In the end, the whole setup looks much more "innocent" now.
But beware: the #include of the implementation headers at bottom
of tree-mutator.hpp needs to be given in reverse dependency order,
due to the circular inclusion (back to tree-mutator.hpp) in
conjunction with the inclusion guards!
...instead of using a hand written implementation,
the idea is to rely on the now implemented building blocks,
with just some custom closures to make it work.
- esp. verify the proper inclusion of the Selector closure in all Operations
- straighten the implementation of Attribute binding
- clean-up the error checking helpers
similar reordering for the third part.
This time most operations are either passed down anyway,
or are NOP, since attribute binding has no notion of 'order'
yay! unit testing rocks.
Actually I changed the test definition for another reason, just to discover
that I've missed to implement that operation in this onion layer
now failing due to a contradiction in test fixture:
it is nonsensical to re-order attributes; rather, we should
cover re-ordering of children, to verify that the mutator binding
properly surpasses the attribute layers and forwards operations
to the lower layers responsible for handling child scopes...
In Theory, acceptSrc and skipSrc are to operate symmetrically,
with the sole difference that skipSrc does not move anything
into the new content.
BUT, since skipSrc is also used to implement the `skip` verb,
which serves to discard garbage left back by a preceeding `find`,
we cannot touch the data found in the src position without risk
of SEGFAULT. For this reason, there is a dedicated matchSrc operation,
which shall be used to generate the verification step to properly
implement the `del` verb.
I've spent quite some time to verify the logic of predicate evaluation.
It seems to be OK: whenever the SELECTOR applies, then we'll perform
the local match, and then also we'll perform the skipSrc. Otherwise,
we'll delegate both operations likewise to the next lower layer,
without touching anything here.
--> now it becomes obvious that we've mostly
missed to integrate the Selector predicate properly
in most bindings defined thus far. Which now causes
the sub-object binding to kick in, while actually
the sub-value collection should have handled
the nested values CHILD_B and CHILD_T
OMG, this is intricate stuff....
Questionable if anyone (other than myself) will be able
to get those bindings right???
Probably we'll need yet another abstraction layer to handle
the most common binding situations automatically, so that people
can use the diff framework without intricate knowledge of
TreeMutator construction.
- an extension to our custom toString and typeString helpers.
- currently just for shared_ptr and unique_ptr
- might add further overloads for other smart-ptr types
integrated into the generic DiffApplicationStrategy.
The dedicated, explicit specialisation for DiffMutable is
no longer needed, since the generic template will degrade or
fall back to precisely this functionality, when the target
implements the DiffMutable interface
This is the first skeleton to combine all the building blocks,
and it passes compilation, while of course most of the binding
implementation still needs to be filled in...
It occurred to me, that 90% of this template specialisation
are entirely generic and not dependant on the actual target type.
While the compiler/linker is able to sort such a situation out,
this might lead to template bloat and possibly subtle errors.
So it seems more adequate to emit the generic part of the code
right away from within a dedicated translation unit within the
library module; so the vtable is already in place and only
the flexible part of the code needs to be re-emitted on
each usage site.
- default recommendation is to implement DiffMutable interface
- ability to pick up similar non-virtual method on target
- for anything else client shall provide free function mutatorBinding(subject)
PERSONAL NOTE: this is the first commit after an extended leave,
where I was in hospital to get an abdominal cancer removed.
Right now it looks like surgery was successful.
this is at the core of the integration problem: how do we expose
the ability of some opaque data structure to create a TreeMutator?
The idea is
- to use a marker/capability interface
- to use template specialisation to fabricate an instance of that interface
based on the given access point to the opaque data structure
but unfortunately this runs straight into a tough problem,
which I tried to avoid and circumvent all the time:
At some point, we're bound to reveal the concrete type
of the Mutator -- at least to such an extent that we're
able to determine the size of an allocator buffer.
Moreover, by the design chosen thus far, the active
TreeMutator instance (subclass) is assumed to live within
the top-level of a Stack, which means that we need to
place-construct it into that location. Thus, either
we know the type, or we need to move it into place.
initially, even the diff applicator was meant to be a
"throwaway" object. But then, on writing some tests,
it seemed natural to allow re-using a single applicator,
after having attached it to some target.
With that change, I failed to care for the garbage
left back in the "old" sequence after applying one diff;
since in the typical usage sequence, the first use builds
content from scratch, this problem starts to show up only
with the third usage, where the garbage left from the input
of the second usage appears at the begin of the "new sequence"
Solution is to throw away that garbage explicitly on re-entrance
..because actually we don't know if the intention is
to drop those waste elements -- and for sure this
discarding of waste does not happen through the
invocation logged here; rather it happens by
abandoning the scope
...which mostly just is either ignoring the
operations or indicating failure on attempt to
'reorder' attributes (which don't have any notion of 'ordering')
overall, the structure of this implementation is still rather confusing,
yet any alternatives seem even less convincing
- if we want to avoid the delegation to base-class, we'd have
to duplicate several functions and the combined class would
handle two distinct concerns.
- any attempt to handle the IDs more "symmetrically" seems to
create additional problems on one side or the other
this also supersedes and removes the initial implementation
draft for attribute binding with the 'setAttribute' API
The elementary part of diff application incl. setting
new attribute values works by now.
While in general it is fine to clean-up any entity IDs
to be US-ASCII alphanumerics (plus some allowed interpunction),
the GenNodes and also keys in object-bindings for diff are
considerd internal interfaces, assuming that any passed
ID symbol is already sanitised and checked. So the
sanitise operation can be skipped. This changeset
adds the same option directly to lib::EntryID,
allowing to create an EntryID that matches
a similar GenNode's (hash) ID.
The way we build this attribute binding, there is no single
entity to handle all attribute bindings. Thus the only way
to detect a missing binding is when none of the binding layers
was able to handle a given INS verb
to summarise, it turned out that it is impossible to
provide an airtight 'emptySrc' implementation when binding
to object fields -- so we distinguish into positive and
negative tests, allowing to loosen the sanity check
only for the latter ones when binding to object fields.
..as concluded from the preceding analysis.
NOTE this entails a semantical change, since this
predicate is now only meant to be indicative, not conclusive
remarks: the actual implementation of the diff application process
as bound via the TreeMutator remains yet to be written...
how can ordinary object fields be treated as "Attributes"
and thus tied into the Diff framework defined thus far.
This turns out to be really tricky, even questionable
while simple to add into the implementation, this whole feature
seems rather qestionable to me now, thus I've added a Ticket
to be revisited later.
In a nutshell, right here, when implementing the binding layer
for STL collections, it is easy to enable the framework to treat
Ref::THIS properly, but the *actual implementation* will necessarily
be offloaded onto each and every concrete binding implementation.
Thus client code would have to add support for an rather obscure
shortcut within the Diff language. The only way to avoid this
would be to change the semantics of the "match"-lambda: if this
binding would rather be a back-translation of implementation data
into GenNode::ID values, then we'd be able to implement Ref::THIS
natively. But such an approach looks like a way inferiour deisgn
to me; having delegated the meaning of a "match" to the client
seems like an asset, since it is both natural and opens a lot
of flexibility, without adding complexity.
For that reason I tend to avoid that shortcut now, in the hope
to be able to drop it entirely from the language
...basically this worked right away and was easy to put together.
However, when considering how many components, indirections and
nested lambdas are working together here, I feel a bit dizzy...
:-/
write down a first draft for a definiton section,
to describe the fundamental parts involved, when
applying a diff message onto implementation defined
data structures
After a break of tree weeks, I found it difficult to find may way
amidst all those various levels of abstraction. In addition to this
definition, we'll probably also need a high level overview of the
whole diff system operation.
...all of this implementation boils down to slightly adjusting
the code written for the test-mutation-target. Insofar it pays off now
having implemented this diagnostic and demonstration first.
Moreover I'm implementing this basic scheme of "diff application"
roughly the fourth time, thus things kindof fall into place now.
What's really hard is all those layers of abstraction in between.
Lesson learned (after being off for three weeks, due to LAC and
other obligations): I really need to document the meaning of the
closures, and I need to document the "abstract operational semantics"
of diff application, otherwise no one will be able to provide
the correct closures.
while I still keep my stance not to allow reflection and
switch-on-type, access to the internal / semantic type of
an embedded record seems a valid compromise to allow
to deal with collections of object-like children
of mixed kind.
Indirectly (and quite intentional) this also opens a loophole
to detect if a given GenNode might constitute a nested scope,
but with the for the actual nested element indeed to cary
a type symbol. Effectively this limits the use of this shortcut
to situations where the handling context does have some pre-established
knowledge about what types *might* be expected. This is precisely
the kind of constraint I intend to uphold: I do not want the
false notion of "total flexibility", as is conveyed by introspection.
since we're moving elements around to apply the diff,
dangerous situation might arise in case anyone takes a copy
of the mutator. Thus we effectively limit the possible
usage pattern and only allow to build an anonymous
TreeMutator subclass through the Builder-DSL.
The concrete "onion layers" of the TreeMutator are now limited
- to be created by the chaining operations of the Builder DSl
- to be moved into target location, retaining ownership.
I still feel somewhat queasy with this whole situation!
We need to return the product of the DSL/Builder by value,
but we also want to swap away the current contents before
starting the mutation, and we do not want a stateful lifecycle
for the mutator implementation. Which means, we need to swap
right at construction, and then we copy -- TADAAA!
Thus I'm going for the solution to disallow copying of the
mutator, yet to allow moving, and to change the builder
to move its product into place. Probably should even push
this policy up into the base class (TreeMutator) to set
everyone straight.
Looks like this didn't show up with the test dummy implementation
just because in this case the src buffer also lived within th
TestMutationTarget, which is assumed to sit where it is, so
effectively we moved around only pointers.
the whole implementation will very much be based on
my experiences with the TestMutationTarget and TestWireTap.
Insofar it was a good idea to implement this test dummy first,
as a prototype. Basically what emerges here is a standard pattern
how to implement a tree mutator:
- the TreeMutator will be a one-way-off "throwaway" object.
- its lifecylce starts with sucking away the previous contents
- consuming the diff moves contents back in place
- thus the mutator always attaches onto a target by reference
and needs the ability to manipulate the target
the collection binding can be configured with various
lambdas to supply the basic building blocks of the generated binding.
Since we allow picking up basically anything (functors,
function pointers, function objects, lamdas), and since
we speculate on inlining optimisation of lambdas, we can not
enforce a specific signature in the builder functions.
But at least we can static_assert on the effective signature
at the point where we're generating the actual binding configuration
we can't generate a static assertion so easily here.
Problem is, when forming this type, we don't know if
the user will override and provide a custom binding
in some chained call within the nested DSL.
Might still be able to come up with some clever trick,
like e.g. returing an unsuitable marker type from these
dummy default implementations and then, later on, when
actually building the collection binding, to detect
those marker types and rise a static assert at that point.
This would at least give us a better error message,
and in theory, it should always be possible to
detect this kind of misuse at compile time
...through the use of partial specialisation and SFINAE.
There are some rather specific (yet expectedly not uncommon) cases,
where we'd be able to provide a sensible default for the
- match predicate
- new element constructor
of the binding. While in all other cases, the user
has to provide an explicit implementation for these
crucial building blocks anyway.
the reason is also to enable usage as metafunction,
to disable specialisations for some type which could
never live within a variant record in question
...but does not compile, since all of the fallback functions
will be instantiated, even while in fact we're overriding them
right away with something that *can* be compiled.
this prompts me to reconsider and question the basic approach
with closures for binding, while in fact what I am doing here
is to implement an ABC.
- the test will use some really private data types,
valid only within the scope of the test function.
- invoking the builder for real got me into problems
with the aggregate initialisation I'd used.
Maybe it's the function pointers? Anyway, working
around that by definint a telescope ctor
when setting up a binding to child elements within a STL collection,
all the variable elements are preconfigured to a more or less
disabled and inactive state.
the concern is for the structure of the builder to be
incomprehensible and completely buried within the
implementation details of the various binding layers
most of the mutation primitives return bool(true)
when /any/ layer or part of the TreeMuator was able
to cope with the diff verb.
This is based on the assumption to configure the
TreeMutator in such a way that at most one facility
will actually handle and apply a given verb. That is,
we'll assume that the TreeMutator acutally wraps and
adapts *one* custom data structure, to which the
diff has to be applied.
The TestWireTap is special, insofar it indeed targets
a *second* data structure, albeit not a "real" one,
just a dest and diagnostics dummy.
the first part of the unit test (now passing)
is able to demonstrate the full set of diff operations
just by binding to a TestMutationTarget.
Now, after verifying the design of those primmitive operations,
we can now proceed with bindings to "real" data structures
when implementing the assignment and mutation primitives
it became clear that the original approach of just storing
a log or string rendered elements does not work: for
assignment, we need to locate an element by ID
this one went through unnoticed, because the situation
is not covered in unit-test. The tests written thus fare
are more like a proof-of-concept. I didn't want to spend
weeks on writing extensive coverage of all corner cases,
at least not before all aspects of the tree diff protocol
are settled. Seemingly this backfires already
now the full API for the "mutation primitives" is shaped.
Of course the actual implementation is missing, but that
should be low hanging fuit by now.
What still requires some thinking though is how to implement
the selector, so we'll actually get a onion shaped decorator
basically we'll establish a collaboration where both sides
know only the interface (contract) of the partner; a safe margin
for allocation size has to be established through metaprogramming (TODO)
what's problematic is that we leave back waste in the
internal buffer holding the source. Thus it doesn't make
sense to check if this buffer is empty. Rather the
Mutator must offer an predicate emptySrc().
This will be relevant for other implementations as well
while the original name, 'replace', conveys the intention,
this more standard name 'swap' reveals what is done
and thus opens a wider array of possible usage
now this feels like making progress again,
even when just writing stubs ;-)
Moreover, it became clear that the "typing" of typed child collections
will always be ad hoc, and thus needs to be ensured on a case by case
base. As a consequence, all mutation primitives must carry the
necessary information for the internal selector to decide if this
primitive is applicable to a given decorator layer. Because
otherwise it is not possible to uphold the concept of a single,
abstracted "source position", where in fact each typed sub-collection
of children (and thus each "onion layer" in the decorator chain)
maintains its own private position
after sleeping one night over the problem, this seems to be
the most natural solution, since the possibility of assignment
naturally arises from the fact that, for tree diff, we have
to distinguish between the *identity* of an element node and
its payload (which could be recursive). Thus, IFF the payoad
is an assignable value, why not allow to assign it. Doing so
elegnatly solves the problem with assignment of attributes
Signed-off-by: Ichthyostega <prg@ichthyostega.de>
I assumed that, since GenNode is composed of copyable and
assignable types, the standard implementation will do.
But I overlooked the run time type check on the opaque
payload type within lib::Variant. When a type mismatch
is detected, the default implementation has already
assigned and thus altered the IDs.
So we need to roll our own implementation, and to add
insult to injury, we can't use the copy-and-swap idiom either.
This is actually a STL library feature, and was added precisely
for the reason encountered here: if we want logarithmic search,
we'll have to construct a new GenNode object, just to have something
for the set to invoke the comparison operator.
C++14 introduced the convention that the Comparator of the set
may define a marker type `is_transparent` alongside with a generic
comparison operator. But, as is obvious from the source code of
our GNU Standard library implementation, our std::set has no such
overload to make use of that feature
http://en.cppreference.com/w/cpp/container/set/findhttp://stackoverflow.com/questions/20317413/what-are-transparent-comparators
The only good thing is that, just 10 minutes ago, I felt like
a complete moron because I'm writing a unit test for such a simple
storage class. ;-)