Commit graph

737 commits

Author SHA1 Message Date
2ea89fcb54 Dispatcher: rework loop control logic
- we got occasional hangups when waiting for disabled state
- the builder was not triggered properly, sometimes redundant, sometimes without timeout

As it turned out, the loop control logic is more like a state machine,
and the state variables need to be separated from the external influenced variables.

As a consequence, the inChange_ variable was not calculated properly when disabled in a race,
and then the loop went into infinite wait state, without propagating this to
the externally waiting client, which caused the deadlock
2018-12-10 00:12:52 +01:00
a49d79ffbd Library: fix spurious wake-up from (non)timed wait
A classical carry-over of dirty values...
Problem arises, when starting an unconditional wait on the same object monitor,
which previously conducted a timed wait. Then the obsolete timeout from the previous
wait remained in place, causing our Sync-Wrapper (erroneously) to assume a timed wait
and then pthread to return immediately from this timed wait.

The result was permanent idle looping in the ProcDispatcher, after the first command was processed
2018-12-10 00:09:56 +01:00
8d6cb19e3f Global-Layer-Renaming: fix handling of GuiResources in the build
the new structure causes them now to be installed into $TARGET/stage
which is simply not what I want. I still consider $TARGET/gui the better choice,
since an administrator or packager is not aware of our layer namings.

The existing solution was half baked anyway, it did not really replicate the source tree.
On the other hand, I want to retain the location of the CSS files within the GUI tree,
since I consider it a good practice, to keep "code-like" resources with the actual code,
and not far away in some arcane "data" directory.

No I've noticed, that the env.GuiResource() function is only used once, for this very task.
So, for the time being, we can keep it simple and deditaced to that task, i.e
we pick up all CSS files we find and install it into a single target directory.

NOTE: this issue has brought to my attention two further, completely unrelated issues

 * Ticket #1192 (Lumiera hangs on failed GUI start)
 * The ProcDispatcher does an idle wait, due to an error in timed-wait implementation
2018-11-16 18:18:33 +01:00
cc2ff520ed DOC: Plan to rename the three Layers
Considering this since some time, since it more and more occurred to me
the existing conventional names are a misfit. And they are dull and clumsy.

This fall, I mentioned it to Benny, and he seemed to be rather favourable towards that idea,
which encourages me just to go ahead. Unfortunately, I am alone on the coding frontier
right now, which has several downsides, but at least it gives me the ability
to pull off radical moves.
2018-11-15 16:06:55 +01:00
866d7efe0a Timeline: push the trackname attribute down into the widget/display
...in accordance to our general design guideline: we don't duplicate
actual model values within the controllers/presenters, since our widgets
act themselves as view-model
2018-11-10 03:02:24 +01:00
c8dc5a24a8 DummySessionConnection: extend population diff to send distinct root-track
This change demonstrates how to deal properly with possible duplicate entities
with similar symbolic ID: define a RandomID (to guarantee a distinct hash on each instance).
In the actual implementation, this should happen already within the domain model,
not when constructing the diff (obviously of course...)

This change also adds a mutation sequence to inject the actual track name
2018-11-10 02:39:17 +01:00
a4c37ed99c Library: allow for an explicitly random EntryID
same pattern as the existing EntryID, i.e. a human readable symbol plus a hash
but the hash is just random, instead of deriving it from the symbol text.

Use case is when we explicitly need a distinct identity, even when the
human readable symbolic name is the same. Actual example: the fork root in the timeline
2018-11-10 01:01:59 +01:00
7cc68fadea GenNode / Timeline: allow to mutate attribute objects with explicitly given ID
so this seems to be the better approach for dealing with this insidious problem.
In some cases -- as here most prominently with the root track within the timeline --
we have to care within the domain model to prepare unique ids even for sub objects
treated as attributes. In the actual case, without that special attention,
all timelines would hold onto an attribute "fork" with the same ID, based
on the type of the nested object plus the string "fork". Thus all root track
representations in the GUI would end up listening to the same ID on the UI-Bus...
2018-11-09 22:55:08 +01:00
1bbe903202 GenNode: revert -- better not handle this problem on ETD level
...rather extend the "object builder" DSL notation to allow passing in a given EntryID literally.
Rationale is, we should handle the problem of unique IDs on the level of the domain model.
If we attempt to "fix" this within GenNode, the price would be to make the ETD creation stateful
2018-11-09 22:50:48 +01:00
83394a6f01 GenNode: investigate Problem with non-unique attribute IDs
this is not a problem, strictly sepaking, locally.
But it becomes a problem once the GUI uses those attribute IDs
as Element-IDs for tangible UI entities, which need to be uniquely
addressable via the UI-Bus.

