Library: prepare for a ETD binding
Document existing data binding logic and investigate in detail what must be done to enable a similar binding backed by Lumiera's ETD structures. This analysis highlights some tricky aspects, which can be accommodated by slight adjustments and generalisations in the `TextTemplate` implementation * `GenNode` is not structured string data, rather binary data * thus exposing a std::string_view is not adequate, requiring to pick up the result type from the actual data binding * moreover, to allow for arbitrary nested scopes, a back-pointer to the parent scope must be maintained, which requires stable memory locations. This can best be solved within the InstanceCore itself, which manages the actual hierarchy of data source references. * the existing code happens already to fulfil this requirement, but for sake of clarity, handling of such a nested scope is now extracted into a dedicated operation, to highlight the guaranteed memory layout.
This commit is contained in:
parent
c0439b265c
commit
64f60356b7
4 changed files with 332 additions and 63 deletions
|
|
@ -41,6 +41,7 @@ namespace util {
|
|||
using std::regex;
|
||||
using std::smatch;
|
||||
using std::string;
|
||||
using std::string_view;
|
||||
|
||||
|
||||
|
||||
|
|
@ -53,6 +54,10 @@ namespace util {
|
|||
RegexSearchIter (string const& toParse, regex const& regex)
|
||||
: std::sregex_iterator{toParse.begin(), toParse.end(), regex}
|
||||
{ }
|
||||
RegexSearchIter (string_view toParse, regex const& regex)
|
||||
: std::sregex_iterator{string::const_iterator{toParse.begin()}
|
||||
,string::const_iterator{toParse.end()}, regex}
|
||||
{ }
|
||||
|
||||
operator bool() const { return isValid(); }
|
||||
|
||||
|
|
|
|||
|
|
@ -71,21 +71,96 @@
|
|||
**
|
||||
** # Implementation notes
|
||||
**
|
||||
** The template specification is parsed and compiled immediately when constructing
|
||||
** the TextTemplate instance. At this point, syntactical errors, e.g. mismatched
|
||||
** conditional opening and closing tags will be detected and raised as exceptions.
|
||||
** The _compiled template_ is represented as a vector of action tokens, holding the
|
||||
** constant parts as strings in heap memory and marking the positions of placeholders
|
||||
** and block bounds.
|
||||
** The template specification is [parsed and compiled](\ref TextTemplate::parse)
|
||||
** immediately when constructing the TextTemplate instance. At this point, syntactical
|
||||
** and logical errors, e.g. mismatched conditional opening and closing tags will be
|
||||
** detected and raised as exceptions. The _compiled template_ is represented as a
|
||||
** vector of action tokens, holding the constant parts as strings in heap memory
|
||||
** and marking the positions of placeholders and block bounds. The branching
|
||||
** and looping possibly happening later, on instantiation, is prepared
|
||||
** by issuing appropriate branching and jump markers, referring
|
||||
** to other points in the sequence by index number...
|
||||
** - `TEXT` stores a text segment to be included literally
|
||||
** - `KEY` marks the placeholders, storing the key to retrieve a substitution value
|
||||
** - `COND` indicates a branching point, based on a data value retrieved by key
|
||||
** - `ITER` indicates the start of an iteration over data indicated by key
|
||||
** - `LOOP` marks the end of the iterated segment, linked back to the start
|
||||
** - `JUMP` represents an unconditional jump to the index number given
|
||||
** Whenever an _else-section_ is specified in the template, a `JUMP` is emitted
|
||||
** beforehand, while the first `TEXT` in the _else-section_ is wired as `refIDX`
|
||||
** from the starting token.
|
||||
**
|
||||
** The actual instantiation is initiated through TextTemplate::render(), which picks
|
||||
** a suitable data binding (causing a compilation failure in case not binding can
|
||||
** The actual instantiation is initiated through TextTemplate::submit(), which picks
|
||||
** a suitable data binding (causing a compilation failure in case no binding can
|
||||
** be established). This function yields an iterator, which will traverse the
|
||||
** sequence of action tokens precompiled for this template and combine them
|
||||
** with the retrieved data, yielding a std::string_view for each instantiated
|
||||
** chunk of the template. The full result can thus be generated either by
|
||||
** looping, or by invoking util::join() on the provided iterator.
|
||||
**
|
||||
**
|
||||
** ## Data Access
|
||||
**
|
||||
** ** The [instantiation processing logic](\ref TextTemplate::Action::instantiate) is
|
||||
** defined in terms of a *data binding*, represented as TextTemplate::DataSource.
|
||||
** This binding, assuming a _generic data access protocol,_ has to be supplied by
|
||||
** a concrete (partial) specialisation of the template `DataSource<DAT>`. This
|
||||
** allows to render the text template with _structured data,_ in whatever
|
||||
** actual format the data is available. Notably, bindings are pre-defined
|
||||
** for string data in a Map, and for Lumiera's »[External Tree Description]«
|
||||
** format, based on a [generic data node](\ref gen-node.hpp). Generally speaking,
|
||||
** the following _abstracted primitive operations_ are required to access data:
|
||||
** - the `DataSource<DAT>` object itself is a copyable value object, representing
|
||||
** an _abstracted reference to the data._ We can assume that it stores a `const *`
|
||||
** internally, pointing to some data entity residing elsewhere in memory.
|
||||
** - it must somehow be possible, to generate a nested sub-data context, represented
|
||||
** by the same reference data type; this implies that there is some implementation
|
||||
** mechanism in place to tap into a _nested sub-scope_ within the data.
|
||||
** - `bool dataSrc.contains(key)` checks if a binding is available for the given \a key.
|
||||
** If this function returns `false` no further access is attempted for this \a key.
|
||||
** - `string const& retrieveContent(key)` acquires a reference to string data representing
|
||||
** the _content_ bound to this \a key. This string content is assumed to remain stable
|
||||
** in memory during the instantiation process, which exposes a `std::string_view`
|
||||
** - `Iter getSequence(key)` attempts to _»open« a data sequence,_ assuming that the
|
||||
** \a key somehow links to data that can somehow be interpreted as a sequence of
|
||||
** nested sub-data-entities. The result is expected as »[Lumiera Forward Iterator]«.
|
||||
** - `DataSource<DAT> openContext(Iter)` is supplied with the \a Iter from `getSequence()`
|
||||
** and assumed to return a new data binding as `DataSource` object, tied to the nested
|
||||
** data entity or context corresponding to the current »yield« of the Iterator. This
|
||||
** implies that a `Iter it` can be advanced by `++iter` and then passed in again to
|
||||
** get the data-src (reference handle) to access the next »sub entity«, repeating
|
||||
** this procedure until the iterator is _exhausted_ (bool `false`). Moreover, it is
|
||||
** assumed, that recursive invocations of `retrieveConent(key)` on this sub-scope
|
||||
** reference will yield the _data values_ designated by \a key _for this sub-entity,
|
||||
** as well as possibly also accessing data _visible from enclosing scopes._
|
||||
**
|
||||
** \par Map Binding
|
||||
** The preconfigured binding to `std::map<string,string>` implements this protocol — relying
|
||||
** however on some trickery and conventions, since the map as such is one single „flat“ data
|
||||
** repository. The intricate part relates to iteration (which can be considered more a »proof
|
||||
** of concept« for testing). More specifically, accessing data for a _loop control key_ should
|
||||
** yield a CSV list of key prefixes. These are combined with the loop control key to form
|
||||
** a prefix for individual data values: `"<loop>.<entity>.<key>"`. When encountering a "key"
|
||||
** while in iteration, first an access is attempted with this _decoration prefix; if this
|
||||
** fails, a second attempt is made with the bare key alone. See TextTemplate_test::verify_iteration,
|
||||
** which uses a special setup, where a string of `key=value` pairs is parsed on-the-fly to populate
|
||||
** a `map<string,string>`
|
||||
**
|
||||
** \par ETD Binding
|
||||
** While the _Map Binding_ detailed in the preceding paragraph is mostly intended to handle
|
||||
** simple key substitutions, the more elaborate binding to `GenNode` data (ETD) is meant to
|
||||
** handle structural data, as encountered in the internal communication of components within
|
||||
** the Lumiera application — notably the »diff binding« used to populate the GUI with entities
|
||||
** represented in the _Session Model_ in Steam-Layer. The mapping is straight-forward, as the
|
||||
** required concepts can be supported directly
|
||||
** - Key lookup is translated into _Attribute Lookup_ — starting in the current record and
|
||||
** possibly walking up a scope path
|
||||
** - the loop key accesses a _nested Attribute_ (lib::diff::GenNode) and exposes its _children_
|
||||
** for the iteration; thus each entity is again a `Rec<GenNode>` and can be represented
|
||||
** recursively as a DataSource<Rec<GenNode>>
|
||||
** - the DataSource implementation includes an _optional parent link,_ which is consulted
|
||||
** whenever _Attribute Lookup_ in the current record does not yield a result.
|
||||
** [External Tree Description]: https://lumiera.org/documentation/design/architecture/ETD.html
|
||||
** [Lumiera Forward Iterator]: https://lumiera.org/documentation/technical/library/iterator.html
|
||||
** @todo WIP-WIP-WIP 3/2024
|
||||
** @see TextTemplate_test
|
||||
** @see gnuplot-gen.hpp
|
||||
|
|
@ -132,7 +207,7 @@ namespace lib {
|
|||
const regex ACCEPT_DATA_ELM {MATCH_DELIMITER + MATCH_DATA_TOKEN};
|
||||
|
||||
inline auto
|
||||
iterNestedKeys (string key, string const& iterDef)
|
||||
iterNestedKeys (string key, StrView const& iterDef)
|
||||
{
|
||||
return explore (util::RegexSearchIter{iterDef, ACCEPT_DATA_ELM})
|
||||
.transform ([key](smatch mat){ return key+"."+string{mat[1]}+"."; });
|
||||
|
|
@ -274,7 +349,7 @@ namespace lib {
|
|||
Idx refIDX{0};
|
||||
|
||||
template<class SRC>
|
||||
StrView instantiate (InstanceCore<SRC>&) const;
|
||||
auto instantiate (InstanceCore<SRC>&) const;
|
||||
};
|
||||
|
||||
/** the text template is compiled into a sequence of Actions */
|
||||
|
|
@ -296,25 +371,27 @@ namespace lib {
|
|||
using DataCtxIter = typename SRC::Iter;
|
||||
using NestedCtx = std::pair<DataCtxIter, SRC>;
|
||||
using CtxStack = std::stack<NestedCtx, std::vector<NestedCtx>>;
|
||||
using Value = typename SRC::Value;
|
||||
|
||||
SRC dataSrc_;
|
||||
ActionIter actionIter_;
|
||||
CtxStack ctxStack_;
|
||||
StrView rendered_;
|
||||
Value rendered_;
|
||||
|
||||
public:
|
||||
InstanceCore (ActionSeq const& actions, SRC);
|
||||
|
||||
bool checkPoint() const;
|
||||
StrView& yield() const;
|
||||
auto& yield() const;
|
||||
void iterNext();
|
||||
|
||||
StrView instantiateNext();
|
||||
StrView reInstatiate (Idx =Idx(-1));
|
||||
StrView getContent(string key);
|
||||
Value instantiateNext();
|
||||
Value reInstatiate (Idx =Idx(-1));
|
||||
Value getContent(string key);
|
||||
bool conditional (string key);
|
||||
bool openIteration (string key);
|
||||
bool loopFurther();
|
||||
void focusNested();
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -579,6 +656,10 @@ namespace lib {
|
|||
: data_{&map}
|
||||
{ }
|
||||
|
||||
|
||||
using Value = std::string_view;
|
||||
using Iter = decltype(iterNestedKeys("",""));
|
||||
|
||||
bool
|
||||
contains (string key)
|
||||
{
|
||||
|
|
@ -586,7 +667,7 @@ namespace lib {
|
|||
or util::contains (*data_, key);
|
||||
}
|
||||
|
||||
string const&
|
||||
Value
|
||||
retrieveContent (string key)
|
||||
{
|
||||
MapS::const_iterator elm;
|
||||
|
|
@ -602,9 +683,6 @@ namespace lib {
|
|||
return elm->second;
|
||||
}
|
||||
|
||||
|
||||
using Iter = decltype(iterNestedKeys("",""));
|
||||
|
||||
Iter
|
||||
getSequence (string key)
|
||||
{
|
||||
|
|
@ -684,12 +762,13 @@ namespace lib {
|
|||
* @return a string-view pointing to the effective rendered chunk corresponding to this action
|
||||
*/
|
||||
template<class SRC>
|
||||
inline StrView
|
||||
inline auto
|
||||
TextTemplate::Action::instantiate (InstanceCore<SRC>& core) const
|
||||
{
|
||||
using Result = decltype (core.getContent(val));
|
||||
switch (code) {
|
||||
case TEXT:
|
||||
return val;
|
||||
return Result(val);
|
||||
case KEY:
|
||||
return core.getContent (val);
|
||||
case COND:
|
||||
|
|
@ -733,7 +812,7 @@ namespace lib {
|
|||
}
|
||||
|
||||
template<class SRC>
|
||||
inline StrView&
|
||||
inline auto&
|
||||
TextTemplate::InstanceCore<SRC>::yield() const
|
||||
{
|
||||
return unConst(this)->rendered_;
|
||||
|
|
@ -750,7 +829,7 @@ namespace lib {
|
|||
|
||||
/** Instantiate next Action token and expose its rendering */
|
||||
template<class SRC>
|
||||
inline StrView
|
||||
inline typename SRC::Value
|
||||
TextTemplate::InstanceCore<SRC>::instantiateNext()
|
||||
{
|
||||
return actionIter_? actionIter_->instantiate(*this)
|
||||
|
|
@ -764,7 +843,7 @@ namespace lib {
|
|||
* @return the rendering produced by the selected next Action token
|
||||
*/
|
||||
template<class SRC>
|
||||
inline StrView
|
||||
inline typename SRC::Value
|
||||
TextTemplate::InstanceCore<SRC>::reInstatiate (Idx nextCode)
|
||||
{
|
||||
if (nextCode == Idx(-1))
|
||||
|
|
@ -776,7 +855,7 @@ namespace lib {
|
|||
|
||||
/** retrieve a data value from the data source for the indiated key */
|
||||
template<class SRC>
|
||||
inline StrView
|
||||
inline typename SRC::Value
|
||||
TextTemplate::InstanceCore<SRC>::getContent (string key)
|
||||
{
|
||||
static StrView nil{""};
|
||||
|
|
@ -811,7 +890,7 @@ namespace lib {
|
|||
{
|
||||
ctxStack_.push (NestedCtx{move (dataIter)
|
||||
,dataSrc_});
|
||||
dataSrc_ = dataSrc_.openContext (ctxStack_.top().first);
|
||||
focusNested();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
@ -831,7 +910,7 @@ namespace lib {
|
|||
++dataIter;
|
||||
if (dataIter)
|
||||
{ // open next nested context *from enclosing context*
|
||||
dataSrc_ = ctxStack_.top().second.openContext (dataIter);
|
||||
focusNested();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
|
|
@ -842,6 +921,28 @@ namespace lib {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Step down into the innermost data item context, prepared at the top of #ctxStack_.
|
||||
* This includes re-assigning the current #dataSrc_ to a new nested data scope,
|
||||
* created from the enclosing scope, which is assumed to sit at the top of ctxStack_,
|
||||
* and which is _guaranteed to rest locked at this memory location_ as long as operation
|
||||
* is carried on within the new nested context. This is to say that a pointer to the
|
||||
* parent scope (residing at ctxStack_.top()) can be embedded and used from this
|
||||
* nested context safely. To leave this nested scope, it is sufficient to
|
||||
* swap this->dataSrc_ with the stack top and then pop the topmost frame.
|
||||
*/
|
||||
template<class SRC>
|
||||
inline void
|
||||
TextTemplate::InstanceCore<SRC>::focusNested()
|
||||
{
|
||||
REQUIRE (not ctxStack_.empty());
|
||||
NestedCtx& innermostScope = ctxStack_.top();
|
||||
DataCtxIter& currentDataItem = innermostScope.first;
|
||||
SRC& parentDataSrc = innermostScope.second;
|
||||
|
||||
this->dataSrc_ = parentDataSrc.openContext (currentDataItem);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -319,7 +319,7 @@ for} tail...
|
|||
|
||||
|
||||
VERIFY_FAIL ("TextTemplate spec without active placeholders"
|
||||
, TextTemplate::compile("horror boredom"));
|
||||
, TextTemplate::compile("O tempora O mores"));
|
||||
|
||||
VERIFY_FAIL ("Tag without key: ...horror ${<placeholder> |↯|}"
|
||||
, TextTemplate::compile("horror ${ } vacui"));
|
||||
|
|
|
|||
|
|
@ -113541,8 +113541,8 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1710804833930" ID="ID_1766619477" MODIFIED="1710804839987" TEXT="Test-Binding">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node COLOR="#338800" CREATED="1710804833930" ID="ID_1766619477" MODIFIED="1711475112219" TEXT="Test-Binding">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
<node CREATED="1710804841699" ID="ID_933127899" MODIFIED="1710804917699" TEXT="eine String-Spec mit kommaseparierten key=val -Literalen"/>
|
||||
<node CREATED="1710804918648" ID="ID_1642910022" MODIFIED="1710804947480" TEXT="wird einmal geparst und in eine Map gepackt ⟶ Map-Binding">
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1711385576730" ID="ID_941884997" MODIFIED="1711385595496" TEXT="muß die geparste Map irgendwo dauerhaft bereithalten">
|
||||
|
|
@ -113569,16 +113569,13 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
<icon BUILTIN="broken-line"/>
|
||||
<node CREATED="1711406517514" ID="ID_1242921809" MODIFIED="1711406555810">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
zunächst einmal: Deklaration kann <font face="Monospaced" color="#1524b7">auto</font> verwenden
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
<node CREATED="1711406562556" ID="ID_21985660" MODIFIED="1711406577582" TEXT="...sofern dann weiter unten, bei der Definition der Typ inferiert werden kann"/>
|
||||
|
|
@ -113589,16 +113586,13 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
<icon BUILTIN="clanbomber"/>
|
||||
<node COLOR="#8d2b6b" CREATED="1711406755346" HGAP="31" ID="ID_866002002" MODIFIED="1711406886648" TEXT="Compiler findet nur den Copy-Konstruktor" VSHIFT="4">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
Grund: die expliziten Spezialisierungen kommen überhaupt erst ins's Spiel, wenn der Compiler die konkreten Template-Argumente bereits erschlossen hat. Vorher schaut er nur in das primäre Template... und wenn das keinen Konstruktor hat, dann kennt der Compiler nur den Copy-Konstruktor. <i>Die resultierende Fehlermeldung ist dann unglaublich hilfreich...</i>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</html></richcontent>
|
||||
<font NAME="SansSerif" SIZE="10"/>
|
||||
<icon BUILTIN="smiley-angry"/>
|
||||
</node>
|
||||
|
|
@ -113614,9 +113608,7 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
</node>
|
||||
<node COLOR="#5b280f" CREATED="1711416080681" ID="ID_1704611898" MODIFIED="1711416428174" TEXT="weil diese dann mit einem enable-if-Spezialfall kollidiert">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
Das sind die Limitierungen bei Function-Overloads. Mit Template-Deduction-Guides hätten wir dieses Problem vermutlich nicht (wenn wir sie denn schreiben könnten).
|
||||
|
|
@ -113628,8 +113620,7 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
<u>Im Detail</u>: Bei der Function Overload-Resolution werden die verschiedenen Kandidaten geordnet. Dabei werden zunächst <b>alle überschüssigen und Default-Argumente weggestrichen</b>. Konsequenz: ein enable-If kann zwar einen einzelnen Overload <i>entfernen </i>— wenn er aber <i>nicht entfernt</i> wurde, stehen zwei äquivalente Overloads da, und es gibt einen Compilation-Fehler. Rückgabewerte helfen hier auch nichts (die tragen nur zur const-ness-Auswahl bei). Das heißt, bei Function-Overload-Resolution muß das enable-If auf einem <b>weiteren</b>  Parameter stehen, der auch <b>tatsächlich verwendet</b> wird. Und an der Stelle wird's dann wirklich so trickreich, das es mir fragil erscheint.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="closed"/>
|
||||
</node>
|
||||
<node CREATED="1711416429849" ID="ID_1709082514" MODIFIED="1711416453288" TEXT="Kann deshalb nur Spezial-Bindings bieten, kein generisches Binding">
|
||||
|
|
@ -113651,11 +113642,191 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1710793367246" ID="ID_1695880616" MODIFIED="1710793369256" TEXT="ETD">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node CREATED="1710804556008" ID="ID_1997691101" MODIFIED="1710804588772" TEXT="DataSrc ist ein Rec<GenNode>"/>
|
||||
<node CREATED="1710804591691" ID="ID_1815976275" MODIFIED="1710804603973" TEXT="Key-Zugriff wird übersetzt in Attribut-Zugriff"/>
|
||||
<node CREATED="1710804616944" ID="ID_538897899" MODIFIED="1710804626706" TEXT="Iteration wird übersetzt in Scope-Iteration"/>
|
||||
<node CREATED="1710804633837" ID="ID_1119504137" MODIFIED="1710804648600" TEXT="jedes Element im Scope ist wieder ein Rec<GenNode>"/>
|
||||
<node CREATED="1710804715954" ID="ID_1537394187" MODIFIED="1710804736900" TEXT="diese Elemente können aber leer sein (wegen Parent-Delegation)"/>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1710804556008" ID="ID_1997691101" MODIFIED="1711475133506" TEXT="DataSrc repräsentiert ein »Objekt« ≙ Rec<GenNode>">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node CREATED="1711475218638" ID="ID_44068114" MODIFIED="1711475231130" TEXT="Benötigte Eigenschaften">
|
||||
<icon BUILTIN="yes"/>
|
||||
<node CREATED="1711475232644" ID="ID_315057671" MODIFIED="1711475241511" TEXT="muß auf Attribute per Key zugreifen können"/>
|
||||
<node CREATED="1711475244323" ID="ID_1323303321" MODIFIED="1711475260460" TEXT="muß für einen solchen Zugriff eine String-Repräsentation liefern">
|
||||
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1711475264023" ID="ID_1962088901" MODIFIED="1711497355791" TEXT="das ist ein kritischer Punkt für dieses Binding">
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1711475279862" HGAP="21" ID="ID_392726526" MODIFIED="1711497352861" STYLE="bubble" VSHIFT="-4">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
<p style="text-align: center">
|
||||
GenNode wurde explizit entworfen
|
||||
</p>
|
||||
<p style="text-align: center">
|
||||
um »Programming by Reflection«
|
||||
</p>
|
||||
<p style="text-align: center">
|
||||
zu unterbinden
|
||||
</p>
|
||||
<p style="text-align: center">
|
||||
<font size="6">‼</font>
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<edge COLOR="#7e4c4c"/>
|
||||
</node>
|
||||
<node CREATED="1711475487018" ID="ID_240701708" MODIFIED="1711475497215" TEXT="aber GenNode hat eine String-Repräsentation">
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
<node CREATED="1711475501474" ID="ID_1956601098" MODIFIED="1711475509907" TEXT="das gilt auch für den »DataCap«">
|
||||
<node CREATED="1711475606554" ID="ID_716867341" MODIFIED="1711475616097" TEXT="der beruht auf einer lib::Variant"/>
|
||||
<node CREATED="1711475617289" ID="ID_653285103" MODIFIED="1711475627315" TEXT="und hat einen eingebetteten Buffer"/>
|
||||
<node CREATED="1711475628101" ID="ID_89556720" MODIFIED="1711475672880">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
mit einer <b>pure virtual</b> String-Repräsentation
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1711475675289" ID="ID_369134454" MODIFIED="1711477410646" TEXT="für GenNode ist das mit einem Variant-Visitor hinterlegt?">
|
||||
<icon BUILTIN="help"/>
|
||||
<node CREATED="1711477412790" ID="ID_512513677" MODIFIED="1711477429494" TEXT="gen-node.cpp : DataCap::operator string()"/>
|
||||
<node CREATED="1711477430263" ID="ID_931178080" MODIFIED="1711477462879" TEXT="delegiert an Variant<DataValues>::operator string()"/>
|
||||
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1711477398892" ID="ID_130677787" MODIFIED="1711477476253" TEXT="ich finde den nicht??">
|
||||
<icon BUILTIN="flag-pink"/>
|
||||
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1711477477202" ID="ID_1417186531" MODIFIED="1711477489600" TEXT="wo versteckt sich der Wicht?">
|
||||
<icon BUILTIN="smiley-oh"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1711477500254" ID="ID_403443386" MODIFIED="1711477519140" TEXT="man könnte es ganz einfach mal »pragmatisch« nutzen">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1711477520228" ID="ID_1881261418" MODIFIED="1711477535488" TEXT="und so herausfinden wo es implementiert ist">
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1711486639403" ID="ID_57682971" MODIFIED="1711486666394" TEXT="außerdem gibt es ein Konsistenz-Problem">
|
||||
<icon BUILTIN="clanbomber"/>
|
||||
<node CREATED="1711486668095" ID="ID_325496153" MODIFIED="1711486682097" TEXT="das API gibt eine eine string_view auf gerenderte Daten"/>
|
||||
<node CREATED="1711486682877" ID="ID_1454625427" MODIFIED="1711486705366" TEXT="aber nicht-String-Content existiert nur als transientes Rendering"/>
|
||||
<node CREATED="1711486706362" ID="ID_1823130623" MODIFIED="1711491152545" TEXT="nur temporär bereithalten?">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
Man könnte erwarten, daß diese string-view tatsächlich nur während der Iteration genutzt wird — letztlich möchte man ja das gerenderte Text-Template irgendwo integrieren...
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
<icon BUILTIN="idea"/>
|
||||
<icon BUILTIN="button_cancel"/>
|
||||
<node CREATED="1711486798598" ID="ID_1504667047" MODIFIED="1711488886985" TEXT="d.h. wieder der shared-ptr-Trick"/>
|
||||
<node CREATED="1711486786808" ID="ID_1943118433" MODIFIED="1711486796658" TEXT="das ist bei der Map-aus-string-spect genauso"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1711491153943" ID="ID_561443056" MODIFIED="1711491282329" TEXT="noch besser: Ergebnistyp dynamisieren">
|
||||
<icon BUILTIN="forward"/>
|
||||
<node CREATED="1711491167092" ID="ID_679038844" MODIFIED="1711491179598" TEXT="dem Iterations-Mechanismus ist dieser Typ nämlich scheißegal"/>
|
||||
<node CREATED="1711491180498" ID="ID_1200622160" MODIFIED="1711491194500" TEXT="und da die InstanceCore ohnehin geTemplated ist...."/>
|
||||
<node CREATED="1711491217221" ID="ID_1271938499" MODIFIED="1711491269346" TEXT="zudem ist GenNode explizit keine String-Datenstruktur (sondern binär)"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1711475137705" ID="ID_265880025" MODIFIED="1711475154240" TEXT="Implementierung der Repräsentation">
|
||||
<icon BUILTIN="flag-yellow"/>
|
||||
<node CREATED="1711491795547" ID="ID_1481897171" MODIFIED="1711491809910" TEXT="Einstiegspunkt für einen Scope ist stets eine GenNode">
|
||||
<node CREATED="1711491812270" ID="ID_234227882" MODIFIED="1711491820513" TEXT="man könnte das auch genau anders herum handhaben"/>
|
||||
<node CREATED="1711491821460" ID="ID_1916429769" MODIFIED="1711491899861" TEXT="aber dann hätten wir ein Problem mit Zugriffen auf Nodes die kein Record sind"/>
|
||||
<node CREATED="1711491921789" ID="ID_714056549" MODIFIED="1711491940208" TEXT="GenNode als Einstiegspunkt dagegen stellt keine Einschränkung dar">
|
||||
<node CREATED="1711491941836" ID="ID_194254261" MODIFIED="1711491952929" TEXT="denn sollte man doch einmal einen Record haben,"/>
|
||||
<node CREATED="1711491953690" ID="ID_477686075" MODIFIED="1711491964956" TEXT="dann läßt sich leicht eine Ref-GenNode bauen"/>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1711492643022" ID="ID_149060446" MODIFIED="1711492665647" TEXT="als Abkürzung / Cache wird ein sekundärer Rec-Pointer mitgeführt">
|
||||
<node CREATED="1711492667078" ID="ID_502425157" MODIFIED="1711492672470" TEXT="dieser kann NULL sein"/>
|
||||
<node CREATED="1711492673202" ID="ID_1566411442" MODIFIED="1711492715043" TEXT="⟹ dann ist klar daß das Target value-GenNode ist"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#f8f1cb" COLOR="#a50125" CREATED="1711492744185" ID="ID_1414659694" MODIFIED="1711493061959" TEXT="zudem brauchen wir einen parent-Scope">
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
<node BACKGROUND_COLOR="#fafe99" COLOR="#fa002a" CREATED="1711493190221" ID="ID_1049043334" MODIFIED="1711493218882" TEXT="hier wird die Sache brandgefährlich">
|
||||
<icon BUILTIN="clanbomber"/>
|
||||
<node CREATED="1711493248149" ID="ID_1396313935" MODIFIED="1711493305705" TEXT="denn die ETD schließt das per Design explizit aus">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
Das ist gradezu die Grundannahme: der Verarbeiter hat die Struktur zu kennen — die Node hat <i>keine introspektiven Fähigkeiten</i>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</node>
|
||||
<node CREATED="1711493338536" ID="ID_1143325593" MODIFIED="1711493361420" TEXT="wäre aber möglich — so wie der Zugriff aktuell implementiert ist">
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
<node CREATED="1711493364914" ID="ID_1017484098" MODIFIED="1711493380944" TEXT="sinnvollerweise müßte man das als Teil des Konzepts betrachten"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1710804591691" ID="ID_1815976275" MODIFIED="1710804603973" TEXT="Key-Zugriff wird übersetzt in Attribut-Zugriff">
|
||||
<node CREATED="1711469498958" ID="ID_106772989" MODIFIED="1711469520804" TEXT="kann kaskadieren in einen parent-Scope"/>
|
||||
<node CREATED="1711469521509" ID="ID_680558791" MODIFIED="1711469543766" TEXT="dafür wird ein optionaler Rec<GenNode>* verwendet"/>
|
||||
</node>
|
||||
<node CREATED="1710804616944" ID="ID_538897899" MODIFIED="1710804626706" TEXT="Iteration wird übersetzt in Scope-Iteration">
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1711469668314" ID="ID_1640263666" MODIFIED="1711469688642" TEXT="hier ist eine zusätzliche Binding-Konvention notwendig">
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
<node CREATED="1711469692623" ID="ID_210919622" MODIFIED="1711469701114" TEXT="der der Scope ist ein vector<GenNode>"/>
|
||||
<node CREATED="1711469701958" ID="ID_1217697238" MODIFIED="1711469731982" TEXT="und nicht eine Liste von »Objekten« ≙ Rec<GenNode>"/>
|
||||
<node CREATED="1711469737057" ID="ID_1561127899" MODIFIED="1711469838206" TEXT="notwendig nur aus Konsistenz-Gründen">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
...denn in der Praxis wird erwartet, daß im Scope sehr wohl wieder verschachtelte Records liegen; sonst würde diese Datenstruktur ja wenig Sinn machen
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1711469895229" ID="ID_1582744279" MODIFIED="1711469917481">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
⟹ wenn Kind <i>kein Record</i> ist
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
<node CREATED="1711469919922" ID="ID_1678329722" MODIFIED="1711469936011" TEXT="dann müssen wir einen pseudo-Scope synthetisieren"/>
|
||||
<node CREATED="1711469936951" ID="ID_1819512723" MODIFIED="1711469968399" TEXT="dieser enthält nur ein virtuelles Attribut "value""/>
|
||||
</node>
|
||||
<node CREATED="1711469979505" ID="ID_1900618698" MODIFIED="1711470039575" TEXT="⟹ sonst: verschachteltes Binding">
|
||||
<arrowlink COLOR="#4d81bd" DESTINATION="ID_1119504137" ENDARROW="Default" ENDINCLINATION="5;-35;" ID="Arrow_ID_279787572" STARTARROW="None" STARTINCLINATION="-46;4;"/>
|
||||
</node>
|
||||
<node CREATED="1711491341932" ID="ID_224092406" MODIFIED="1711491362644" TEXT="all dies soll komplett dynamisch (demand-driven) gehandhabt werden">
|
||||
<icon BUILTIN="yes"/>
|
||||
<node CREATED="1711491377592" ID="ID_1223395543" MODIFIED="1711491418403">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
es gibt ein Prädikat <font face="Monospaced" color="#1729bd">genNode.isNested()</font>
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1710804633837" ID="ID_1119504137" MODIFIED="1711470052179" TEXT="normalerweise ist Element im Scope wieder ein Rec<GenNode>">
|
||||
<linktarget COLOR="#4d81bd" DESTINATION="ID_1119504137" ENDARROW="Default" ENDINCLINATION="5;-35;" ID="Arrow_ID_279787572" SOURCE="ID_1900618698" STARTARROW="None" STARTINCLINATION="-46;4;"/>
|
||||
</node>
|
||||
<node CREATED="1710804715954" ID="ID_1537394187" MODIFIED="1711470077889" TEXT="verschachtelte Scopes können aber leer sein (wegen Parent-Delegation)"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
|
|
@ -121239,9 +121410,7 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
<linktarget COLOR="#65354c" DESTINATION="ID_135618716" ENDARROW="Default" ENDINCLINATION="21;-62;" ID="Arrow_ID_696408734" SOURCE="ID_1103669600" STARTARROW="None" STARTINCLINATION="-2;93;"/>
|
||||
<node CREATED="1711405899493" ID="ID_423341464" MODIFIED="1711406049382" STYLE="bubble">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<head/>
|
||||
<body>
|
||||
<table http-equiv="content-type" content="text/html; charset=utf-8" class="t-sdsc-begin">
|
||||
<tr class="t-sdsc">
|
||||
|
|
@ -121266,9 +121435,7 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
</node>
|
||||
<node CREATED="1711406206524" ID="ID_1416799059" MODIFIED="1711406241333">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
Automatic Deduction setzt ctor im <b>primären</b>  Template vorraus
|
||||
|
|
@ -121279,9 +121446,7 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
<node CREATED="1711406253820" ID="ID_985724957" MODIFIED="1711406261329" TEXT="+ diverse partielle Spezialisierungen"/>
|
||||
<node BACKGROUND_COLOR="#d6b67b" COLOR="#a50125" CREATED="1711406265488" ID="ID_24293979" MODIFIED="1711406390599">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
⟹ dann findet der Compiler ohne Hilfe <b>keinen Construktor</b>
|
||||
|
|
@ -121289,9 +121454,7 @@ std::cout << tmpl.render({"what", "World"}) << s
|
|||
</body>
|
||||
</html></richcontent>
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<head/>
|
||||
<body>
|
||||
<p>
|
||||
bzw. er findet nur den Copy-Konstruktur
|
||||
|
|
|
|||
Loading…
Reference in a new issue