turns out to be somewhat tricky.
The easy shot would be to use the comma operator,
but I don't like that idea, since in logic programming, comma means "and then".
So I prefer an || operator, similar to short-circuit evaluation of boolean OR
Unfortunately, OR binds stronger than assignment, so we need to trick our way
into a smooth DSL syntax by wrapping into intermediary marker types, and accept
rvalue references only, as additional safeguard to enforce the intended inline
definition syntax typical for DSL usage.
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)
...as it turned out, the result type was the problem: the lambda we provide
typically does not yield an Allocator, but only its baseclass function<UICoord(UICoord)>
solution: make Allocator a typedef, we don't expect any further functionality
...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)
...this was quite an extensive digression, which basically gave us
a solid foundation for topological addressing and pattern matching
within the "interface space"
rationale: sometimes (likely this is even the standard case) we do not just
want to "extend", rather we want to extent at very specific levels.
This is easy to implement, based on the existing building blocks for path manipulation
the original construction works only as long as we stick to the "classical" Builder syntax,
i.e. use chained calls of the builder functions. But as soon as we just invoke
some builder function for sake of the side-effect on the data within the builder,
this data is destroyed and moved out into the value return type, which unfortunately
is being thrown away right afterwards.
Thus: either make a builder really sideeffect-free, i.e. do each mutation
on a new copy (which is kind of inefficient and counterfeits the whole idea)
or just accept the side-effect and return only a reference.
In this case, we can still return a rvalue-Reference, since at the end
we want to move the product of the build process out into the destination.
This works only due to the C++ concept of sequence points, which ensures
the original object stays alive during the whole evaluation of such a chained
builder expression.
NOTE: the TreeMutator (in namespace lib::diff) also uses a similar Builder construction,
but in *that* case we really build a new product in each step and thus *must*
return a value object, otherwise the reference would already be dangling the
moment we leave the builder function.
- the default should be to look for total coverage
- the predicates should reflect the actual state of the path only
- the 'canXXX' predicates test for possible covering mutation
I set out to "discover" what operations we actually need on the LocationQuery
interface, in order to build a "coordinate resolver" on top. It seems like
this set of operations is clear by now.
It comes somewhat as a surprise that this API is so small. This became possible
through the idea of a ''child iterator'' with the additional ability to delve down and
expand one level of children of the current element. Such can be ''implemented''
by relying on techniques similar to the "Monads" from functional programming.
Let's see if this was a good choice. The price to pay is a high level of ''formal precision''
when dealing with the abstraction barrier. We need to stick strictly to the notion of a
''logical path'' into a tree-like topology, and we need to be strong enough never to
give in and indulge with "the concrete, tangible". The concrete reality of a tree
processing algorithm with memory management plus backtracking is just to complex
to be handled mentally. So either stick to the rules or get lost.
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
we need to layer our Navigator implementation on top,
since this object needs to capture a reference to the "current position".
This is necessary to be able to derive the child position by extending
and then to form a child navigator -- which is the essence of
implementing expandChildren()
...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....
NOTE it just type checks right now,
but since meta programming is functional programming, this means
with >90% probability that it might actually work this way....
...which also happens to include sibling and child iteration;
this is an attempt to reconcile the inner contradictions of the design
(we need both absolute flexibility for the type of each child level iterator
yet we want just a single, generic iterator front-end)
...this was a difficult piece of consideration and analysis.
In the end I've settled down on a compromise solution,
with the potential to be extended into the right direction eventually...
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.
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 was deliberately represented in an asymmetric way, to verify the
design's ability to cope with such implementation intricacies. So basically
we have to kick in at LEVEL == 1 and access the implementation differently.
This exercise just shows again, that treating tree structures recursively
is the way to go, and we should do similar when coding up the query-API
for the real GTK toolkit based window elements...
this fixes a silly mistake:
obviously we want named sub-nodes, aka. "Attributes",
but we used the anonymous sub-nodes instead, aka. "Children"
Incidentally, this renders the definitions also way more readable;
in fact the strange post-fix naming notation of the original version
was a clear indication of using the system backwards....
the intention is to rely solely upon this abstract interface
in order to navigate the structure of the actual UI, so the
resolution process remains decoupled from the technicalities
of the actual UI toolkit set.
Through implementation of the corresponding unit test we'll determine
what it actually takes to build such a path resolution algorithm...
obviously, we get a trivial case, when the path is explicit,
and we need a tricky full blown resolution with backtracking
when forced to interpolate wildcards to cover a given UICoord
spec against the actual UI topology.
Do we need it?
* actually not right now
* but already a complete implementation of the ViewSpec concept
requires such a resolution
It is not possible to inherit through boost operators
and defining them explicitly is not that much fuss either.
Plus we avoid the boost include on widely used header
the usual drill...
once there is one additional non explicit conversion ctor,
lots of preferred conversion paths are opened under various conditions.
The only remedy is to define all ctors explicitly, instead of letting the
compiler infer them (from the imported base class ctors). Because this way
we're able to indicate a yet-more-preferred initialisation path and thus
prevent the compiler from going the conversion route.
In the actual case, the coordinate Builder is the culprit; obviously
we need smooth implicit conversion from builder expressions, and obviously
we also want to restrict Builder's ctors to be used from UICoord solely.
Unfortunately this misleads the compiler to do implement a simple copy construction
from non const reference by going through the prohibited Builder ctor, or to
instantiate the vararg-ctor inherited from PathArray.
Thus better be explicit and noisy...
After completing the self-contained UICoord data elements,
the next thing to consider might be how to resolve UI coordinates
against an actual window topology. We need to define a suitable
command-and-query interface in order to build and verify this
intricate resolution process separated from the actual UI code.
exploring the idea of a configuration DSL.
As a first step, this could be a simple internal DSL,
implemented as a bunch of static functor objects, which are internally bound
and thus implemented by the ViewLocator within InteractionDirector
responsible for access and allocation of component views.
Internally wired to the PanelLocator within the global WindowLocator
This setup settles those nasty qeustions of crosswise top-level access
this starts work on a new UI global topic (#1004)
- coin a new term: "view component"
- distinction between veiw component and Panel
- consider how to locate view components
- WindowList becomes WindowLocator
...since the generateErrorResponse() in UiDispatcher already adds some
explanatory boilerplate to the message; and we can not do anything beyond
publishing the message into the UI message box