I worked under the erroneous assumption, that Doxygen
will use its internal entity-IDs as the link-IDs when
generating mardown-links. Yes, this seemed logical and
this would be the way I'd implement it....
But seemingly, Doxygen is not so consistent when it
comes to questions of syntax. The same holds true for
markdown, which lacking a coherent definition anyway.
Another problem is that Doxygen's auto-link generation
frequently fails, for reasons not yet clear to me.
Sometimes it seems to be necessary to give it a nudge
by including the \ref command. While I'm not willing
to go into focussed invstigation of Doxygen syntax
right now, at least I've done a search-and-replace
to remove the malformed links I've written the
last days
the initial draft of this concept is in place now, and
the first round of unit tests pass. I've got some understanding
of the purpose of the interactions and involved elements
and I'm confident this design is evolving in a sane way.
Note: extensive documentation is in the TiddlyWiki,
here I've just pasted and reworded some paragraphs from there
and integrated them into the Doxygen docs
Explanation: sigC++ was already linked as transitive dependency
from gtkmm, since it is used for the "signal-slot" system wihin GTK.
But now we want to use sigC++ itself from our generic UI-Backbone,
so we need to pick up the additional compiler and linker flags
and use them when building the relevant parts of both the application
and the test suite
at the point when we're connecting a new node to the UI-Bus,
the new node's BusTerm is not yet initialised, since we need
the uplink connection we're about to create for setting up
this BusTerm.
Consequently, the new nodes's ID is not yet initialised,
so we need to pass this endpoint-ID explicitly to the
registration function.
since, by definition, the Nexus is "the" up-link,
all we need is clever overriding of the relevant
handling functions, so the nexus will care for the routing,
while the CoreService cares for command and presentation
state handling
next step will be to rig the mock element and set up
and cover the basic / generic element behaviour
This changeset
- adapts the (planned) unit test to the semantic of
the EventLog, which is now fully implemented
- adjusts the function names on the public Tangible interface,
to be better in line with the naming convention of the
corrsponding operations on the UI-Bus:
* "mark" operations are towards the UI element
* "note" messages are from the UI element towards some
state manager, which can be reached via the bus
so this turned out to be rather expensive,
while actually not difficult to implement.
On the way, I've learned
- how to build a backtracking matcher, based on
a filtering (monadic) structure and chained lambdas
- learned the hard way how (not) to return a container
by move-reference
- made first contact with the regular expressions
now available from the standard library
this function is of use also for creating a vector of strings
from a bunch of C-Strings, but it could also be used to
construct other stuff initialised by strings (e.g. RegExps)
this deals with a recurring problem in test code:
very common "simple" fractional values can not be represented
precisely as binary floating point. The classical example is 0.1
Since this is a diagnostics facility, we can cheat around this
insidious problem by just setting a limited rendering precision.
Floating point numbers behave deterministic; you just need
to know how to deal with limited precision.
abandon the use of an assertion exception to signal match failure,
rather use a final bool conversion to retrieve the results.
Error messages are now delivered by side effect into STDERR
The reason is we're unable to deliver the desisred behaviour
with the chosen DSL syntax in C++ ; on a second thought the
new approach is even better aligned with the overall way
we're writing tests in Lumiera. And we produce match-trace
messages to indicate the complete matching path now
implemented a solution to determine negative matches.
But because this solution relies on throwing from a destructor,
it is not possible to catch the resulting assertion failure.
Not sure why (AFAIK there is no second exception thrown
while unwinding the stack), but throwing from dtors is
considered "undefined behaviour" anyway.
So this solution is of limited use
beyond that solution, I'm not sure if the desired syntax
can be implemented at all in C++. Seems that we need to build
a bracketing construct, first to initiate a negated match
and finally, after all queries, to detect if there happened
any failure or not
...no need to enclose empty sections when there are no
attributes or no children. Makes test code way more readable.
TestEventLog_test PASS as far as implemented
...and fix an error (header include order of diagnostics facility)
which prevented the first matcher implementation to work
the after()-match now works as expected
this is the tiny bit of operational functionality needed on top:
whenever we're reconfiguring the predicate, we need to re-trigger
the evaluation (and clear the cached value)
n.b.: I've verified in debugger that the closure is
allocated on the heap and the functors are passed by value
after looking into our various iterator tools,
it seems obvious that our filtering iterator implementation
has almost all of the required behaviour; we only need to
add a hook to rewrite and extend the filtering functor,
which can now nicely done with a lambda closure.
This means all memory management, if necessary, is
pushed into std::function and the automated memory
management for closures provided by the runtime.
...providing the standard implementation of UI-Bus connectivity.
It seems reasonable to place all of the UI-Bus implementation into
a single translation unit
what you see here now is just the tip of the icebearg...
If we follow this route, the Lumiera UI will become way more
elaborate and responsive than average desktop applications
..while we should note at this point that the whole techique
of hijacking std::hash is superfluous now, since the standard libray
does no longer define a static assertion which defeats SFINAE
some tests rely on additional diagnostics code being linked in,
which happens, when lib/format-util.hpp is included prior to
the instantiation of lib::diff::Record rsp. lib::Variant.
The reason why i opended this can of worms was to avoid includion
of this formatting and diagnostics code into such basic headers
as lib/variant.hpp or lib/diff/gen-node.hpp
Now it turns out, that on some platforms the linker will use
a later instantiation of lib::Variant::Buff<GenNode>::operator string
in spite of a complete instantiation of this virtual function
being available already in liblumierasupport.so
But the real reason is that -- with this trickery -- we're violating
the single definition rule, so we get what we deserved.
TODO (Ticket #973): at a later point in development we have to re-assess,
the precise impact of including lib/format-util.hpp into
lib/diff/gen-node.hpp
Right now I expect GenNode to be used pervasively, so I am
reluctant to make that header too heavyweight.
preliminary workaround for Ticket #972
On Debian/Jessie, we observed the following error
"gtk-lumiera.css:38:19Theming engine 'adwaita' not found"
even though the package gnome-themes-standard *is* installed
This allows at least to bring the UI up, even if loading
our custom theme and stylesheet fails.
This is a development snaphot pre release of Lumiera.
It features codebase maintenance, upgrade to C++14 and GTK-3
and some work towards a Proc-GUI connection (unfinished)
Update README, AUTHORS, LICENSE and similar release docs.
because otherwise we'd need to send a whole subtree
over the wire and then descend into it just to find an element.
This too is a ripple effect of making '==' deep
well... this was quite a piece of work
Added some documentation, but a complete documentation,
preferably to the website, would be desirable, as would
be a more complete test covering the negative corner cases
yeah, working with open fire is dangerous...
For performace reasons I've undercut the premise
to make GenNode / Record immutable. Now I'm dealing with
raw storage layout together with this quite hairy distinction
between "attribute scope" and "child scope"
In hindsight, it might have been better to implement Record
as a single list, and to maintain a shortcut pointer to jump
to the start of the attributes.
while implementing this, I've discovered a conceptual error:
we allow to accept attributes, even when we've already entered
the child scope. This means that we can not predictable get back
at the "last" (i.e. the currently touched) element, because this
might be such an attribute. So a really correct implementation
would have to memorise the "current" element, which is really
tricky, given the various ways of touching elements in our
diff language.
In the end I've decided to ignore this problem (maybe a better
solution would have been to disallow those "late" attributes?)
My reasoning is that attributes are unlikely to be full records,
rather just values, and values are never mutated. (but note
that it is definitively possible to have an record as attribute!)
...while I must admit that I'm a bit doubtful about that
language feature, but it does come in handy when manually
writing diff messages. The reason is the automatic naming
of child objects, which makes it often hard to refer to
a child after the fact, since the name can not be
reconstructed systematically.
Obviously the downside of this "anonymous pick / delete"
is that we allow to pick (accept) or even delete just
any child, which happens to sit there, without being
able to detect a synchronisation mismatch between
sender and receiver.
i.e. flat match, not deep equality.
This allows to send just an Ref (with the ID) over the
wire to refer to an complete object to be picked, moved
or deleted on the receiver side.
in the first version, I defined equality to just compare the IDs
But that didn't seem right, or what one would expect by the concept
of equality (this is a long standing discussion with persistent
object-relationally mapped data).
So I changed the semantics of equaility to be "deep".
As this means possiblty to visit a whole tree depth-first,
it seems reasonable to provide the shallow "identity-comparison" likewise.
And the most reaonable choice is to use the "matches(object)" API
for that, since, in case of objects, the matches was defined
as full equality, which now seems redundant.
Thus: from now on: obj.matches(otherObj)
means they share the same IDs
The Ref-GenNode is just a specifically constructed GenNode,
and intended to be sliced down to an ordinary GenNode
immediately after construction. It seems, GCC didn't "get that"
and instead emitted an recursive invocation of the same ctor,
which obviously leads to stack overflow.
Problem solved by explicitly coding the copy initialisation,
after the full definition of Ref is available.
the type is the only meta attribute supported by now,
thus the decision was to handle this manually, instead of
introducing a full scope for meta attributes. Unfortunately
this leads to an assymetry: while it is possible to send an
attribute named "type", which will be intercepted and used
as a new type ID, the type will not show up when iterating
or searching through attributes.
When applying a diff, the only possibility is to *insert*
a new type attribute, and we need to check and handle this
likewise manually.
It is difficult to reconcile our general architecture for the
linearised diff representation with the processing of recursive,
tree-like data structures. The natural and most clean way to
deal with trees is to use recursion, i.e. the processor stack.
But in our case, this means we'd have to peek into the next
token of the language and then forward the diff iterator
into a recursive call on the nested scope. Essentially, this
breaks the separation between receiving a token sequence and
interpretation for a concrete target data structure.
For this reason, it is preferrable to make the stack an
internal state of the concrete interpreter. The downside of
this approach is the quite confusing data storage management;
we try to make the role of the storage elements a bit more
clear through descriptive accessor functions.
implement the list handling primitives analogous to the
implementation of list-diff-applicator -- just again with
the additional twist to keep the attribute and child scopes
separated.
...so now the stage is set. We can reimplement
the handling of the list diff cases here in the context
of tree diff application. The additional twist of course
being the distinction between attribute and child scope
each language token of our "linearised diff representation"
carries a payload data element, which typically is the piece
of data to be altered (added, mutated, etc).
Basically, these elements have value semantics and are
"sent over wire", and thus it seems natural when the
language interpreter functions accept that piece of payload
by-value. But since we're now sending GenNode elements as
parameter data in our diff, which typically are of the
size of 10 data elements (640 bit on a 64bit machine),
it seems more resonable to pass these argument elements
by const& through the interpreter function. This still
means we can (and will indeed) copy the mutated data
values when applying the diff, but we're able to
relay the data more efficiently to the point where
it's consumed.
this boils down to the two alternatives
- manipulate the target data structure
- build an altered copy
since our goal is to handle large tree structures efficiently,
the decision was cast in favour of data manipulation
so basically it's time to explicate the way
our diff language will actually be written.
Similar to the list diff case, it's a linear sequence
of verb tokens, but in this case, the payload value
in each token is a GenNode. This is the very reason
why GenNode was conceived as value object with an
opaque DataCap payload
while it's still not really clear how we'll use this helper
and if we need it at all -- some weeks ago I changed its
semantics to be strictly based on the delta to a reference level.
Now this means, we could go below level zero, but this doesn't
make any sense in the context of navigating a tree. Actually,
our test case triggered this situation, which caused the
reference level to wrap around, since it is stored in an
unsigned variable.
Thus I'll add a precondition to keep the level positive,
and I'll change the test to comply.
Initially I've deliberately omitted those, to nudge towards
using time quantisation and TCode formatting for any external
representation of time values.
While this recommendation is still valid, the overloaded
string conversion turns out to be helpful for unit testing
and diagnostics in compound data structures.
See Record<GenNode>
initially the intention was to include a "bracketing construct"
into the values returned by the iterator. After considering
the various implementation and representation approaches,
it seems more appropriate just to expose a measure for the
depth-in-tree through the iterator itself, leaving any concerns
about navigation and structure reconstruction to the usage site.
As rationale we consider the full tree reconstruction as a very
specialised use case, and as such the normal "just iteration" usage
should not pay for this in terms of iterator size and implementation
complexity. Once a "level" measure is exposed, the usage site
can do precisely the same, with the help of the
HierarchyOrientationIndicator.
Whooa!
Templates are powerful.
programming this way is really fun.
under the assumption that the parts are logical,
all conceivable combinations of theses parts are bound to be correct
it passes compilation, but the test still fails, since
I've changed the expected semantics of the iteration,
in the light of the insights I've gained during
re-investigation of the IterExplorer.
What I now actually intend is rather to embed a
HierarchyOrientationIndicator into the iterator,
instead of returning a special "bracket" marker
reference to indicate return from a nested scope.
Only a Record payload constitutes a nested scope.
For all other (primitive) values, we return an empty iterator.
When used within ScopeExplorer, this implementation will just
lead to exposing any simple value once, while delving into
iteration of nested scopes
The only substantial change (besides compilation fixes) is
to confine the iteration to *const access*
This is a good thing; the whole Record/GenNode structure
was designed to represent immutable data, necessitating
a dedicated *Mutator* for any reshaping.
seemingly the operator-> was not yet used in any real scenario.
The whole point with IterAdapter is that it uses an opaque "location type",
which is owned by the controlling container. In many cases this will
actually be just a pointer into the container storage, but we
must not assume it is this way. Thus the only way to obtain a
(language) pointer is to dereference the "position type" and
take the address of the result
Initially I intended just to supply an addapter to use
the monadic IterExplorer for this recursive expansion
of GenNode contents. Investigating this approach was
relevant to highlight the minimum requirements for
such an evaluation mechanics: since our GenNode
is an hierarchical structure without back-links,
we are bound to use a stack at some point. And
since an Iterator is a materialised continuation,
we can not use the processor stack and are forced
to represent this stack in memory.
Yet, on second thought, we do not need the full power
of the IterExplorer monad; especially we do not need
to bind arbitrary functions into the monad, just one
single scope exploring function, implemented as
Variant visitor. Based on these observations, we can
"inline" the monad structure into a double nested
iterator, where the outer capsule carries a stack
of scopes to be explored.
This helper was drafted for the Job / JobPlanning and Scheduler
interface in 2013, but seemingly not yet put into action. While
in the original use case, we have a genuine measuerment for the
tree depth (given by the depth of the processing stack), in other
use cases we want to use to offset embedded within the indicator
itself for keeping track of the depth. Thus I add a second
mark operation, which usess the current offset to set a new
reference level. This has the consequence that the offset
has now to reflect the new reference point immediately
Since C++ is not a real functional programming language and
has unsafe unmanaged pointers, it is not difficult to produce
dangling references within an extended evaluation pipeline
involving transient objects and pass-by-reference.
In the initial implementation, I built in a safeguard copy
into the signature of the Explorer function, to make sure even
a transiently dressed-up input value gets materialised before
proceeding with the source sequence. Unfortunately this safeguard
turns out as a roadblock now; we might as well take the input
by reference and return an "expanded" state by value. We might
even want to do the full "expansion" on referred state, when
we're able to ensure the source values remain in memory
until consumption.
Thus now the full power of decision is placed on the signature
of the explorer function. The expansion strategies of IterExplorer
will no longer attempt to "sanitise" the signature of the passed-in
function to prevent desaster; I've added some warnings into the
documentation to highlight that danger. Basically, if you want
to be clever, then you're bound to read and understand inticacies
of the implementation.
If in doubt, use values and copying. C++ is optimised for that.
allow to pick the bare function signature from any "function like" entity
Note: we're still unsing our own Typelist construct to represent
the function argument types. Since we're now using C++11, this
could be replaced by varargs, and we could get rid of those
various overloads for different numbers of arguments.
Unfortunately this transition is linked to the usage of
argument tuples in our command framework (which could then be
replaced by std::tuple), and this is still a bit of rather
technical work, which I do not want to spend much time on
right now
remembered that some years ago I had to deal with a very similar problem
for planning the frame rendering jobs. It turned out, that the
iterator monad developed for this looks promising for our task at hand
horay!
seems like madness?
well -- found and squashed a bug: equality on RecordRef
implicitly converted to GenNode(RecordRef), which always
generates new (distinct) IDs and so never succeeds. What
we really want is equality test on the references
contrary to the Visitor, accepting a Predicate is const,
and -- of course -- the Predicate invocation returns bool.
This can be used to implement comparison operators or
search functions on Variant based data structures.
There is no generic implementation for these functions, since
they are highly dependent on the payload used within Record<TY>
Here we use Record<GenNode>, which turns the whole setup into an
recursive data type; we especially rely on the fact that each
GenNode has an embedded symbolic ID, and we use this ID to encode
the 'key' for named attributes
while in debugging, it turned out that the short type-prefix
was implemented in a too simplistic way; it fails on stuff
like 'lib::diff::Record<lib::diff::GenNode>'
while I must add, that the whole purpose of these ID functions
is somewhat unclear and needs to reveal itself as we move forward
initially my intention was to use the ID for equality test.
But on a second thought, this seemed like a bad idea, since
it confuses the concepts of equality and identity.
Note: at the moment, I do not know if we even need an equality test,
so it is provided here rather for sake of completeness. And this
means even more that we want an 'equality' implementation that
does what one would naively expect: compare the object identity
*and* compare the contents.
...while on the train back from FrOSCon.
still the same old problem: we need a better hash function
for generating our Entry-IDs. The default hash function from Boost performs
poor on strings with common prefix and trailing number.
We use a hackish workaround, which is sufficient to avoid collisions
among the first 10000 numbers.
not entirely sure about the design, but lets try this approach:
they can be "cloned" and likewise move-assigned, but we do not
allow the regular assignment, because this would enable to use
references like pointers (what we deliberately do not want)
especially setting (changing) attributes turned out to be tricky,
since in case of a GenNode this would mean to re-bind the hash ID;
we can not possibly do that properly without knowing the type of the payload,
and by design this payload type is opaque (erased).
As resort, I changed the semantics of the assign operation:
now it rather builds a new payload element, with a given initialiser.
In case of the strings, this ends up being the same operation,
while in case of GenNode, this is now something entirely different:
we can now build a new GenNode "in place" of the old one, and both
will have the same symbolic ID (attribute key). Incidentally,
our Variant implementation will reject such a re-building operatinon
when this means to change the (opaque) payload type.
in addition, I created a new API function on the Mutator,
allowing to move-in a complete attribute object. Actually this
new function became the working implementation. This way, it is
still possible to emplace a new attribute efficiently (consider
this to be a whole object graph!). But only, if the key (ID)
embedded in the attribute object is already what is the intended
key for this attribute. This way, we elegantly circumvent the
problem of having to re-bind a hash ID without knowing the type seed
initially, the intention was to inject the type as a magic attribute.
But this turned out to make the implementation brittle, asymmetric
and either quite demanding, or inefficient.
The only sane approach would be to introduce a third collection,
the metadata attributes. Then it would be possible to handle these
automatically, but expose them through the iterator.
In the end I decided against it, just the type attribute
allone does not justify that effort. So now the type is an
special magic field and kept apart from any object data.
this solves the problem how to deal with value access
- for the simple default (string) implementation,
we use a 'key = val' syntax and thus have to split strings,
which means we need to return contents by value
- for the actual relevant use case we have GenNode entries,
which may recursively hold further Records. For dealing
with diff messages over this data struture, its a good
idea to allow for const& value access (otherwise we'd
end up copying large subtrees for trivial operaions)
OMG, what was all this about?
OK... this cant possibly work this way.
At least we need to trim after splitting the attributes.
But this is not enough, we want the value, which implies
to make the type flexible (since we cant return a const& to
a substring extracted on-the-fly)
this was an half hearted attempt to satisfy CLang,
but GCC as keen as a razor insists on these inherited
functions not being accessible --
seems like the time is over, when GCC used to be forgiving
and CLang briliantly precise...
So the conclusion of this "round trip" is: whenever GCC
also starts whining about shadowed overloaded virtual functions,
we'll just switch to "-Wno-overloaded-virtual" and be done with
that pointless discussion.
Since C++11, we have the Java style override specifier,
which does a way better job at spotting signature mismatches
Note: not fixing all relevant warnings.
Especially, the "-Woverloaded-virtual" of Clang defeats the whole purpose
of generated generic interfaces. For example, our Variant type is instantiated
with a list of types the variant can hold. Through metaprogramming, this
instantiation generates also an embedded Visitor interface, which has
virtual 'handle(TY)' functions for all the types in question
The client now may implement, or even partially implement this Visitor,
to retrieve specific data out of given Variant instance with unknown conent.
To complain that some other virtual overload is now shaddowed is besides the point,
so we might consider to disable this warning altogether
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=56402
The lambda definition captures the this pointer,
but the ctor of the lamda does not initialise this capture.
In our case, we're lucky, as we don't use the "this" pointer;
otherwise, we'd get a crash a runtime.
Fixed since GCC-4.7.3 --> it's *really* time to upgrade to Debian/Jessie
the object VTable is typically emitted when the compiler
encounters the first non-static non-inline function of
the class or a derived class.
Sometimes this happens within the wrong library and so
the compiler needs a nudge to emit those infrastructure functions.
But in most cases this works out of the box and need no further
magic incanctations, which might have a downside.
Especially because also a non-inline dtor does incur a call overhead,
whereas an inline dtor can be trivially elided.
after sleeping a night over this, it seems obvios
that we do not want to start the build proces "implicitly",
starting from a Record<GenNode>. Rather, we always want
the user to plant a dedicated Mutator object, which then
can remain noncopyable and is passed by reference through
the whole builder chain. Movin innards of *this object*
are moved away a the end of the chain does not pose much risk.