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>
This basically finishes definition of the fundamental
UI-Element and Bus protocol -- with one notable exception:
how to mutate elements by diff.
This will be the next topic to address
not really sure about its usefullness, but it seems
low hanging fruit for me right now (while I am still
aware of all details how the UI-Bus works).
This might possibly be helpful to broadcast "reset" messages....
NOTE: we don't have any "real" UI-Element implementation yet.
Such would have to define its own, private error and message handling.
It is likely that we'll end up with some kind of base implementation
within model::Element and model::Controller.
Anyhow, this is future work
basic state capturing, storage and replay now works as intended
More elaborate state management will be implemented later,
when we know more about perspectives and work sites!
- suppres sending redundant stat mark messages from MockElm
- emit a "reset" state mark when an actual reset happens
- let the PresentationStateManager discard recorded special state
when receiving a "reset" mark for a given element
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. ;-)
...and I made the decision *not* to consider any kind of
generic properties for now. YAGNI.
UI coding is notorious spaghetti code.
No point in fighting that, it is just the way it is,
because somewhere you're bound to get concrete, hands-on.
...everything working out of the box thus far,
which is remarkable, since I didn't write a single
line of implementation code beyond what's available
as basic bus functionality. So this one just
fell into place
right now, what we actually need here is just some integer,
so the GenNode payload is typed to int (or just to anything
different than a Record, because the Record signals that
we intend to bind, not to invoke the command)
the values.child() call would also do a bounds check,
but only to rise a error::Invalid "index out of bounds".
So now we generate a clear message to indicate that
actually a runtime-checked type mismatch caused this problem
the functionality as such is already covered,
but it seems important enough to warrant a dedicated test.
incidentally, Duration still lacked a default ctor.
Time values are default constructible, yet immutable.
incidentally, this uncovered yet another unwanted narrowing conversion,
namely from double via gavl_time_t to TimeValue or alternatively
from double via FSecs (= rational<long>) to Duration.
As in all the previos cases, actually the compiler is to blame,
and GCC-5 is known to get that one right, i.e. let the SFINAE fail
instead of passing it with a "narrowing conversion" warning.
Note: the real test for command binding with immutable types
can be found in BusTerm_test
Completely removed the nested hierarchy, where
the top-level implementation forwarded to yet another
sub-implementation of the same interface. Rather, this
sub-implementation (OpClosure) is now a mere implementation
detail class without VTable, and without half-baked
re-implementation of the CmdClosure interface. And the
state-switch from unbound to bound arguments is now
implemented as a plain-flat boolean flag, instead of
hiding it in the VTable.
To make this possible, without having to rewrite lots of
tests, I've created a clone of StorageHolder as a
"proof-of-concept" dummy implementation, for the sole
purpose of writing test fixtures. This one behaves
similar to the real-world thing, but cares only
for closing the command operation and omits all
the gory details of memento capturing and undo.
Seems this was part of the confusion when looking at
the inheritance graph: Names where almost reversed
to the meaning. the ArgumentHolder was *not* the
argument holder, but the top level closure. And
the class "Closure" was not "the" Closure, but
just the argument holder. ;-)
still TODO: the ability to use immutable types
within the command framework. In theory, this
shouldn't be had to implement, since we're creating
a new opaque value holder within the command registry
anyway, so it should be sufficient to refrain from
re-assigning a new value tuple. This is relevant,
since e.g. our time framework is built on immutable
value types.
...when the Test-Nexus processes a command binding message.
In the real system of course we do not want to log every bind message.
The challenge here is the fact that command binding as such
is opaque, and the types of the data within the bind message
are opaque as well. Finally I settled on the compromise
to log them as strings, but only the DataCap part;
most value types applicable within GenNode
have a string representation to match.
the rationale is that I deliberately do not want to provide
a mechanism to iterate "over all contents in stringified form".
Because this could be seen as an invitation to process GenNode-
datastructures in an imperative way. Please recall we do not
want that. Users shall either *match* contents (using a visitor),
or they are required to know the type of the contents beforehand.
Both cases favour structural and type based programming over
dynamic run-time based inspection of contents
The actual task prompting me to add this iteration mechanism
is that I want to build a diagnostic, which allows to verify
that a binding message was sent over the bus with some
specific parameter values.
...also for the existing variant, which packages an
arbitrary number of arguments in stringified form
into a given container type. Moreover, the new
form of stringify allows to write util::join
in a clearer way, eliminating the lambda.
very similar to boost::irange, but without heavyweight boost
includes, and moreover based on our Lumiera Forward Iterator concept
Such a inline-range construct makes writing simple tests easy
based on the new generic tuple builder, we're now able to
add a new binding function into the command implementation
machinery, alongside the existing one. As it stands, the
latter will be used rather by unit tests, while the new
access path is what will be actually taken within
the application, when receiving argument binding
messages dispatched via the UI-Bus.
without that check, in theory our test runner will tolerate
a non-zero return value, like throwing or failing an assert,
which is not what we want....
guess these happenend to get in by forgetting to
add this check when switching a test from PLANNED to TEST
this was a classical example of a muddled and messed-up design,
driven just by the fact that I wanted to "spare" some functions,
with the net effect of writing more functions, plus a proxy class
plus create a lot of confusion for the reader.
This was easy to resolve though, once I resorted to the
general adivice to make public interface methods final,
make the extension ponts protected and never
to chain two extension points
based on the previous experiments, this adds a fake operation
and a definition frame to hook this operation as pseudo Proc-Layer command
WIP: the invocation itself is not yet implemented.
We need to build a custom invocation pattern for that,
in order to be able to capture the instance-ID of the command
on invocation
NOTE: also, because of #989, we can not bind a time value for this test
we made double use of our Tuple type, not only as a
generic record, but also as a metaprogramming helper.
This changeset replaces these helpers with other
metafunctions available for our typelists or type sequences
(with the exception of code directly related to Tuple itself,
since the intention is to delete this code alltogether shortly)
basically this comes down to provide some convenience fixture
within the test::Nexus, which automatically generates and wires
mock commands.
Not sure if this is even possible to the extent envisioned here
since our test.sh runner can be used to verify the
expected output printed by tests, working with these
output transcripts of larger tests can be hard at times.
These separators help to find who produced which output
and they prevent a regexp match to grep beyond the feed
of a single function (which can be a common problem
when using the self-diagnostic output of the facility
currently in test, which obviously will be similar
on any data printed.
First part is to define the steps (the protocol) at the
model element level, which gets a command prepared and invoked.
Test fails still, because there is no actual argument binding
invoked in the TestNexus
we deleted an object on the heap,
and afterwards re-accessed the memory through the
dangling pointer to verify the deletion actually happened.
This works most of the time, unless the memory manager decides
to map that page differently -- in which case we just hit
random memory contents.
A better idea is thus to place this TestFrame object
into a statically allocated buffer and invoke the dtor
explicitly. This allows us to conduct the test reliably.
- replace remaining usages of typeid(T).name()
- add another type simplification to handle the STL map allocator
- clean-up usage in lib/format-string
- complete the unit tests
- fix some more bugs
This clean-up action for Ticket #985 started out as search
for a lightweight generic solution. What is left from this
search now, after including the actual utility code into
our support library, might serve to document this new
feature for later referral
over time, we got quite a jungle with all those
shome-me-the-type-of helper functions.
Reduced and unified all those into
- typeString : a human readable, slightly simplified full type
- typeSymbol : a single word identifier, extracted lexically from the type
note: this changeset causes a lot of tests to break,
since we're using unmangeled type-IDs pretty much everywhere now.
Beore fixing those, I'll have to implement a better simplification
scheme for the "human readable" type names....
due to the new automatic string conversion in operator<<
the representation of objects has changed occasionally.
I've investigated and verified all those incidents.
...other than intended, the bomb did explode on random occasions,
with an probability of about 4% (when rr >= 96).
Btw, there was also the mistake to throw an heap allocated
object by pointer. Damn Java habits.
- remove unnecessary includes
- expunge all remaining usages of boost::format
- able to leave out the expliti string(elm) in output
- drop various operator<<, since we're now picking up
custom string conversions automatically
- delete diagnostics headers, which are now largely superfluous
- use newer helper functions occasionally
I didn't blindly change any usage of <iostream> though;
sometimes, just using the output streams right away
seems adequate.
- simple function to pick up the mangled type
- pretty-printing is implemented in format-obj.cpp
- also move the demangleCxx()-Function to that location,
it starts to be used for real, outside the test framework
our minimal compiler requirement is gcc-4.9 since the
transition to Debian/Jessie as reference system.
gcc-4.9 is known to treat SFINAE on private fields properly
this is a stripped-down and very leightweight variant
of the well-known enable_if metaprogramming trick.
Providing this standard variant in a header with minimal
dependencies will allow us to phase out boost inclusions
from many further headers. As a plus, our own variant
is written such as to be more conciese in usage
(no "typename" and no acces of an embedded "::type" menber)
...and learned a lot about the new type_traits on the way.
As it seems, it is not possible to get a clean error message
when passing an "object" with no custom string conversion;
instead, some overload for an rvalue-ostream kicks in.
probably I'll go for shoing a type string in these cases
now we use boost::format through our own front-end util::_Fmt
solely, which both helps to reduce compilation time and code size,
and gives us a direct string conversion, which automatically
uses any custom operator string() available on arguments.
While desirable as such, I did this conversion now, since
it allows us to get rid of boost::str, which in turn helps
to drill down any remaning uses of our own util::str
...this is necessary whenever the mocked facility covered
by log matching is managed automatically as singleton,
because then other test cases will leave garbage
in the log
this test is intended as counterpart to
AbstractTangible_test::verify_mockManipulation()
It creates a mock element and verifies bidirectional
connnectivity to the UI-Bus
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
the "log joining" functionality was already implemented
and covered with the generic event log facility, but this test
here was drafted even before that, meaning that the semantics
of matchingn on the log, especially on events, as been
implemented slightly different than planned
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
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
I think it is a shame to waste the nice name "nexus"
just for a test facility; rather I've named our central
routing hub in the UI-Bus gui::ctrl::Nexus
So it makes sense to name the fake for unit testing
the test-nexus (we're not at nexus 5 yet)
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
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
...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
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