An obvious solution is to inject randomness into the Attribute ID hash
2018-11-09 20:19:45 +01:00
20451c958a Timeline: add preliminary track-head display
just some labels, so that we can see the added content
TODO: unsolved problem: how to pass the track name
2018-11-06 01:01:00 +01:00
13286f4b90 ElementBox: define the desired properties of this fundamental building block (#1185) 2018-11-01 20:37:36 +01:00
04b665afd1 Timeline: concept for the TrackBody helpers
these recursively nested helper entities work together with the TimelineCanvas
and enable the latter to draw the track background in the Timeline Widget and
to find out about the vertical coordinates where to place content (Clip, Effects, Markers)
2018-10-31 03:52:24 +01:00
0aa4a8cb42 Timeline: make the Patchbay a Viewport container, to follow body scrolling
Gtk::Viewport allows to add the ability to scroll a partial view window
for a container larger than the available display area. The position
and movement of this window is controlled by Gtk::Adjustments,
which can be located elsewhere.

Here we use the existing Adjustments of the ScrolledWindow
holding the body canvas; this setup makes the header pane follow
the scroll movements of the body
2018-10-30 03:37:55 +01:00
8803af1a0a Timeline: further steps towards attaching the widget structure 2018-10-28 18:56:04 +01:00
3dd3fc7810 Timeline: decide upon the organisation of the header pane
we'll uses a recursive structure here, based on nested grids
2018-10-28 01:56:24 +02:00
c212ce94ca Timeline: setup basic widget structure 2018-10-28 01:30:02 +02:00
2d4e58db02 Timeline: consider how to manage size and layout of timeline contents
bottom line is to do most autmatically, and to establish a slave-relation
navigation-area -> timeline-ruler
header-pane-content -> corresponding track-body

this can be accomplished mostly by connecting the aproprieate signals,
thus these widgets will live within the Layout-Manager, which consequently
is renamed into TimelineLayout
2018-10-27 17:27:29 +02:00
c3d91d4ed3 Timeline: draft for building the nested recursive display structure
the solution idea is to use a helper frame, and an "anchor functor",
which is passed down from the respective parent context, and which
does the actual work of injecting the child widgets at the apropriate
position within the parent display.
2018-10-27 01:52:46 +02:00
572bd38fec DummySessionConnection: produce a simple population diff message
seems to work surprisingly well...
the diff application poceeds in the GUI up to the point
where the TrackPresenter need to be inserted into a two-fold display context
2018-10-15 02:54:42 +02:00
de5f0b85d4 DummySessionConnection: new tab in the TestControl dialog box
...to trigger the new fake-functionality
2018-10-14 23:59:35 +02:00
77902d54a6 DummySessionConnection: prepare schaffolding for fake-commands (see #1042)
To drive the timeline display in the UI ahead, the plan is to have
a faked action, which injects dummy population diff messages into the GUI,
resulting in the build-up of a typical simple session timeline
2018-10-14 17:24:13 +02:00
67cccbdc5d Timeline: actually accept and install the TimelineWidget
As starting point, provide an empty placeholder widget to fill the void
2018-10-14 03:48:39 +02:00
6e18452c37 Timeline: arrange for a tabbed notebook to hold the timeline widgets
...and remove all the leftover test and research code from 2016
which was archived to /research some days ago
in 3f87ef43ec
2018-10-13 21:56:36 +02:00
202b1e4dbd Timeline: implement handling of INS verb to create new Timeline
decision: for now we will represent *every* Timeline present in the Session.
Later it would also possible to skip some representation; however we'd need
a way to store such presentation state such that we'd be able to get at this
persisted stat right at this point here, when processing the Diff.
2018-10-13 03:47:31 +02:00
bfca473dce Timeline: decide upon the diff format expected for creating a timeline
other than the regular way of building an object,
we do expect a minimal structure to be sent right within the INS message.

Rationale: the standard way would allow for too much leeway and created
unwanted intermediary states. The non-standard way decided upon here
is well within the limits of our diff language
2018-10-13 02:46:09 +02:00
e81b0592d3 TreeMutator: combine no-op layer with selective other diff binding
...and complete unit test coverage.
This is complex stuff and we'd better be careful it actually works
2018-10-12 02:05:11 +02:00
0d5f29446b TreeMutator: provide no-op implementation
how to further your career with eight simple steps
2018-10-12 01:07:13 +02:00
fb93e349da TreeMutator: conjure up a black hole mutator
...which is a somewhat involved version of /dev/null
2018-10-11 23:56:33 +02:00
82321a7594 Timeline: draft solution how to delegate to the actual TimelineWidget
Problem is, the InteractionDirector, being the representation of the model root,
needs to manage and maintain the collection of "timelines". However, these
can not be widgets, rather, they need to attach to widgets living within
the GUI widget structure proper, i.e. within the TimelinePanel

proposed solution is to build a smart handle based on WLink,
but also delegating the DiffMutable interface
2018-10-11 17:21:47 +02:00
b65db50666 Timeline: some considerations regarding timeline slave display (#1083) 2018-10-11 14:29:55 +02:00
2adcabbef5 Timeline: draft the root attachment point where timelines are created by diff
This involves a fundamental decision about how to build structures in the Lumiera UI:
They shall be solely created in response to diff messages. Which leads us to
introduce a new (and quite challenging) concept: the »DiffConstituent«
2018-10-10 05:45:46 +02:00
08ed6e1ee8 Timeline: building the layout and control structure (#1016)
This marks start of actual work on this fundamental task.

Extensive planning from 2016 is available, together with an almost
complete diff binding for the entities involved into timeline display.
2018-10-08 02:27:28 +02:00
76dd4fb5dc ...tidy.up: prepare for working on the timeline display
''a new hope''

This was quite a long way until we're back at the point of
re-building the timeline anew.

Stash the canvas research code to make room for new things to come
2018-10-07 03:44:00 +02:00
cd557f50ec DemoGuiRoundtrip: successfully completed (closes #1999) 2018-10-06 17:42:22 +02:00
e8931bf4bf NotificationDisplay: react on changes of the error state
this turned out to be more tricky than expected.
When we initially configure the UI and invoke this->show_all(),
seemingly some draw-callbacks will be scheduled into the event loop.
Just set_visible(false) on the relevant buttons directly after that call
will have no effect (since the widget is still hidden at that point anyway,
it is not yet mapped and realised).

Thus we need to schedule a callback with the Glib::signal_idle(),
so our state detection runs after the initial mapping of the UI


NOTE: there is a minor itch, which I don't address right now:
when adding the error state and thus revealing the additional buttons,
the error log grabs some additional horizontal space, even while there
would be ample space for the additional buttons within the button bar.
When the error state is cleared and the buttons thus hidden again,
the additional horizontal space is dropped and the error log gets
narrower. Probably we'd need some special GTK call to re-allocate
the required space properly
2018-10-05 18:26:26 +02:00
12344ae9d8 NotificationDisplay: add an Error-State and implement signal to trigger on change
this is more or less gratitious functionality for now,
yet I consider it a proof-of-concept
2018-10-05 15:59:21 +02:00
33af82cf73 NotificationDisplay: now responding to the "Flash" message on UI-Bus
solved by temporarily adding a CSS class.
Mostly this was an issue of writing the Stylesheet properly.

Hint: use the GTK+ inspector, i.e. run with

GTK_DEBUG=interactive target/lumiera
2018-10-05 05:36:53 +02:00
e573d3cc96 StyleCSS: add alternative stylesheet to be comined with the system theme (#1170)
Even while we (still) have the goal to ship our own stylesheet and provide
the typical subdued media-aplication look, right now this porting and styling effort (#1023)
is unfinished and handled with rather low priority (writing code is more important
than toying with styles and looks).

This alternative stylesheet is meant to be used with a typical "light" desktop theme.
We'll add just the bare minimum of definitions to make lumiera work well in that setup.
And right now, I'll use that setup to continue with my development work
2018-10-05 03:25:50 +02:00
fb4d9be2b4 draft generic decorator to make a widget flash
not finished, having problems with Lumiera's stylesheet
2018-10-05 00:16:45 +02:00
5aa28626ad NotificationDisplay: function to demote error entries into warnings
...and remove them from the mark-index for special handling
2018-10-03 19:33:28 +02:00
e9527d6304 NotificationDisplay: proper handling of marks at insert position
Basically we create a pair of marks, with left/right gravity and then
inject the content between. Unfortunately, when the insert position
is the very end of the buffer (which it always is), this trick
leads to nesting the marked regions into each other.

As a remedy, we first insert the trailing newline,
and then attach the insert position one step before
2018-10-03 19:13:39 +02:00
4635d18265 NotificationDisplay: draft function to retain only the errors
discard all other info log messages and retain only the entries marked as error.
This is also a proof-of-concept regarding position bookmarks and markup.

Implemented by populating a new buffer and swapping it into place.
2018-10-03 17:50:20 +02:00
c6b8811af0 Library: utility to interpret a text as bool value (yes/no)
...also fixes the problem with the "expand" mark in DemoGuiRoundtrip
2018-10-03 04:43:16 +02:00
7655960b23 Fix Zombie invocation in GUI shutdown (closes #1178)
== possible Scenario ==
 1. Gui: sigTerm invoked
 2. last Subsystem -> cleans all remaining Subsy entries
 3. main-Thread wakens
 4. leaves main() und undloads the GUI plug-in
 5. which destroys the `DependencyFactory<LocationQuery>` placed in static memory
 6. the Gui-Thread returns from sigTerm()  and invokes `~UiManager()`
 7. which indirectly deregisters through `InteractionDirector` the `LocationQuery` Service
 8. `DependInject::Service::shutdown()` grabs the Lock ==> **BOOM**

== Solution ==
Ensure all dtors of the UI backbone are invoked ''prior'' to calling sigTerm()
2018-10-02 02:45:01 +02:00
4e94dfd4d9 FailureHandling: improved ZombieCheck
now capturing the Zombie's ID

==> surprise, its ClassLock<gui::interact::LocationQuery>
2018-10-01 05:51:21 +02:00
70c8718258 FailureHandling: rectify shortcomings in Proc-Command error handling
and especially our provisional dummy code to execute some commands "right here"
should also check and raise captured exceptions from command invocation
2018-10-01 04:09:45 +02:00
77c9a6a1da FailureHandling: investigate crash in emergency shutdown
As it turns out, several problems reinforce each other
- lumiera error does not properly propagate the cause message
- our test/dummy code does not check the ExecResult
- thus the exception is detected rather accidentally, when entring the next sync/wait state
- emergency shutdown is chaotic in its very nature (this is well known...)
- but especially triggerShutdown is not airtight and might die...
- causing the shutdown to hang....

And last but not least, a ZombieCheck tripwire got triggered,
but unfortunately I was unable to get hold of the zombie iteself
2018-10-01 04:09:45 +02:00
23430f8800 NotificationDisplay: fix improper state mark for "expand" (WIP)
test_meta_markAction always produces a state mark with payload type string.
However, the model::Tangible expects a bool payload when handling the "expand" mark.

- add diagnostics to lib::variant to indicate expected and actual payload type
- attempt to fix with boost::lexical_cast; this is insufficient, since
  you'd expect such a function to understand "true" and "false" etc.

Moreover, raising this exception causes emergency shutdown, which
flounders due to triggering a ZombieCheck. Interesting.
2018-10-01 00:47:19 +02:00
f9c6a49b9b NotificationDisplay: implement reset/clearAll 2018-10-01 00:47:19 +02:00
f97beaa774 GuiNotification: implementation complete (closes #1047)
The very backbone structure of the Lumiera UI, the UI-Bus is now fully defined
and proven to be operative, including asynchronous dispatch of messages
an a generic notification mechanism
2018-10-01 00:46:22 +02:00
5fd3fb3d7b DemoGuiRoundtrip: first successful complete roundtrip GUI->Proc->GUI (see #1099)
A communication chain, triggered from a button in a non-modal dialog box,
passing invocation into another thread, dispatched by the ProcDispatcher,
then again passing thread boundaries to push a response back into the UI.

This is a milestone, and integrates several components built during the last years.
2018-09-29 17:34:25 +02:00
abfb897336 DemoGuiRoundtrip: now invoke the Proc-Layer commands from within the UI dialog 2018-09-29 15:23:47 +02:00
e54556f565 DemoGuiRoundtrip: draft mock commands to be invoked
this would be the first half of the roundtrip, the call UI -> Proc
2018-09-29 13:37:48 +02:00
6b941b2c1e TestControl: retrieve action arguments from the dialog controls
...and this also demonstrates how action code will typically be arranged within such a dialog page
2018-09-29 01:26:35 +02:00
10b9abd79b TestControl: build the necessary control widgets
- a text input field
- a trigger to invoke the showInfo function on GuiNotification
- triggers to send state mark messages via GuiNotification into the UI-Bus
- a combo box to define the action-ID within those state mark messages

With these controls, it should be possible to execute all the variations
of the Tangible element protocol and verify the respective behaviour
has been coded up properly within the receiving ErrorLogDisplay widget

Note the key point (and the next step to code up) is for #1099 to
invoke a dummy/demo command in Proc-Layer, which in turn pushes an
reaction via the GuiNotification facade back into the UI asynchronously...
2018-09-27 04:15:27 +02:00
77980ef024 TestControl: the first tangible UI feedback caused via UI-Bus (see #1099)
wrap up the helpers and wire the connection to the UI-Bus.
Then attempt a direct invocation, still within the GTK thread.

While this might seem as just some silly experiment, in fact it is
*** THE FUCKING FIRST TIME to transmit a visible action to a real widget ***

this links together and integrates various efforts achieved during the last years
2018-09-26 17:17:59 +02:00
74f3ab3932 TestControl: concept draft how to simplify building notebook widgets
Gtk::Notebook is a quite powerful container foundation to build complex dialog widgets with multible pages on tabs.
Hower, the construction, wiring an setup is notoriously tedious, due to the repetitiveness
and the sheer amount of child widgets spread over various pages.

This design draft is an attempt to mitigate the required boilerplate, without
overly much obscuring the structure. The basic idea is to package each page into
a locally defined child struct, which is actually heap allocated and managed automatically.
This way, each child page gets its own namespace, and wiring to other components
is made explicit by passing named ctor arguments -- while the overall structure
of building and wiring of widgets stays close to the habits of Gtkmm programming.
2018-09-26 15:47:39 +02:00
0c082361f3 TestControl: hooked up a simple child-dialog (see #1174)
...which gives us already the base functionality required to run the first tests

- can be triggered from the Help menu
- non-modal dialog (Gtk::Dialog)
- attached as child / slave-Window to the current active workspace window
- window manager hint to keep it on top
- have a notebook control within the dialog
- attached (passively) to the UI-Bus
2018-09-25 06:11:38 +02:00
7f760d77e5 TestControl: prepare a location in the UI for test and diagnostics (#1074) 2018-09-25 05:31:41 +02:00
245a07bc16 NotificationDisplay: integrate for warning and error display
Surprise, surprise.... it really works
our "bang" messages are fantastic, yellow and bold now
2018-09-24 04:17:04 +02:00
fa55ff63d5 NotificationDisplay: investigate ways to define the markup tag(s)
...with the option to expand this approach later to use a central StyleManager (#1169)
2018-09-24 03:30:42 +02:00
5b14e83ebf NotificationDisplay: investigate options to organise error display markup
...just to decide not to follow-up too much on that topic right now.
As it turns out, GTK seems to be lacking in that respect. I have plotted
some ideas how we could work around that discrepancy in future...

And for this simple DemoGuiRoundtrip, we'll just use direct styling,
but we'll store a table of bookmarks for the error entries, allowing
us to add further features later on top
2018-09-23 16:20:24 +02:00
0be0f77c16 NotificationDisplay: wire up simple message display 2018-09-21 14:38:38 +02:00
8fa3eb2517 NotificationDisplay: integrate with the new Revealer-Functor (closes #1162)
after an extended digression to fix our matcher for tests on the EventLog,
the new helper abstractions gui::model::Expander and gui::model::Revealer
are now covered and ready for use.

In this special case here, the controller uses both the Expander and Revealer
inherited from model::Tangible; yet both are wired to access the actual
display widget via the getter, and delegate to the Expander rsp. Revealer
located within the widget. Which in turn are wired when creating the widget
within the InfoboxPanel.

Bottom line -- we have a generic scheme now, and the actual implementation
is filled in as lambda, at the point where the component or widget is created
2018-09-21 05:17:54 +02:00
258a807e97 EventLog: reorder code to accommodate the split
well... reduction in size of the debug build objects
turns out not to be so large as I hoped. But it is significant anyway,
about 3-4MB on the most affected test classes. Plus from now on we
do not repeat that code on other tests using the same features.
2018-09-20 04:11:00 +02:00
1df5bf5c5e EventLog: split into header and dedicated translation unit (WIP) 2018-09-20 02:19:10 +02:00
9e96effcf1 EventLog: prepare for dedicated translation unit
up to now, EventLog was header only, which seems to cause
a significant bloat in terms of generated code size, especially
in debug builds. One major source for this kind of "template bloat"
is the IterChainSearch, rsp. the filter and transformer iterators.

And since EventLog is not meant for performance critical application code,
but rather serves as helper for writing unit tests, an obvious remedy is
to move that problematic part of the code down into a dedicate translation
unit, instead of using inline functions. To prepare this refactoring,
some var arg (templated) API funcitons need to be segregated.
2018-09-20 01:42:31 +02:00
121d78e13b EventLog: now able to write the condition to verify doRevealYourself (#1162)
this initially (on 1.9.18) triggered this extended digression;
The initial naive implementation (without backtracking) did not allow
to express such a simple thing like "function XXX" not invoked (again) after "function XXX"
2018-09-19 03:27:48 +02:00
03a1d58198 EventLog: verify and complete the TestEventLog_test
can now cover all the cases as initially intended,
including backtracking
2018-09-19 02:52:38 +02:00
3994f805b0 EventLog: further bugfix to get the augmented sequencing logic correct.
For the before / after chaining search functions,
we now do one single step in the respective direction before evaluating
the new (next) filter condition. However, we also need to *deactivate* the
previous condition, otherwise that single "step" might cause us to jump
or even exhaust the underlying filter, due to the old filter condition
still being applied.
2018-09-19 01:24:26 +02:00
991b8ace82 EventLog: rectify the quirky logic for before / after chains
due to the lack of real backtracking, the existing solution
relied on a quirk, and started the before / after chained search
conditions /at/ the current element, not after / before it.

Now we're able to remove this somewhat surprising behaviour, yet to do so
we also need to introduce basic "just search" variations of all search
operations, in order to define the initial condition for a chained search.
Without that, the first condition in a chain would never be able to
match on the header entry of the log
2018-09-19 00:21:09 +02:00
0c7996fe90 EventLog: drop-in the new IterChainSerach engine
- need to use dedicated steps in the chain for every added condition now

- seems to break the logic on tests on non-match.
  This doesn't come as a surprise, since backtracking can be expected
  to reveal additional solutions.

NOTE: some tests broken, to be investigated

est-event-log-test.cpp:228: thread_1: verify_callLogging: (log.ensureNot("fun").after("fun").after("fun2"))
2018-09-16 03:02:22 +02:00
1683439b32 ChainSearch: backtracking verified -- finished 2018-09-16 01:08:49 +02:00
84399aa407 ChainSearch: verify proper interplay of two dynamic search conditions 2018-09-16 01:08:49 +02:00
84c30fe802 ChainSearch: need to gear up immediately after backtracking
...which can be achieved by checking the backtracking loop
always right after the non-backtracking iteration, exploiting
the fact that the guard conditions of both are complimentary.
So the only case when we'd actually enter the backtracking
loop after regular iteration would precisely be when
we drop down due to exahausting an upper layer.

The result now reads

"sausage-bacon-tomato-and-spam-spam-bacon-spam-tomato-and-spam-bacon-tomato-and-bacon-tomato-and-tomato-and"

...which sounds correct, yay!
2018-09-16 01:08:48 +02:00
646a2e42cf ChainSearch: need to overload also the iterator function
...since usually such evaluation layers are finally wrapped into
an IterableDecorator and then presented as TreeEplorer -- an exercise
we do not want to perform here, since it is pointless in the typicall
use case. The IterChainSearch is already meant to be ready-for-use.

Thus, instead of wrapping again, the pragmatic solution is simply
to overload the missing operator++ and make it call the augmented
iterNext() function. Related to this, we also need to ensure
proper operation in case no further expansion is mandated
2018-09-16 01:08:48 +02:00
38a1aad897 ChainSearch: bugfixes on reworked construction
...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?
2018-09-16 01:08:48 +02:00
05e6e7f316 ChainSearch: remould construction to get the logic correct
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.
2018-09-16 01:08:45 +02:00
767156e912 TreeExplorer: unit test coverage for injected custom layer 2018-09-16 01:07:23 +02:00
09d923db06 TreeExplorer: add the ability to inject a custom defined layer
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).
2018-09-15 03:09:48 +02:00
8aae789b82 ChainSearch: test case to scrutinise chained filter reconfiguration
...and TADAA ... there we get an insidious bug:

we capture *this by reference into the expansion functor,
and then we move *this away, out from the builder into the target....
2018-09-14 21:06:15 +02:00
29d2c151b3 ChainSearch: add builder function just to replace the filter
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.... :-)
2018-09-14 21:06:15 +02:00
10f21f77f8 ChainSearch: resolve the problems and get basic functionality to work
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.
2018-09-14 21:06:15 +02:00
df7a9492b7 TreeExplorer: helper function so support ChainSearch::clearFilter()
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
2018-09-14 21:06:15 +02:00
5b92f5cf74 ChainSearch: fix broken logic when configuring base layer
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
2018-09-14 21:06:15 +02:00
2ca3e95e9e ChainSearch: allow for overconstrained search
...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
2018-09-14 21:06:15 +02:00
0eec4d3b5f ChainSearch: now use the improved TreeExplorer capabilities to address the shortcoming
...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)
2018-09-14 21:06:15 +02:00
7cdd680e78 TreeExplorer: clean-up after refactoring
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)
2018-09-14 21:06:15 +02:00
9067740456 TreeExplorer: refactor Expander to fit in with the reworked design
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
2018-09-14 21:06:15 +02:00
36d79be8b2 TreeExplorer: refactor Filter in a similar way 2018-09-14 21:06:15 +02:00
1e657acbff TreeExplorer: refactor Transformer to employ the improved wrapping style
...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
2018-09-14 21:06:15 +02:00
a91025bfe0 TreeExplorer: perpare a builder for a suitably adapted, wrapped functor.
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
2018-09-14 21:06:15 +02:00
5d4f1015aa TreeExplorer: consider refactoring of the functor adaptatiion mechanism
...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
2018-09-14 21:06:15 +02:00
e3ca8548a4 TreeExplorer: allow for a disabled filter
...considered as one of the building blocks to resolve Problems in the Design of ChainSearch
2018-09-14 21:06:15 +02:00
34b78fc47e ChainSearch: identify some possible problems
- as implemented now, we will finally backtrack into the unfiltered base iterator
- it is not possible to implement a clearFilter() operation
2018-09-14 21:06:15 +02:00
c0b8b63df0 ChainSearch: ensure to pass current state without spurious copy
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
2018-09-14 21:06:14 +02:00
d923138d1c ChainSearch: configure the core of the chained search mechanism
...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
2018-09-14 21:06:14 +02:00
2b72175e04 ChainSearch: Storage for the filter chain 2018-09-14 21:06:14 +02:00
d398177a71 ChainSearch: now actually build the processing pipeline in the ctor
..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
2018-09-14 21:06:14 +02:00