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 :-/
as it turns out, we need to set the property_expand() on the child widget
within Gtk::Expander explicitly, to cause the child to grab and additional
available screen space (which obviously is what we want in case of a
log display with scrollbars)
basically Gtk::Expander will do the trick.
However, resizing of the enclosing panel is not handled properly,
the log does not expand to take up the available space, as it did
automaticall when just added directly into the frame
no need to define a private function on Wizard anymore, it just forwards the call
to the service actually implementing the view allocation. For now this is the
PanelLocator (and eventually this will be the ViewLocator / ViewSpecDSL)
PanelLocator is a sub component of the WindowLocator (top-level GUI service).
Eventually this shall become a mere widget/component access service, with the
actual lookup and allocation logic layered on top through ViewLocator, configurable
via ViewSpec-DSL.
We can not implement the full scheme right now, since we're lacking knowledge
about internals of a typical Lumiera UI widget
This is only a premature hack, since the whole structure of PanelManager is somewhat broken.
Moreover, the ViewLocator is not really ready for use yet, so this hack at least
allows us to "reach into" a top-level window and "grab" the pannel we need.
* have a dedicated "information hub" controller, which acts a receiver of "error log messages" on the UI-Bus
* let that controller in turn allocate an apropriate view on demand
The goal is to build a (in itself completely meaningless) ping-pong interaction
between the UI and Proc-Layer, for the purpose of driving the integration ahead.
The immediate challenge is how to create and place an apropriate "GuiComponentView",
i.e. a Tangible, which is connected to the UI-Bus with an predictable EntryID.
And the problem is to get that settled right now, without building the envisioned
generic framework for View allocation in the UI. When this is achieved,
it should be a rather small step to actually send those notifications over
the UI-Bus, which is basically implemented and ready by now.
right now this will just end up in the log, since not even the
notification display is implemented beyond the GuiNotification-facade.
Anyway, we get some kind of communication now for real, in the actual application
...because due of #211, we usually don't execute commands yet.
For now there is only the backdoor to prefix the command-ID with "test"
With this change, the TODO message appears now immediately after GUI start!
In the end, I decided against building a generic service here,
since it pretty much looks like a one-time problem.
Preferrably UI content will be pushed or pulled on demand,
rather than actively coding content from within the UI-Layer
- activation signal is a facility offered and used solely by Gtk::Application
- we do not need nor want an Gtk::Application, we deal with our own application
concerns as we see fit.
Gio::Application holds a signal_activation(), which seems to be used for
precisely that task we need here: to do something right after the UI is operative
...and while doing so, also re-check the state of the GTK toolkit initialisation.
Looks like we're still future-proof, while cunningly avoiding all this
Gnome-style "Application" blurb
I will abandon work on the ViewSpec DSL in current shape (everything fine with that)
and instead work on a general UI start-up and content population sequence.
From there, my intention is to return to the docks, the placement of views
and then finally to the TimelineView
This finishes the first round of design drafts in this area.
Right now it seems difficult to get any further, since most of
the actual view creation and management in the UI is not yet coded.
looks like I'm trapped with the choice between a convoluted API design
and an braindead and inefficient implementation. I am leaning towards the latter
looks like we're hitting a design mismatch here....
...and unfortunately I have to abandon this task now and concentrate
on preparation of my talk at LAC.2018 in June
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).
The original goal for #1129 (ViewSpecDSL_test) is impossible to accomplish,
at least within our existing test framework. Thus I'll limit myself to coding
a clean-room integration test with purely synthetic DSL definitions and mock widgets
Problem is, we can not even compile the conversion in the "other branch".
Thus we need to find some way to pick the suitable branch at compile time.
Quite similar to the solution found for binding Rec<GenNode> onto a typed Tuple
Attempt to find my way back to the point
where the digression regarding dependency-injection started.
As it turns out, this was a valuable digression, since we can rid ourselves
from lots of ad-hoc functionality, which basically does in a shitty way
what DependencyFactory now provides as standard solution
FIRST STEP is to expose the Navigator as generic "LocationQuery" service
through lib::Depend<LocationQuery>
more of a layout improvement, to avoid any code duplication.
The mechanics remain the same
- write an explicit specialisation
- trigger template intantiation within a dedicated translation unit
from now on, we'll have dedicated individual translation units (*cpp)
for each distinct interface proxy. All of these will include the
interfaceproxy.hpp, which now holds the boilerplate part of the code
and *must not be included* in anything else than interfac proxy
translation units. The reason is, we now *definie* (with external linkage)
implementations of the facade::Link ctor and dtor for each distinct
type of interface proxy. This allows to decouple the proxy definition code
from the service implementation code (which is crucial for plug-ins
like the GUI)
The recently rewritten lib::Depend front-end for service dependencies,
together with the configuration as lib::DependInject::ServiceInstance
provides all the necessary features and is even threadsafe.
Beyond that, the expectation is that also the instantiation of the
interface proxies can be simplified. The proxies themselves however
need to be hand-written as before
I am fully aware this change has some far reaching ramifications.
Effectively I am hereby abandoning the goal of a highly modularised Lumiera,
where every major component is mapped over the Interface-System. This was
always a goal I accepted only reluctantly, and my now years of experience
confirm my reservation: it will cost us lots of efforts just for the
sake of being "sexy".
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
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.
...which showed up under high system load.
The initialisation of the member variables for the check sum
could be delayed while the corresponding thread was already running
- 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