TODO: might break some unit-tests...
Explanation: our wrapper around boost::format has special
built-in support for custom operator string(). Any type,
which is neiter standard, or printable through such a
custom string conversion, is represented as a type-string.
For this fallback case, we now use our recently added
demangling call (which actually relies on a rather obscure
but standard compiler API)
still passes compilation, but not actually tested.
The visitor-style accees needs to be implemented, and the
whole virtual copy support mechanism extracted into a separate
header and covered by unit test
now the solution with the copy policy class is in place,
I prefer to return to the more verbose yet clearer notion
of distinct constructors for each case on the outer and
the inner capsule likewise.
The idea with the separate builder class would be significant
only if this class would also provide the copy support. This
turns out to be difficult, due to the access restrictions
and the necessary passing of type parameters.
turns out to be quite a tough challenge....
since obviously we want to support usage of types with
partially disabled copy/assignment operations within Variant.
As long as the corresponding operations on the container aren't
invoked, we expect those types to be usable just fine.
The problem arises at the interaction with type erasure;
to support corret copy / assignement in such a situation, we need
virtual copy / assignment operators. And, since these are to be installed
into a VTable, the templated functions will be instantiated allways,
which might cause invocation of inhibited copy / assignement functions
and thus compilation failure, in spite of never actually invoking such
an illegal operation.
The drafted solution is to mix in a specifically configured copy support policy,
which at least raises a runtime error, instead of invoking the incriminating operation(s)
finally got all those copy / assgnment flavours straight.
Still unsolved: unable to instantiate the Variant template
for a type with private assignment operator (like e.g. Time )
The problem is our virtual assignement operator, which forces
instantiation of the implementation (for the VTable), even if
the actual assignment is never invoked.
this was an immature first desgin attempt; we need a lightweight
Variant (typesafe union) implementation, so now is the time for
a second attempt. The existing Variant is used only once, and this
usage as such is in a questionable context, likely to be reworked
when we actually start coding up the builder. So I'll just move
it away and mark it @deprecated for the time being.
After some reconsideration, I decide to stick to the approach with the closures,
but to use a metaprotramming technique to build an inheritance chain.
While I can not decide on the real world impact of storing all those closures,
in theory this approach should enable the compiler to remove all of the
storage overhead. Since, when storing the result into an auto variable
right within scope (as demonstrated in the test), the compiler
sees the concrete type and might be able to boil down the actual
generated virtual function implementations, thereby inlining the
given closures.
Whereas, on the other hand, if we'd go the obvious conventional route
and place the closures into a Map allocated on the stack, I wouldn't
expect the compiler to do data flow analysis to prove this allocation
is not necessary and inline it away.
NOTE: there is now guarantee this inlining trick will ever work.
And, moreover, we don't know anything regarding the runtime effect.
The whole picture is way more involved as it might seem at first sight.
Even if we go the completely conventional route and require every
participating object to supply an implementation of some kind of
"Serializable" interface, we'll end up with a (hand written!)
implementation class for each participating setup, which takes
up space in the code segment of the executable. While the closure
based approach chosen here, consumes data segment (or heap) space
per instance for the functors (or function pointers) representing
the closures, plus code segment space for the closures, but the
latter with a way higher potential for inlining, since the closure
code and the generated virtual functions are necessarily emitted
within the same compilation unit and within a local (inline, not
publickly exposed) scope.
so yes, it is complicated, and inevitably involves three layers
of indirection. The alternative seems to bind the GUI direcly to
the Session interface -- is there a middle gound?
For the messages from GUI to Proc, we have our commands, based
on PlacementRef entities. But for feeding model updates to the
GUI, whatever I consider, I end up either with diff messages or
an synchronised access to Session attributes, which ties the
responsiveness of the GUI to the Builder operation.
- we use a GenNode element
- this holds a polymorphic value known as DataCap
- besides simple attribute values, this may hold collections of GenNode sub elements
- a special kind of GenNode collection, the Record, is used to represent objects
The purpose of this setup is to enable an external model representation
which is only loosely coupled to the interndal data representation
through the exchange of (tree)diff messages
previously this operation was named 'attach', which an be confused
with attching an object to this location. Indeed, the session interface
even offers such an attach function. By renaming the focus moving
operation into QueryFocus::shift(Scope), this ambiguity is resolved
This is the first step towards a generic backbone to connect
any GUI elements to the session within Proc-Layer.
It is based on a spefic understanding of Model-View-Controller,
which turns the Model-Controller interactions into messages.
sans the implementation of the index lookup table(s)
The algorithm is KISS, a variant of insertion sort, i.e.
worst time quadratic, but known to perform well on small data sets.
The mere generation of the diff description is O(n log n), since
we do not verify that we can "find" out of order elements. We leave
this to the consumer of the diff, which at this point has to scan
into the rest of the data sequence (leading to quadratic complexity)
finally....
The problem is that the C++ "dependent types" defeat the typical
DSL usage, where you define some helper function in a generic
language setup class and mix this language in as superclass.
This is, C++ requires us to refer explicitly to any dependent type,
since, due to possible template specialisations, the parser
can't know if a given symbol is a inherited type or a field.
As a solution, we place the token constructor functors into a
static struct "token", which allows to write e.g. token.insert(xyz)
As decided in beb57cde
this changeset switches our basic list diff language to work
in the style of an insertion sort. Rather than 'pushing back'
out-of-order elements, we scan and bring forward missing elements.
Later, when passing the original location of the elements
fetched this way, a 'skip' verb will help to clean up
possible leftowers, so implementation is possible
(and indeed acomplished) without shifting any other elements.
and this adds a twist: conceptually, we identify the token
with the abstract handler function it represents. But C++
does not allow us to compare member pointers to virtual functions,
for good reason: even two pointers with the "same offset" into
the VTable might end up referring to different implementations,
when bound to instances of different subclasses. This is what
polymorphism is all about.
At this point it seems reasonably, albeit a bit uggly, to use the
diagnostic ID as placeholder instead, and just compare these IDs
instead. We assume that in practice tokens will be defined through
the provided helper macro, which ensures unique identifiers.
basically just a function to pick up the container and element type automatically.
The actual implementation is delegated to the exisiting lib::iter_stl::IterSnapshot
Heureka! found out that the C++ standard library exposes a
cross vendor C++ ABI, which amongst others allows to show
object code names and type-IDs in the language-level, human
readable unmangeld form.
Of course, actual application code should not rely on such a
internal representation, yet it is of tremendous help when
writing and debugging unit tests.
Signed-off-by: Ichthyostega <prg@ichthyostega.de>
Actually I arried at the conclusion, that the *receiving* of
a diff representation is actually a typical double-dispatch situation.
This leads to the attempt to come up with a specialised visitor
as standard pattern to handle and apply a diff. Obviously,
we do not want the classical GoF-Visitor, but (yes, we had
that discussion allready) -- well in terms of runtime cost,
we have to deal with at least two indirections anyway;
so now I'm exploring the idea to implement one of these
indirections through a functor object, which at the same time
acts as "Tag" in the diff representation language (instead
of using an enum as tag)
initial considerations; there is a concurrency problem, since
all of session handling within Proc is deliberately not threadsafe.
Thus the decision is to make this the gui::model::SessionFacade's responsibility
The actual problem is not resolved; the pluginloader
should detect the duplicate and not add the handle
to the database initially. Or it should add it
as "duplicate" or "alternate implementation"
Which probably means we need to coder some additional
corner cases. But certainly not now, we have other
more important stuff to do first... we've already
lost the battle against Duke Nukem Forever :-P
Mark parts of the timeline state handling which will certainly
not be retained: any part where the GUI widgets "hold" some kind
of model. GUI widgets shall be *mapped upon* a model representation
and *wired* with callbacks.
Especially I am suspicious when GUI presentation code "reaches into"
any kind of model data structure to find out something. It should
be the other way round (dont call us, we call you)
actually we should make our timeline a real custom widget,
and do it according to the letter. I.e. really implement
all those callbacks which are recommended, but no other
callbacks.
This has the additional benefit of being able to retrieve
the drawing style in the official way, and define our own
CSS classes, which can be styled by the user in a systematic way.
This is not really a solution, but kind of narrowes down the problem.
Our GUI uses an obsolete C-ish approach at releasing resources at
several points. This is probably a left-over from earlier days.
Especially since we started out with libGDL without C++ wrappers.
And at that time, we didn't use smart-pointers, as we should do,
but we tried to do things manually, which is an approach which never
works in an event driven and condition based environment. Goto fail.
Here I just commented out the manual clean-up code from several dtors.
The real solution would be not to allocate these resources through
the raw C calls at first place, but rather use the mm-wrappers
and leave it to them to unwind at the right moment.
TODO:
- scan the GUI code for *every* instance where we still muck around with gobjects
and either replace that by a mm-wrapper, or wrap it in a smart handle.
- make sure that *all* dtors are either empty, or really airtight and EX_FREE
doh...
this happens when you draft some quite intricate logic and then
get sidelined with other tasks for several years. Mind me, I didn't
even recall that I had treated this whole issue and created
a clean-up thread.
A full fledged implementation would have a real lifecycle and
thus detect the re-entrance; but since none of the components
to be managed by the OutputDirector is even remotely planned
or even coded, the functions were just drafted as stubs.
Which caused us happily to create yet another clean-up thread
whenever the subsystem-runner signalled "please shut down".
Our GUI shutdown logic looks rather confused. Why the hell do
some widgets "unregister" themselves in a dtor. This should never
be necessary. Maybe it's a leftover from C-style programming
and obsolete now, after the switch to GDLmm