diff --git a/src/lib/diff/gen-node.hpp b/src/lib/diff/gen-node.hpp index 566469d5c..88d00e15e 100644 --- a/src/lib/diff/gen-node.hpp +++ b/src/lib/diff/gen-node.hpp @@ -193,6 +193,9 @@ namespace diff{ template X const& get() const; + /** peek into the type field of a nested `Record` */ + string + recordType() const; /** visit _children_ of a nested `Record` */ Rec::scopeIter @@ -689,7 +692,30 @@ namespace diff{ return Variant::get(); } - + + /** + * @return either the contents of a nested record's type field + * or the Rec::TYPE_NIL marker. + * @remarks this function never raises an error, even if the element + * in fact doesn't constitute a nested scope. Effectively this + * allows to "peek" into the contents to some degree. + */ + inline string + DataCap::recordType() const + { + Rec* nested = unConst(this)->maybeGet(); + if (!nested) + { + RecRef* ref = unConst(this)->maybeGet(); + if (ref and not ref->empty()) + nested = ref->get(); + } + + return nested? nested->getType() + : Rec::TYPE_NIL; + } + + /** diff --git a/tests/library/diff/tree-manipulation-binding-test.cpp b/tests/library/diff/tree-manipulation-binding-test.cpp index 281a95b23..196c57319 100644 --- a/tests/library/diff/tree-manipulation-binding-test.cpp +++ b/tests/library/diff/tree-manipulation-binding-test.cpp @@ -71,7 +71,7 @@ namespace test{ CHILD_A("a"), // unnamed string child node CHILD_B('b'), // unnamed char child node CHILD_T(Time(12,34,56,78)), // unnamed time value child - SUB_NODE = MakeRec().genNode(), // empty anonymous node used to open a sub scope + SUB_NODE = MakeRec().type("ω").genNode(),// empty anonymous node used to open a sub scope ATTRIB_NODE = MakeRec().genNode("δ"), // empty named node to be attached as attribute δ CHILD_NODE = SUB_NODE, // yet another child node, same ID as SUB_NODE (!) GAMMA_PI("γ", 3.14159265); // happens to have the same identity (ID) as ATTRIB3AS diff --git a/wiki/renderengine.html b/wiki/renderengine.html index b8d2e2253..f57178ea0 100644 --- a/wiki/renderengine.html +++ b/wiki/renderengine.html @@ -2316,7 +2316,7 @@ For this Lumiera design, we could consider making GOP just another raw media dat →see in [[Wikipedia|http://en.wikipedia.org/wiki/Group_of_pictures]] -
+
//Abstract generic node element to build a ~DOM-like rendering of Lumiera's [[session model|HighLevelModel]].//
 GenNode elements are values, yet behave polymorphic. They are rather light-weight, have an well established identity and can be compared. They are //generic// insofar they encompass several heterogeneous ordering systems, which in themselves can not be subsumed into a single ordering hierarchy. The //purpose// of these generic nodes is to build a symbolic representation, known as [[external tree description|ExternalTreeDescription]], existing somewhere "outside", at a level where the fine points of ordering system relations do not really matter. Largely, this external description is not represented or layed down as a whole. Rather, it is used as a conceptual reference frame to describe //differences and changes.// Obviously, this means that parts of the altered structures have to appear in the description of the modifications. So, practically speaking, the prime purpose of GenNode elements is to appear as bits of information within a ''diff language'' to exchange such information of changes.
 
@@ -2324,12 +2324,27 @@ To be more specific, within the actual model there are [[Placements|Placement]].
 A generic node may //represent any of these kind// -- and it may have ~GenNode children, forming a tree. Effectively all of this together makes ~GenNode a ''Monad''.
 
 GenNode elements are conceived as values, and can thus be treated as mutable or immutable; it is up to the user to express this intent through const correctness.
-Especially the nested structures, i.e. a GenNode holding an embedded {{{diff::Record}}}, are by default immutable, but expose a object builder API for remoulding. This again places the actual decision about mutations into the usage context, since the remoulded Record has to be assigned explicitly.
+Especially the nested structures, i.e. a GenNode holding an embedded {{{diff::Record}}}, are by default immutable, but expose a object builder API for remoulding. This again places the actual decision about mutations into the usage context, since the remoulded Record has to be assigned explicitly. In fact, this is an underlying theme of the whole design laid out here: Data represented as GenNode graph is //structured, yet opaque.// It is always tied into an usage context, that "happens to know" what to expect. Moreover, the values to be integrated into such a structure are of limited type -- covering just the basic kinds of data plus a recursive structuring device:
+* integral values: {{{int}}}, {{{int64_t}}}, {{{short}}}, {{{char}}}
+* logic values: {{{bool}}}
+* measurement data: {{{double}}}
+* textual data: {{{std::string}}}
+* time representation: {{{time::Time}}}, {{{time::Duration}}}, {{{time::TimeSpan}}}
+* identity and reference: {{{hash::LuidH}}}
+* object-like structure:  {{{diff::Record<GenNode>}}}
+* for cross-links and performance considerations, we also provide a {{{Record}}}-//reference element.//
+
 
 
 !to reflect or not reflect
 When dealing with this external model representation, indeed there are some rather global concerns which lend themselves to a generic programming style. Simply because, otherwise, we'd end up explicating and thereby duplicating the structure of the model all over the code. Frequently, such a situation is quoted as the reason to demand introspection facilities on any data structure. We doubt this is a valid conclusion. Since introspection allows to accept just //any element// -- followed by an open-ended //reaction on the received type// -- we might arrive at the impression that our code reaches a maximum of flexibility and "openness". Unfortunately, this turns out to be a self-deception, since code to do any meaningful operation needs pre-established knowledge about the meaning of the data to be processed. More so, when, as in any hierarchical data organisation, the relevant meaning is attached to the structure itself, so consequently this pre-established knowledge tends to be scattered over several, superficially "open" handler functions. What looks open and flexible at first sight is in fact littered with obscure and scattered, non obvious additional presumptions.
-This observation from coding practice gets us to the conclusion, that we do not really want to support the full notion of data and type introspection. We //do want// some kind of passive matching on structure, where the receiver explicitly has to supply structural presuppositions. In a fully functional language with a correspondingly rich type system, a partial function (pattern match) would be the solution of choice. Under the given circumstances, we're able to emulate this pattern based on our variant visitor -- which basically calls a different virtual function for each of the types possibly to be encountered "within" a ~GenNode.
+This observation from coding practice gets us to the conclusion, that we do not really want to support the full notion of data and type introspection. We //do want// some kind of passive matching on structure, where the receiver explicitly has to supply structural presuppositions. In a fully functional language with a correspondingly rich type system, a partial function (pattern match) would be the solution of choice. Under the given circumstances, we're able to emulate this pattern based on our variant visitor -- which basically calls a different virtual function for each of the types possibly to be encountered "within" a ~GenNode. + +This is a rather challenging attitude, and in practice we're bound to allow for a bit of leeway, leave some loopholes to at least "peek" into data about to be handled: +* based on the form of a given node's ID element, we allow to distinguish //attributes and children.// +* the {{{Record<GenNode>}}}, which is used to represent object-like entities, exposes a //semantic type filed// to be used at the local scope's discretion. +* we explicitly enable receiving or handling code to "peek" into that type field, thereby silently absorbing the case when the GenNode in question in fact does not hold a Record. +
Each [[Timeline]] has an associated set of global [[pipes|Pipe]] (global busses), similar to the subgroups of a sound mixing desk.
@@ -8200,8 +8215,7 @@ i.e. a ''unified diff'' or the ''predicate notation'' used above to describe the
 
-
-The TreeMutator is an intermediary to translate a generic structure pattern into heterogeneous local invocation sequences.
+
The TreeMutator is an intermediary to translate a generic structure pattern into heterogeneous local invocation sequences.
 within the [[diff framework|TreeDiffModel]], this is a crucial joint, since here the abstract, generic, ~DOM-like ExternalTreeDescription meeds opaque, local and undisclosed data structures.
 
 &rarr; see TreeMutatorEvolution for an extensive discussion about the principles and architecture, which gradually leads to the final design of the ~TreeMutator, as realised now in code.