AUA: Henne oder Ei?

denn:

  • Nexus verwendet CoreService als "upstream", um alle sonstigen Nachriten dorthin zuzustellen
  • CoreService hat Nexus als Upstream, um mit dem restlichen System kommunizieren zu können

gemeint ist: im ctor

Es speichert nur die Referenz

Ganz anders Model::Tangible: dieses registriert sich bei der Konstruktion

oder anders herum,

aber so herum macht es mehr Sinn

CoreService hat keine volle Bus-Connection

...das hab ich mir jetzt explizit so überlegt und es ist sinnvoll.

Nur ein Tangible kann eine volle Bus-Connection haben, und das heißt,

es kann downlink-Nachrichten bekommen. Dagegen hat CoreService lediglich ein "freistehendes"

BusTerm, das damit Nachrichten an den Nexus schicken kann.

UI: GuiNotification

...hängt am UI-Bus,

aber nur via einfacher "uplink"-Verbindung

Sicht "von unten"

...weil es dadurch passieren könnte,

daß die Konstruktion des GuiRunners schon scheitert, bevor der Rumpf des ctors aufgerufen wird.

In einem solchen Fall wird leider auch der Rumpf des dtors nicht aufgerufen, wodurch das

Term-Signal nicht ausgesendet würde.

gemeint ist: keine volle bidirektionale Connection,

denn CoreService ist kein Tangible. Das macht Sinn so, habe darüber nachgedacht.

Anmerkung: ein "frestehendes" BusTerm ist valide und zugelassen, es hat halt nur eine uplink-Connection.

nur ein Tangible kann downlink-Nachrichten sinnvoll empfangen;

es muß dazu auch jede Menge Methoden implementieren.

GtkLumiera in Konstruktion

NotificationFacade noch nicht offen

müssen eigens aktiviert werden

...weil unser Thread-Framework

tatsächlich erzwingt, daß der neue Thrad zu laufen beginnt, bevor die

startende Funktion zurückkehrt.

Daher können wir zumindest annehmen, daß die ganze Initialisierung

bereits läuft, wenn die start()-Funktion mit true (Erfolg) zurückkommt.

Allerdings ist definitiv ein Race gegeben, und wenn

direkt beim Starten anderer Subsysteme nach dem GUI etwas schiefläuft,

dann kann der Shutdown-Prozeß den Start des GUI überholen.

wirkt alles mehr oder weniger beliebig...

0000000937: ERR: core-service.hpp:111: worker_3: ~CoreService: Some UI components are still connected to the backbone.

Speicherzugriffsfehler

...muß diejenigen Bus-Verbindungen abziehen, die von Members dieser Klasse stammen

  • CoreService selber
  • der NotificationService

wenn ich per Value capture, dann gibts schon

beim Start des GUI einen SEGFAULT

Und noch schlimmer: im Debugger gibts keinen

die Closure eines Lambdas hängt am Kontext

konkret:

der Kontext ist hier nämlich ein anderes Lambda, das dem Aufruf des GUI-Plugins mitgegeben wurde.

Dagegen die alte Lösung erzeugte an dieser Stelle einen Bind-Ausdruck, und das war offenbar genug,

um nicht mehr von dem direkten Kontext abhängig zu sein, in dem der Thread gestartet wurde.

Denn dieser Kontext (auf dem Stack) ist natürlich lange schon weg, wenn der Thread

terminiert und dann tatsächlich den Fuktor aufrufen möchte

Debug: nur ein Element connected

weil die Abstraktion "UI-Element" eben grade

die UI-internen Framework-Aspekte ausklammert.

Die Elemente stellen eine Abbildung der Strukturen aus der Session dar,

und ihre "Methoden" sind Commands auf der Session!

muß eigens aktiviert werden

Fehlerlog-Anzeige vorläufig irgendwo....

...das ist nützlich zur Diagnose,

  • aber läßt sich das überhaupt auf IterSource übertragen?
  • war es überhaupt je gerechtferigt? zu starke Annahme über den Diff-Erzeuger!

ist schon schlimm genug....

...denn eigentlich geht es nur um ein einfaches Producer-Interface,

das einen Element-Pointer durchreicht. Das einzige Problem, das ich sehe,

ist, daß hier ownership übertragen wird.

nämlich

  • ein sehr theoretisches und anspruchsvolles Konzept
  • der Zwang, das auf jedem Empfänger umzusetzen
  • die hablseidene Trickserei mit der konkreten Puffergröße
  • den double-dispatch im Diff-Framework selber
  • das Variant-basierte GenNode-Framework

das wird sowiso ein Desaster

das heißt, die Diff-Implementierung muß länger leben

faktisch erfolgt somit ein Callback aus einem anderen Thread

um das Diff zu pullen

jede Facade-Funktion brauch einen Dispatcher

Das wird eine ganze Me

bevor die Facade geöffnet wir

und arbeitet asynchron

Argument-Storage

organisieren

brauche dedizierten Dispatcher

...könnte das am Ende nicht sinnvoll sein,

speziell den UI-Shutdown-Trigger über den neuen Mechanismus laufen zu lassen,

obwohl jener doch genau der Anlaß war, diesen neuen Mechanismus zu bauen.

wenn die Queue voll ist

wird erst alles Andere abgearbeitet

wenn UI-Thread blockt/verhungert,

kommt der rettende Shutdown gar nicht durch

bisher können wir das GUI nur aktiv intern schließen,

indem wir ein GTK-Signal erzeugen, das das Hauptfenster schließt

...das war genau der Kern der "Plugin-Debatte".

Eine solche globale, flache, dynamisch gebundene Ebene

klingt nach wahnsinnigen Möglichkeiten, aber nur solange, bis man sich

eine einzige Funktion konkret durchdenkt: es läuft auf Spaghetti-Code hinaus

...indem der NotificatonService nun vom UI-Manager gemanaged wird :)

zieht komplett-Umbau

des Gui-top-Level nach sich

...nur eine heuristische Vermutung von mir

stützt sich auf folgenden Quellcode

Application::Application(const Glib::ustring& application_id, Gio::ApplicationFlags flags)

:

  // Mark this class as non-derived to allow C++ vfuncs to be skipped.

  //Note that GApplication complains about "" but allows NULL (0), so we avoid passing "".

  Glib::ObjectBase(0),

  Gio::Application(Glib::ConstructParams(custom_class_init(), "application_id", (application_id.empty() ? 0 : application_id.c_str()), "flags", GApplicationFlags(flags), static_cast<char*>(0))),

  m_argc(0),

  m_argv(0)

{

  gtk_init(0, 0);

}

Proc: SessionCommand

setzt aktivierten Dispatcher zwingend voraus

es genügt definitiv nicht, nur die Dispatcher-Komponente(Schnittstelle) erreichen zu können.

Jede Operation, die über dieses externe Interface bereitsteht, benötigt zur Implementierung

eine aktiv laufende Dispatcher-Queue.

Daher macht es Sinn, den Interface-Lebenszyklus ganz starr an den Disspatcher zu binden

...und zwar wirklich sehr implizit,

nämlich über die Identität (IDs) der Command-Parameter.

Das heißt, ein eingehendes Command paßt nur zu einer bestimmten Session-Instanz,

was zwar jederzeit (via statisches/internes Session-API) verifizierbar ist, jedoch nicht offensichtlich

das folgt einfach aus den logischen Eigenschaften der beteiligten Komponenten,

welche eben autonom sind.

Das heißt im Klartext, alle Clients müssen darauf vorbereitet sein, daß diese Schnittstelle

jederzeit wegbrechen kann, was dann heißt, daß irgend ein Aufruf eine Exception wirft

wer besitzt die

Implementierung

meint: zwei gekoppelte Statusvariable

muß alle Operationen durchschleifen

oder muß PImpl als Interface exponieren

meint: zwei gekoppelte Statusvariable

Shutdown tricky

Session-Subsystem implementieren (#318)

....das ist schon mehr ein Meta-Ticket,

und es hängt wohl zu viel darunter, um es gleich ganz abschließen zu können.

Aber ich akzeptiere es und verwende es jetzt als Treiber

ist nicht "die Session

das Lock sorgt hier für konsistenten Zustand und Sichtbarkeit (memory barrier)

Lock ist hier das Dispatcher-Lock

...wenn jemand zugreift

grundlegende Design-Enscheidung

  • wir haben Komponenten mit Dependency-Injection
  • da beide Komponenten nur nach ihren eigenen Hinsichten funktionieren,
    wird das System insgesamt einfacher

muß SessionCommandService schließen

bevor die Dispatcher-loop angehalten wird

1/2017 Review durchgeführt und Logik überarbeitet.

Einziger Risikofaktor ist nun, wenn beim Schließen des SessionCommand-Intertfaces

oder beim Signalisieren an den Thread eine Exception fliegt; denn dann loggen wir zwar,

aber die Shutdown-Rückmeldung kommt u.U niemals an, und damit bleiben wir

am Ende von main() einfach hängen.

Ich halte diese Fälle aber für in der Praxis nicht relevant,  und verzichte daher auf eine Spezialbehandlung

Einfachen Aufruf

implementieren

will sagen: in den Visitor-Methoden-Implementierungen

aber ist nicht auf dem Visitor-Interface darstellbar

....über einen GenNode-Visitor nachdenken

aber

  • nicht jetzt
  • das Problem müßte mehrfach auftreten
  • könnte zu Switch-On-Type-programming führen

Idee: Zusammenarbeit

...das wäre eine Protokoll-Erweiterung

vertagt; Ticket #1058

...das heißt,

die mehrfachen Indirektionen und das Ein-/Auspacken der Argumente

es wäre denkbar, an dieser Stelle

unvollständige Argument-Tupel zu akzeptieren

und die Argumente von links her zu schließen (currying)

denn es erzwingt,

daß die betreffenden Commands schon erzeugt und registriert sein müssen,

wenn in der UI ein InvocationTrail angelegt wird.

Architektur-Entscheidung

kann offen bleiben

reine ID-Wirtschaft wäre möglich

...wartet noch darauf,

daß die alte, obsolete Timeline zurückgebaut ist

siehe guifacade.cpp

Problem ist: wenn das triggerShutdown kommt,

bevor die Notification-Facade geöffnet werden konnte

Begründung:

Das neue System ist anscheinend fest integriert in Gio::Application.

Mir ist nicht klar, wieso ein Fenster/Widget das Interface Gio::Actionable implementieren muß.

Ich werde den Verdacht nicht los, daß hier das Ziel verfolgt wird, eine "Action" von den

Grenzen der Applikation zu befreien und direkt in den Desktop zu integrieren.

Mit Desktop ist natürlich der Gnome-Desktop gemeint. Was diesen Verdacht bestärkt,

ist, daß Gio::Application sofort auch gleich eine dBus-Verbindung hochfährt.

eigentlich wollen wir "das aktuelle"

Lösung: schwebende Bindung

arbeitet dann freischwebend

...meint:

wir müssen zur Aufrufzeit einer Aktion

an den aktuellen Kontext ankoppeln können.

Das heißt, der UiManager muß im Stande sein,

diesen "aktuellen Kontext" irgendwo aufzufischen

dort wird der Kontext aufgegriffen

...und der wird in der Tat an vielen Stellen includiert

und verwendet, und das ist auch gut so

Njet

InvocationTrail ist tot

generisches Öffnen

...um die Entwicklung des Designs zu erzwingen

und den Teufelskreis zu durchbrechen!

...stattdessen einen Fehler-Indikator auslösen

(Beispiel "in-point fehlt")

...das ist eine Reaktion,

die von einem managing Ui-Element ausgeführt wird,

aber von einem externen State-Change getriggert wird

damit UNDO funktionieren kann,

müssen wir schon beim capture wissen,

welches Objekt (ID) hinzugefügt werden wird.

Denn sonst müßten wir uns den gegenwärtigen Inhalt speichern

und das wäre unsinnig.

wir können den größten Teil dieser Einzeiler-Funktionen loswerden,

da es nur darum geht, via globalCtx auf den passenden Controller zuzugreifen

das verschiebt das Problem nur

UI-Koordinaten (UICoord)

...and this anchorage can be covered and backed by the currently existing UI configuration

...by interpolation of some wildcards

...need to be extended to allow anchoring

we may construct the covered part of a given spec, including automatic anchoring.

navigate to the real UI component

designated by the given coordinate spec

...halten wir besser raus aus diesem Design.

Denn es würde stärkere Annahmen über die "Zielelemente" erforderlich machen,

und diese dann doch wieder in ein Korsett zwängen. Im Moment (10/17) habe ich

stark den Verdacht, daß wir das nur in wenigen Spezialfällen brauchen werden,

und dann kann man es auch extern belassen.

...was für verschiedene Arten von Zugriff

sind denkbar und müssen in der Strategy konfigurierbar sein?

mögliche

Komponenten

wie funktioniert

Pfad-Navigation?

betrachte ich als ungesund

eine reverse resolution

zentrales Problem

...der beim Erstellen des Elements

mit den zu diesem Zeitpunkt bekannten UI-Korrdinaten bestückt wird

auf welche Eigenschaften

stützen wir uns?

wer bestimmt,

was "Kind" ist?

oder den Spot verschieben

verbleibende

Probleme

VariadicArgumentPicker_test

welche Operationen

sind wirklich notwendig

...denn wir verlangen, daß wir nach dem Interpolieren über eine Lücke

immer noch mindestens ein explizt gegebenes Element im Pfad haben,

welches auch von der UI-Topologie bestätigt wird.

Grund: wir wollen vermeiden, abschließende Wildcards bloß irgendwie zu binden

nämlich wenn der Pfad mit einem explizit gegebenen Präfix anfängt,

dann aber Wildcards enthält, die nicht nach den verschärften Bedingungen gecovert werden können.

beispielsweise

  • wenn schon das Präfix nicht paßt
  • wenn das erste Element nach dem Gap nirgends im realen UI in der tiefe existiert
  • wenn mehr Wildcards da sind, als restliche Tiefe zum Matchen

letzten Endes war es nahezu gleich schwer zu implementieren,

aber von der Aufruf-Logik her einfacher, stets nach partieller zu suchen

und totale Coverage nur nachträglich durch Längenvergleich festzustellen

wo der Spot ist

Innerhalb einer Kinder-Folge gibt es keine duplikaten IDs.

Das heißt, es genügt, den ersten Match zu nehmen.

Warnung: diese Konvention ist besonders tückisch,

denn eine Verletzung kann weithin unbemerkt bleiben

der Resolver macht kein Memory-Management,

sondern speichert einfach Zeiger.

Es wird erwartet, daß diese gültig bleiben,

solange irgend jemand auf den Resolver oder den

daraus resultierenden Pfad zugreift

die Topologie, aber auch der Fokus-Zustand

ändern sich nicht während der aktiven Lebensdauer eines Resolvers

Hierbei ist aktive Lebensdauer wie bei einem Iterator zu verstehen.

aber es gibt Konsistenzchecks + Exceptions

wenn die Auswertung aufgrund einer gebrochenen Konvention entgleist

Tiefensuche über die reale UI-Topologie

Ziel ist, den Pfad bestmöglich zu covern

Es gilt die erste maximal abdeckende Lösung

Tiefe, bis zu der dieser Pfad gedeckt ist.

Sofern der Pfad bereits explizit ist, genügt diese Info allein

heap-allozierter expliziter Pfad.

  • wird notwendig, wenn *this wildcards enthält
  • Lösung wird unter Alternativen ausgewählt (nach maximaler Tiefe)

Ha! das ist eine Monade!!!!!1!11!

...aber im Moment der Lösung brauche ich den Pfad aufwärts.

Das heißt, wir müssen ihn bereits im Aufrufkontext bereit liegen haben

Puffer ab Tiefe

vom Pfad initialisieren

monadische Lösung

möglich?

echte Expand-Funktion notwendig

mathematische Monaden sind viel mehr...

Im Besonderen sind es Typen höherer Ordnung,

also mehr als bloß parametrisierte Typen (Templates)!

alles mit einer Form des IterExplorer machbar ist

...da IterExplorer einen Template-Template-Parameter nimmt,

ist er eigentlich ein Meta-Template, und es gibt diverse

Ausprägungen, die alle subtile Seiteneffekte ausnutzen....

...was nicht grade zur Verständlichkeit des Ganzen beiträgt

das Fortschreiten der Berechnung dargestellt werden kann

Problem: Layer sind verkoppelt

dann aber als State Monad

m >>= f = \r -> let (x, s) = m r

                            in (f x) s

wende die resultierende State-Monade

auf den Zwischenzustand x aus (1) an

sind Monaden

wirklich hilfreich?

alternatives

Ziel

Fazit:

brauche...

das impliziert grundsätzlich einen Stack

expand() ruft eine vorbereitete Parametrisierung

 für diesen Expand-Mechanismus auf

...selbst wenn man ihn für eine triviale Implementierung

eigentlich überhaupt nicht braucht.

Das kann zwar zu einem gewissen Grad abgemildert werden,

indem man einen speziellen Inline-Stack mit Heap-Overflow nutzt

ich brauche ihn nicht

die Builder-Operationen moven den bisherigen Iterator-compound weg.

Ich könnte mir vorstellen, daß das einen naiven User ziemlich schockiert....

Lösung wäre, das Iterator-API erst nach einem expliziten terminalen Aufruf freizuschalten

weil...

  • sich zwar die Logik syntaktisch anschreiben läßt
  • aber beide Zweige u.U nicht auf den gleichen Typ hinauslaufen
  • und erst in der Anwendung dieses Ausdruckes werden die Typen gleichgestellt

_fun<FUN>::Sig scheitert

das ist aber unpraktisch....

....also ist es gradezu natürlich,

einen Expand-Funktor als generisches Lambda zu schreiben!

aber eine falsche Template-Instantiierung

ist ein Compile-Fehler, kein Substitutions-Fehler

oder man fällt auf eine mögliche Substitution zurück

...und wenn die Scheitert, ist das ein compile-Fehler

denn das ist der sinnvollste Fall für ein generisches Lambda

....und das ist alarmierend,

denn Debugging ist mindestens doppelt so schwer...

TreeExplorer per slicing move entfernen

wir haben bisher viel zu naiv angenommen,

daß der parent-Iterator immer auch ein TreeExploer ist.

Dem ist nicht so, ab dem Moment, wo wir mehrere Decorator-Layer haben!!

Aufrufpunkt: invokeTransformation()

In instantiation of 'lib::{anonymous}::_ExpansionTraits<FUN, SRC>::Res lib::{anonymous}::_ExpansionTraits<FUN, SRC>::Functor::operator()(ARG&) [with ARG = long int; FUN = lib::test::IterTreeExplorer_test::verify_transformOperation()::<lambda(auto:2)>&; SRC = lib::iter_explorer::IterableDecorator<long int, lib::iter_explorer::WrappedIteratorCore<lib::TreeExplorer<lib::iter_explorer::StlRange<std::vector<long int>&> > > >; lib::{anonymous}::_ExpansionTraits<FUN, SRC>::Res = std::basic_string<char>]':

src/lib/iter-tree-explorer.hpp:426:50: error: no match for call to '(std::function<std::basic_string<char>(lib::iter_explorer::IterableDecorator<long int, lib::iter_explorer::WrappedIteratorCore<lib::TreeExplorer<lib::iter_explorer::StlRange<std::vector<long int>&> > > >&)>) (long int&)'

also IterableDecorator aufgesetzt auf die Core im Transformer

wir gehen davon aus,

daß der Optimizer solche inline-Accessor-Funktionen

ohnehin restlos wegoptimieren wird

und zwar in dem Moment, wo man die Layer zusammensetzt.

...denn wenn mal ein Layer "nur" Iterator wäre,

dann könnte es eine Kombination geben, die einen solchen Layer übersrpingt

...nur was billig ist,

denn im Moment brauchen wir das überhaupt nicht

...setzt eigentliche Expand-Operation darunter voraus

...also Programmierung analog zum Filter

  • sofort aus dem Konstruktor heraus die Invariante etablieren
  • nach jedem Iterations-Schritt die Invariante erneut wiederherstellen

der Natur der Dinge folgen,

nicht den technischen Möglichkeiten

....und dann könnten diese Transformer in der Kette

nicht mehr die expandChildren aufrufen

...das heißt, es ist nicht möglich,

daß ein Layer irgendwo in der Mitte einen solchen generischen Hook aufruft.

Es sei denn, man speichert einen Funktion-Pointer in der Basis,

oder nimmt eben gleich eine virtuelle Funktion.

Das ist aber hier aus grundsätzlichen Überlegungen heraus keine Option

das IterStateWrapper-API ist optimal

unter der Annahme, daß wir beim Lumiera Forward Iterator - Konzept bleiben

-- das heißt, beliebig oft yield, und Iterations-Ende per bool()-Test

Unter dieser Annahme kommt yield stets vor iterNext (wenn überhaupt).

Und yield muß (a) einen Status liefern, (b) einen Wert liefern.

Einziger Ausweg wäre, wie das IterAdapter macht, einen Pointer rauszugeben.

Das ist eigentlich keine gute Lösung, weil die Implementierung dann sehr tricky wird.

Siehe IterAdapter als abstoßendes Beispiel.

Und als weitere Alternative bleibt nur die Einführung von State, und das bedeutet,

sich im Iterator oder in der Implementierung irgendwo noch eine zusätzliche bool-Flag zu speichern.

implementieren ebenfalls expandChildren()

ganz anders als bei IterAdapter, wo das Sinn macht...

...ohne daß die Funktionen auch virtuell sind,

können wir nicht sicherstellen

  • daß die hereingereichte Implementierung die Funktionen überschreibt

Konzept funktioniert nicht

aus processing-Function

innen heraus

will sagen, das ist ja auch eine durchgeknallte Idee....

wenn eine Funktion in einem Layer expanded

...d.h, entweder man gibt aus dem Functor das zurück,

was vor dem expand anstand (=der Vater), oder man verwirft diesen

und liefert das, was nach dem expand erscheind

  • asIterSource() verwendet ein eigenes Interface, um diesen call an die Implementierung durchzureichen
  • in dieses Interface habe ich nun einen Rückgabewert eingebaut
  • damit kann ich das IterSource-Front-End refreshen
  • trotzdem hässlich...

an welcher Stelle wird diese Mechanik

an einen bestehenden Iterator angeschlossen

Stichwort HierarchyOrientationIndicator

...will sagen, bin nun schon mehrfach in dieses Problem gelaufen,

nachdem ich dachte, alles so schön gelöst zu haben.

Das Problem ist, daß eben auch der Konsument irgendwie

von den verschachtelten Strukturen mit gesteuert wird.

Das Ergebnis ist eben nicht rein linear.

...das ist nämlich der triviale Workaround

wir hatten bisher eine auto-Aufräum-Routine in iterNext(),

welche dazu führt, daß ein erschöpfter Vater sofort weggeräumt wird,

noch bevor wir dazu kommen, die Kinder zu pushen

jeder Zugriff auf ein Sub-Objekt muß durch eine VTable

Stichwort: virtual base offset

...da gibt es eine explizite Ausnahme-Regel.

Die Copy-Konstruktoren werden aus der Kandidaten-Menge entfernt.

Grund ist, daß die default-Initialisierung der Member-Felder noch nicht hinrechend geklärt war.

C++17 holt das nach

...dort wird einfach on-demand in der Basisklasse nachgeschaut.

Wenn dabei ein Basis-Copy-Ctor gezogen wird, dann wird eben default-Init für die Felder im abgeleiteten Objekt gemacht.

Es gibt dann eine neue, explizite Regel, die verhindert, daß zufällig ein aus der Basis geerbter Ctor

die Signatur eines Copy-ctors überdeckt

Allerdings genügt es, dies an einer Stelle in der Kette zu ergänzen

...und zwar genau dort, wo erstmals ein Basis-Objekt akzeptiert wird.

Das ist bei uns im BaseAdapter, also der ersten Ebene über dem zu initialisierenden Basis-Objekt.

Alle anderen Layer darüber reichen dann korrekt mit dem geerbten Ctor diese Initialisierung nach Unten.

übrigens ist es im IterSource<T>::iterator nicht  notwendig

...das war nur ein unnötiger Fix nach dem Gießkannen-Prinzip.

Denn dieser Iterator soll niemals mit einem Basis-Objekt initialisiert werden,

sondern stets von der IterSource-Builder-Funktion konstruiert.

Und wenn man selber keinen Ctor in eine Klasse schreibt, sondern nur ctor-erbt,

dann werden auch die Copy-Konstruktoren korrekt automatisch generiert.

Ticket machen: #1125

...daß man ein Ding komplett in einen Iterator packt,

und dieser es dann auch managed

ist das #190

...ist partiell diese Idee.

Nur auch das auf einem etwas anderem Level,

und immer mit einem Heap-allozierten vector

versehentlich wurde auch der an std::forward gegeben

habs mit FormatUtils_test bewiesen

Dazu in NumIter einen explizit tracenden move-ctor eingebaut

weil der Aufruf von join(&&) selber wasserdicht ist

D.h. er frisst keine Werte.

Deshalb fällt dieses doppelte Problem nicht auf

...weil man den konkreten Typ der Core kennen muß

verschiedendste Pipeline-Konstruktionen

können nun hinter dem gleichen Interface sitzen

rein ein Problem mit der Test-Fixture.

Da die Quelle nun von einem shared-ptr gehalten wird,

erzeugt eine Kopie des Iterator-Front-End

nun nicht mehr eine Kopie des ganzen Zustandes.

das wäre aber bequem für den Test.

Frage: ist das überhaupt eine gute Idee, vom Design her??

siehe std::shuffle

vorgegebene Zahlenfolge
in untendlichem Zufalls-Baum finden

man kann move(iter) verwenden,

wenn man konsumieren möchte

rLet(40878 < 18446744073709551615) → S

|↯| S ... 40878

rLet(40879 < 18446744073709551615) → F

|!| expand 40879

rLet(0 < 4) → A

rLet(40880 < 18446744073709551615) → Q

|.| A -->> 40879

|!| expand 40879

rLet(0 < 4) → F

rLet(1 < 4) → N

|.| F -->> 40879

|↯| F ... 40879

rLet(1 < 4) → W

|↯| W ... 40879-0-1

rLet(2 < 4) → N

|↯| N ... 40879-0-2

rLet(3 < 4) → T

|↯| T ... 40879-0-3

rLet(4 < 4) → F

|↯| N ... 40879-1

rLet(2 < 4) → F

|↯| F ... 40879-2

rLet(3 < 4) → A

|!| expand 40879-3

rLet(0 < 4) → J

rLet(4 < 4) → Y

|.| J -->> 40879-3

|↯| J ... 40879-3

rLet(1 < 4) → H

|↯| H ... 40879-4

rLet(2 < 4) → H

|↯| H ... 40879-5

rLet(3 < 4) → F

|↯| F ... 40879-6

rLet(4 < 4) → V

|↯| Q ... 40880

rLet(40881 < 18446744073709551615) → A

|↯| A ... 40881

rLet(40882 < 18446744073709551615) → X

|↯| X ... 40882

rLet(77943 < 18446744073709551615) → R

|↯| R ... 77943

rLet(77944 < 18446744073709551615) → X

|!| expand 77944

rLet(0 < 4) → U

rLet(77945 < 18446744073709551615) → I

|.| U -->> 77944-0

|↯| U ... 77944-0

rLet(1 < 4) → X

|!| expand 77944-1

rLet(0 < 4) → K

rLet(2 < 4) → Z

|.| K -->> 77944-1-0

|!| expand 77944-1-0

rLet(0 < 4) → V

rLet(1 < 4) → Y

|.| V -->> 77944-1-0-0

|↯| V ... 77944-1-0-0

rLet(1 < 4) → I

|↯| I ... 77944-1-0-1

rLet(2 < 4) → I

|↯| I ... 77944-1-0-2

rLet(3 < 4) → Z

|!| expand 77944-1-0-3

rLet(0 < 4) → Q

rLet(4 < 4) → X

|.| Q -->> 77944-1-0-3-0

|↯| Q ... 77944-1-0-3-0

rLet(1 < 4) → M

Protocol of the search: 77944-1-0-3-1

...weil es mutmaßlich

im realen UI in ähnlicher Form auch auftreten wird:

die Menge der Top-Level-Fenster ist eben etwas anderes,

als die Menge der Tracks in der Timeline.

Erst nach einer Transformation wird daraus eine Menge von Strings

Expand-Funktor hat einen Rückgabe-Typ

wenn es sie gäbe könnte man sie hier nutzen

...das heißt:

diese Struktur muß bereits beim Aufbauen des GUI

nebenbei mit aufgebaut werden, und über alle

mutierenden Aktionen hinweg automatisch konsistent bleiben

möglicherwese aber notwendig

...will sagen

wenn ich mir heute so die Situation vorstelle,

könnte es darauf hinauslaufen, daß man das braucht.

Und zwar, zumindest die Eigenschaft, von gegebenem Element

die Koordinaten zu ermitteln.

Das ist aber dann pratkisch auch schon eine "Up"-Funktion,

selbst wenn man sie nur indirekt implementiert

vorläufige MInimal-Lösung

bis jetzt kommen wir ohne Pos-Abstraktion aus

das Nav-Interface könnte daraus entstehen

es werden jetzt keine weiteren Features für TreeExplorer gebaut....

...mal sehen, ob wir jemals daran anstoßen...

gemeint ist,

zusätzlich zu dem Eintrag im Stack,

der ohnehin selbst Heap-alloziert ist

Was ist Nav und was ist Iteration-control?

Ist es sinnvoll, beide in einem gemeinsamen API zu haben,

oder delegieren wir besser?

Was sind die Kosten dafür?

IterSource muß insgesamt besser erweiterbar werden....

der Expander sitzt nun doch dahinter, in der Implementierung

das Ergebnis ist der konkrete Iterator-Typ

Wire-Tap-Implementierung

das heißt, depth ist aktuelle Tiefe!

ist keine Lösung

ist partielle Lösung

Ergebnis-Ausgabe ist die jeweilige mögliche Coverage

...insofern wir nur eine (partielle) Lösung signalisieren,

wenn wir einen direkten Match erziehlen.

Ein wildcard-Match führt nur dazu, daß wir zu den Kindern absteigen,

aber zählt erst mal für sich nicht als Lösung

  • nur für internen Gebrauch
  • protected im Builder

Lösungen müssen

am Ende des Patterns liegen

YAGNI

  • die Standard-Implementierung von std::swap macht einen Dreiecks-Move
  • wir haben effiziente Move-Konstruktoren

...und nicht den möglichen Zustand.

Denn für letzteren gibt es die "canXX"-Prädikate

macht es Sinn, dafür einen expliziten Testfall zu konstruieren,

oder verfangen wir uns da sofort zu sehr in der Implementierungs-Technik?

neue Einsicht 31.12.17

totale Coverage ist das, was man naiverweise erwartet.

Also sollte das auf dem API der default sein

...das heißt: keine Wildcards, keine pseudo-Specs (currentWindow)

Zweck ist vor allem, meta-Specs wie firstWindow, currentWindow aufzulösen

...verwendet einen GenNode-Tree

als Repräsentation des real-existierenden UI

Integration

Resolver / Navigator

nämlich in lib::meta::func::PApply::bindBack

// generic lambda, operator() is a template with one parameter

auto vglambda = [](auto printer) {

    return [=](auto&&... ts) // generic lambda, ts is a parameter pack

    {

        printer(std::forward<decltype(ts)>(ts)...);

        return [=] { printer(ts...); }; // nullary lambda (takes no parameters)

    };

};

Vermutung: muß Lambda instantiieren...

...merke

die Spezialbehandlung für const& gilt nur, wenn wir direkt auch diesen Typ nehmen.

Im vorliegenden fall wird aber der conversion-Operator aufgerufen, um den Initializser zu erzeugen.

Daher denkt der Compiler, er kann das Ursprungsobjekt jezt wergwerfen.

Spezialbehandlung

Perspektive

...um zu prüfen, ob das allgemeine Design

mit solchen Asymetrien umgehen kann,

welche ziemlich sicher noch viel mehr

bei der Navigation in einem realen GUI auftreten

...und ich hab mir letzte Woche noch solche Vorwürfe gemacht,

daß ich mich wieder mal "akademisch" verspielt habe.... :-P

...und die IterSource dann nur über WrapIter definieren.

Schichten-Prinzip...

das ist immer schon korrekt erledigt

...und das heißt.

ein Value wird auch sofort konstruiert,

egal, ob man den dann gleich wegwirft.

  • wird mäßig häufig aufgerufen
  • beim "Öffnen" und zur Navigation
  • im Interaktions-Kontext
  • keinen Speicherdruck erzeugen

...es ist im Rahmen;

denn wir akzeptieren double dispatch sogar in der Diff-Anwendung,

welche viel häufiger läuft, als dieser View-Zugriff hier.

Allerdings, die doppelte Indirektion ist nicht grundsätzlich notwendig hier,

da wir nur einen einzigen Anwendungsfall haben. Die zweite Indirektion in jedem Aufruf

bewirkt nur eine Entkoppelung vom Implementierungs-Kontext

wir brauchen keine Token

....anders als im Diff-Framework

senden wir hier keine beliebigen Nachrichten,

sondern interpretieren jeweils nur eine einzige feste Konfiguration

Allokator pro Typ

...wir brauchen eine Repräsentation,

um auszudrücken, daß gewissen Angaben ausgelassen wurden

Alternative: wrap UI-Coord,

thin autmentation layer

alloc = unlimited

locate = panel(timeline)

alloc = onlyOne

locate = external(beamer)

               or perspective(mediaView).panel(viewer)

               or existingPanel(viewer)

               or firstWindow().panel(viewer)

alloc = limitPerWindow(2)

locate = perspective(edit).existingPanel(viewer)

               or currentWindow().existingPanel(viewer)

               or existingPanel(viewer)

               or panel(viewer)

im Asset-Panel der jeweiligen Gruppe hinzufügen

alloc = unlimited

locate = currentWindow().perspective(edit).existingPanel(asset).existingGroup()

               or perspective(asset)panel(asset)

               or firstWindow().panel(asset)

alloc = limitPerWindow(1)

locate = currentWindow().existingPanel(infobox)

               or firstWindow().panel(infobox)

Voraussetzung: Anwendbarkeit erkennen

Frage: wieviel Interaction Control

müssen wir sofort jetzt implementieren

brauche ein aktuelles Modell-Element

Problem: Zusammenarbeit

mit docking panels

Grundlagen für InteractionControl

wird der Link zwischen CoreService und UI-State dangling

...mit der Ausnahme des Automatismus,

der es selbst vom Bus abkoppelt

in generischer UI-Struktur bewegen

...denn das ist das vereinfachte Setup für "einfache" Applikationen

muß kein Manager sein

Abstraktion zur Steuerung schaffen

wie bestimmt?

das Diff wird auf den Platzhalter angewendet

wenn das Diff ein Element aus einer Kind-Menge wegfallen läßt,

dann muß dieses automatisch deregistriert werden

vermittelt über den ViewLocator (InteractionDirector)

Brücke: gemeinsamer Controller

sets für eine feste session::Timeline

Verwaltung autmatisch via ViewLocator -> PanelLocator

es gibt eine EmptyTimeline

Frage ist, wie viel des Verhaltens programmieren wir selber explizit aus,

und welchen Teil des Verhaltens überlassen wir GTK

Das war zwar schon meine Bauchgefühl,

habe aber sicherheitshalber diese Analyse nochmal gemacht.

Details im  TiddlyWiki....

braucht feste Speicher-Addresse

..d.h. der Controller muß wieder auf das Widget zugreifen

und sei es auch bloß über ein Interface!

aber: Binding im Diff-System durchaus möglich

...denn:

das Diff-System verlangt nicht, daß Kinder in der Collection auch Tangible sind.

Es verlangt nur

  • daß wir wissen, wie wir Kinder machen
  • daß wir für ein gegebenes Kind ein DiffMutable beschaffen können

Problem: Slave-Timeline

grundsätzliches

Problem

speziell die Umordnungen ergeben sich

...und der Dekorator würde die beobachteten Operationen

an diese Notifikations-Schnittstelle senden.

Implementiert würde sie vom jeweiligen Widget

korrekt wäre, die Diff-Verben mitzulesen.

Das geht aber nicht, weil wir intern (aktiv) iterieren.

Wollten wir das doch, müßten wir das gesamte Diff-Applikator-Design wegwerfen.

Da aber eigentlich eine 1:1-Zuordnung zwischen Diff-Verben und Operations-Primitiven besteht,

könnte man trotzdem (mit etwas Hängen und Würgen) noch hinkommen.

Der Dekorator würde also auf dem TreeMutator sitzen...

Weil wir die "skip"-Operation für zwei Zwecke verwenden,

und man im Skip nicht weiß, ob man das Element überhaupt noch anfassen darf,

denn es könnte ja auch ein von "find" zurückgelassener Müll sein.

Daher gibt es die matchSrc-Operation. Effektiv wird die aber nur bei einem Delete aufgerufen...

  • man sitzt mit dem Detektor unter dem API
  • dadurch entstehen "ungeschriebene Regeln", wie das API auzurufen ist
  • alternativ könnten wir die Operationen komplett 1:1 definieren, also eine explizite delete-Operation einführen
  • dafür würde dann die matchSrc wegfallen, was praktisch alle sinnvollen Unit-Tests stark beschränkt.

nach der Mutation erfolgt Display-Neubewertung

interagiert mit den Presentern

d.h. eine LUID

wir lassen es offen, welche Art von ID das ist.

Irgend eine BareEntryID genügt

...abstraktes Interface

latürnich

...den muß jeder individuell implementieren,

um die Bindung herzustellen

theoretisch könnte man eine Timeline ohne Sequenz

oder eine Sequenz ohne root-Fork zulassen

Thema "Darstellung von Objekt-Feldern im Diff"

da hab ich mir ausgiebig Gedanken darüber gemacht (in dieser Mindmap)

  • entweder ein Feld ist wirklich optional belegbar, dann kann es mit dem Diff kommen
  • wenn dagegen ein Feld zwingend befüllt sein soll, muß man das über den Konstruktor erzwingen
    in diesem Fall müssen alle Daten bereits mit dem vorangehenden INS kommen,
    welches den Konstruktor-Aufruf auslöst

die betreffenden Felder sind echt optional.

Der Ctor belegt sie mit einem sinnvollen Leerwert

Das Objekt muß so geschrieben werden, daß es mit den Leerwerten umgehen kann,

was typischerweise heitß, daß es verschiedene Betriebsmodi bekommt.

Das Diff kann dann später die konkreten Werte für die Attribute nachliefern;

typischerweise wird es das in einem Populationsdiff sofort als Nächstes machen.

zwei mögliche

Konsequenzen

funktioniert fast immer

"was kann denn schon passieren??"

Betriebsart "partiell initialisiert"

..hier das Widget, das ebenfalls

  • nur partiell aufgebaut existieren können muß
  • später sich dynamisch erweitern können muß
  • in der Behandlung der UI-Signale ebenfalls checks einbauen muß

einen Fall, der praktisch nie auftritt

und zwar interessanterweise über Kreuz gegliedert

  • die Ctor-Lösung (hat aber etwas mehr Umsetzungsaufwand)
  • die "wird schon klappen"-Lösung

wenn alle Objekte wirklich auf partiell initialisierten Zustand vorbereitet sind,

und auch über ihre APIs dem Nutzer diese Unterscheidnung mit aufzwingen

...welche darin besteht,

daß man überall, in der Fläche, sich um Zustandsabhöngigkeit kümmern muß,

und deshalb dazu neigt, das Problem jeweils wegzutricksen.

Es besteht also die große Gefahr, zu "sündigen" und

heimlich in den "wird schon nix passieren" Fall zu geraten.

das heißt, nur diese Lösung gründet in der Natur der behandelten Sachverhalte.

Wenn etwas seinem Wesen nach nicht optional ist, dann wird es auch nicht optional behandelt.

Es ist keine weitere Argumentation notwendig.

...nach allen gängigen Prinzipien der instrumentellen Vernunft.

KISS

YAGNI

"fokussiere Dich"

hier hab ich endlich mal die Gelegenheit, sauber zu arbeiten

hey, es ist mein Leben

...hab ich mich je anders entschieden?

wenn ich mich überhaupt entscheiden konnte...

...nochmal zusammengefaßt

  • immer wenn ein Feld seinem Wesen nach zwingend gesetzt sein muß (und aus keinem anderen Grund)
  • dann wird dies per Konstruktor so erzwungen
  • daher muß dann im Diff bereits im INS-Verb die notwendige Information transportiert werden
  • das heißt, bei der Diff-Erzeugung muß man aufpassen und an dieser Stelle bereits einen Record mit den Daten liefern

wie in Kopf und Rumpf injizieren

...sie verwenden dann ein LabelWidget zur Darstellung

Ein Clip hat verschiedene Erscheinungsformen im UI

Verwende das als Leitgedanke, um das Layout zu entwickeln

UI-Bus gilt nur für globale Belange

es geht nur um Rollen

das lokale Element muß nur als View fungieren

kann sich selbst

transformieren

...um mal was im UI anzeigen zu können

...wird zwar vom Skript ausgelesen,

aber nicht weiterverwendet.

Die Icon-Größen ergeben sich aus den Boxes auf 'plate'

Docks enthalten Component Views

...nur enabled wenn

mehr als ein top-level Fenster offen

A Gtk::UIManager constructs a user interface (menus and toolbars) from one or more UI definitions,

which reference actions from one or more action groups.

realisiert Vererbung zu fuß

...anstatt eine auf den konkreten Typ getemplatete Subklasse zu verwenden,

wird eine "CreatePanelProc" in einen PanelDescriptor eingewickelt.

Letzten Endes wird dieser dann per Match auf die Typ-ID ausgewählt.

AUA!

wie komme ich da drauf?

Ich wollte untersuchen, ob Gtk::manage( ptr ) korrekt die übergebenen Objekte aufräumt.

Wie sich nun zeigt, passiert das Aufräumen im dtor desjenigen Widget, dem das zu managende Objekt als Kind gegeben wurde.

Im vorliegenden Fall wäre das der dtor des umschließenden ScrolledWindow. Der aber wird offensichtlich nicht aufgerufen,

auch nicht im Application-Shutdown!

....erzeugt wird das hier:

dock_.add_item(timelinePanel->getDockItem(),Gdl::DOCK_BOTTOM);

Helper to build the menu and for registering and handling of user action events

es sieht so aus, als wäre es "das" WorkspaceWindow

aber es kann davon mehrere geben

innere

Struktur

heißt: Element registriert sich am UI-Bus

heißt: Element deregistriert sich am UI-Bus

...ist immer ein tangible

presentation

state

vom tangible initiiert

dafür genügt der normale Reset

mark "clearMsg"

mark "clearErr"

mark "reset"

Nachricht an irgend ein Wurzel-Element

generisch

sinnvoll?

was haben alle UI-Elemente wirklich gemeinsam?

die Frage ist, wie generisch ist eigentlich ein Command-Aufruf selber?

Macht es daher Sinn, ein generisches API allgemein sichtbar zu machen,

oder handelt es sich nur um ein Implementierungsdetail der UI-Bus-Anbindung?

...wird sinnvoll im Rahmen von InteractionControl

ich wollte explizit kein generisch-introspektives UI,

weil das die Tendenz hat, sich zu einem Framework auszuwachsen.

Für die UI-Programmierung muß man Spaghetticode akzeptieren.

gemeint, eine ENUM von verschiedenen Graden der Aufgeklappt-heit

Dann mußte das allerdigns jeweils für alle Elemente sinnvoll sein

und der muß vom konkreten Widget implementiert werden

dann wird eine state mark ausgesendet

need to bubble up

support ist optional

nach Broadcast von "reset"

sollte logischerweise der PresentationStateManager leer sein

ist er aber nicht notwendig,

denn er kann Zustand von nicht mehr existierenden Elementen aufgezeichnet haben.

Nur Elemente, die im Moment angeschlossen sind, bekommen die "reset"-Nachricht mit;

sofern sie tatsächlich abweichenden Zustand haben, sollten sie sich resetten

und eine state mark "reset" zurückschicken...

....so harmlos hat alles angefangen

Denn das heißt, ich muß konkret ausarbeiten,

wie man einen Diff gegen eine opaque Implementierungs-Datenstruktur aufspielt.

Und ich muß das in einem Test zumindest emulieren können!

muß DiffApplicationStrategy

noch einmal implementieren

das mag überraschen --

ist aber im Sinne des Erfinders

  • DiffApplicationStrategy war von Anfang an als technisches Binding konzipiert
  • es ist besser, die gleiche Semantik der Sprache X-mal herunterzucoden
  • cleverer Code-re-Use zahlt sich i.d.R. nicht aus

dies setzt volle Implementierung

des Tree-Mutators voraus

der schwierigste Teil, das Mutieren von Attributen,

ist jedoch schon prototypisch implementiert

Mutator verwendet einen Binder

Diff kennt keine Zuweisung

Nein

aber was dann wenn out-of-order

eindeutig überlegen

  • faktorisiert sauber
  • Zustand delegiert auf die jeweilige Kinder-Sammlung
  • diese wird damit auch zum generischen Element

schlechter....

  • sammelt viel technische Komplexität auf top-level
  • wir müssen eine meta-Repräsentation aufbauen
  • wir müssen Adapter zentral generieren, anstatt uns vom Installieren von Closures treiben zu lassen

Primitive

(impl-ops)

of questionable use

with multiple layers

since skipSrc performs both the `del` and the `skip` verb, it can not perform the match itself...

...because it is also used to discard garbage after a findSrc operation.

Thus we need to avoid touching the actual data in the src sequence, because this might lead to SEGFAULT.

For this reason, the implementation of the `del` verb has to invoke matchSrc explicitly beforehand,

and this is the very reason `matchSrc` exists. Moreover, `matchSrc` must be written such

as to ensure to invoke the Selector before performing a local match. And skipSrc has to

proceed in precisely the same way. Thus, if the selector denies responsibility, we'll delegate

to the next lower layer in both cases, and the result and behaviour depends on this next lower layer solely

then move into target

since, on interface level, we're pretending that this mutator is a single collection like thing,

while in fact the implementation might bind to several opaque target structures.

Thus, internally we'll have a selector to determine which onion layer is responsible for

handling an element as designated by the argument. It is then the responsibility

of this specific onion layer to accept forward until meeting this element.

warning: messed-up state in case of failure

this is (probably) the only operation which entirely messes up the mutator state

when the designated target does not exist. The assumption is that a diff application front-end

will check the bool return value and throw an exception in that case

move into target

throw when

insufficent space

...in Fällen, in denen der konkrete onion-layer

überhaupt nicht im Stande ist, das zu beurteilen.

Wichtigster solcher Fall ist die Bindung auf Objekt-Felder

invoke mutateChild

NOTE: mutator need to be written in such a way

to be just discarded when done with the alterations.

That is, the mutator must not incorporate the target data, rather it is expected

to construct the new target data efficiently in place.

Mutator enthält die Bindung auf die konkreten Daten

stellt sich u.U erst während der Verarbeitung heraus:

bei "offenen Datenstrukturen" entscheided jeder Typ selber,

welchen Mutator er erzeugt

aber: Aufrufprinzip

Verb muß den

Diff bekommen

und delegiert iterativ

an die Verben

...denn in dem Moment, wo wir den top-level TreeMutator erzeugen,

können wir rekursiv abfragen, wie groß alle möglichen Kind-Mutatoren werden können

nur Zuweisung einiger Referenzen

....denn der liegt (mind) einmal vor,

eingebettet in ein Selektor-Prädikat,

welches bestimmt, ob dieses Attribut angesprochen wird

was man konventionellerweise auch macht.

Ich verstehe nun, warum. Es ist der vernünftigste Weg.

Leider scheidet das aber für uns hier genau aus,

denn das gesamte Projekt entstand, aufgrund der inhärenten Limitierungen

der "vernünftigen" (=pragmatischen) Lösung.

dieser Ansatz löst tatsächlich das Problem,

aber zu dem Preis, daß er die Strukturen von innen her zersetzt.

Auf lange Sicht wird das System wuchern wie ein Krebsgeschwühr,

und man kann das nur mit Disziplin eindämmen, was realistisch gesprochen meint,

daß es vergeblich ist. Einen Kampf gegen das Menschliche, Allzumenschliche kann man nicht gewinnen.

das ist die schlankeste Lösung, die ästhetisch befriedigt.

Sie hat aber das Problem, daß dadurch die Kollaboration im Kern ausgelöscht wird.

Wir haben eine Seite, die absolute Macht hat, und einen "Partner", der tatsächlich nur ferngesteuert ist.

Wir müssen dafür auf die Subsidiarität verzichten, und damit auf die Möglichkeit zur Entkoppelung.

Dazu kommt, daß die notwendige Fern-Wirkung stets eine zusätzliche Last bedeutet.

Denn wir müssen auf Umstände und Strukturen einwirken, die von dem Ort, an dem die

Steuerung stattfindet, entfernt ist, entfernt in einen anderen Kontext.

und nur letztere sind tangibel

um den Binde- bzw. Anknüpfungs-Punkt in den real-Daten überhaupt zu finden,

müssen IDs aus dem DOM innerhalb der real-Daten nochmal wiederholt, also redundant vorliegen

damit das DOM ein echtes DOM ist, muß es die relevanten real-Daten duplizieren,

um sie in einem abstrahierten Kontext zugänglich zu machen

noch zusätzlich zur genannten Duplikation muß

die Abblidung der Strukturen aufeinander

an irgend einer Stelle repräsentiert werden.

man kann versuchen, die beiden Elemente der Duplikation aufzulösen.

Allerdings gibt es dafür überhaupt nur zwei mögliche Richtungen.

  • man löst die Parallel-Strukturen auf
  • man ersetzt das DOM durch reine Bindungs-Strukturen

Beide Ansätze laufen aber auf eine der schon genanten, anderen Alternativen hinaus.

Wenn man die Parallel-Strukturen beseitigt, enden wir bei irgend einer Form von Fernsteuerung.

Wenn man die Modell-Natur aus dem DOM entfernt, das heißt, dort nur noch reine
Binde-Strukturen speichert, dann endet man bei einer Form von Introspektion. Entweder,

das Rückgrat und die Navigation verbleibt bei dieser Introspektion; dann haben wir eines

der typischen Objekt-Systeme. Oder die Binde-Daten werden zu einem reinen Anhang

an eine selbständig bestehende Datenstruktur; dann enden wir bei klassischer Introspektion.

reflektiert die Zahl der Struktur-Element

...will sagen:

für die habe ich bereits eine effiziente Implementierung,

die darauf beruht, den Content beiseite zu schieben.

Ich brauche also nur ein Container-Frontend (z.B. einen Vector ohne Inhalt) zusätzlich,

um den verschobenen Inhalt erst mal aufzunehmen.

Also zählen Kinder-Collections nur als ein Strukturelement.

rekursiv,

duch Bindung bestimmt

das ist der wesentliche Kniff,

durch den das Problem mit der "absrakten, opaquen" Position entschärft wird

  • Diff-Anwendung wird massiv und in der Breite stattfinden
  • sie wird als Reaktion auf UI-Events auftreten
  • sie dient dazu, andere UI-Operationen einzusparen
  • also muß speziell das Traversieren bis an den Anwendungsort bedacht werden

...d.h. die bis jetzt geschriebene TreeApplikator-Implementierung

ist erstaunlich leichtgewichtig. Zu den zwei Indirektionien der Sprache

kommt nur entweder ein weiterer aus der GenNode bzw stattedessen ein dynamic cast hinzu.

Alles andere steckt in dem expliziten Mutator-Typ

 -- das gibt einen wichtigen Hinweis --

...da wir eine verb-basierte Sprache implementieren,

also einen double-dispatch haben

weil wir den Anwendungs-Kontext noch überhaupt nicht kennen.

Man könnte also später, wenn das ganze System "steht",

das Diff-System noch einmal reimplementieren, dann mit einem vorgegebenen Diff-Typ

Beschluß: akzeptiert

im Sinn von "polymorpic value" ist das Backend virtuell

....wenngleich auch dieser aus einem Template generiert wird

(will sagen, es ist nicht sofort offensichtlich, daß wir jeweils einen Interpreter generieren)

wir verzichten auf Introspektion der Elemente

denn genau zu diesem Zweck haben wir die "External Tree Description"

...d.h,

kann zusätzlich zu einem anderen Adaptor

in die Mutator-Dekorator-Kette gehängt werden

und protokolliert somit "nebenbei" was an Anforderungen an ihm vorbeigeht

streng genommen ist es nur erlaubt, das ID-Symbol auszuwerten

Visitor bedeutet zwei Indirektionen

...und das ist nicht akzeptabel für ein reines Selektor-Prädikat!

denkbar nur bei Sub-Objekten

gilt für alle praktischen Anwendungen

....auch wenn man zehnmal meinen könnte,

Kinder eines reinen Wert-Typs wären sinnvoll --

sie sind es nicht!

Jede sinnvolle Entität hat mehr als ein Attribut!

denn es macht keinen Sinn, Entitäten und reine Wert-Elemente

auf der gleichen Ebene in der gleichen Sammlung zu mischen.

D.h., entweder man hat ein Objekt, das als Kinder z.B. eine Liste von Strings hat,

oder man hat eine Entität, die z.b. zwei getypte Objekt-Kinder-Sammlungen hat,

wie z.B: eine Spur mit Labels und Clips

"target matches spec"

aber existiert nominell und kontext-abhängig

das sind die konkreten Implementierungen

für spezifische Arten von Bindings

kann niemals geschachtelte sub-Mutatoren modellieren

ja wirklich, das wäre nicht sinnvoll!!!!!

auch wenn man meinen könnte, es geht.

Grund ist nämlich, es kann jeweils nur ein Onion-Layer für ein gegebenes Element "zuständig" sein.

Und aus Gründen der logischen Konsistenz darf dieser Diagnose-Layer niemals für ein Element zuständig sein,

denn sonst würde er es für darunter liegende Layer verschatten.

immer Mitwirkung des Elements

weil beim Assignment die Spec (=GenNode) eben

zwar die ID des Zieles, aber den neu zuzuweisenden Wert enthält.

Also wird sich das Ziel nicht anhand des neuen Wertes finden lassen,

weil es eben grade noch nicht diesen neuen Wert trägt.

generische Repräsentaton ist so gewählt,

daß sich alle relevanten Eigenschaften darstellen lassen

wenn also ein Teil der diff-Funkttionalität nicht verfügbar ist,

dann wird es wohl so sein, daß sie auch nicht gebraucht wird

zwar erscheint es nicht sonderlich sinnvoll,

als target auch eine Menge von primitiven Werten zuzulassen.

Es gibt aber auch keinen wirklichen Grund, dies zu verbieten,

sofern es gelingt, die Funktionalität gutmütig zu degradieren.

...will sagen,

da sind mehrere Layer an praktisch ungebundenem Template-Code dazwischen,

so daß zu befürchten steht, daß ein unpassendes Lambda erst weit entfernt

eine womöglich irreführende Meldung generiert

erfordert wirklich Kooperation

...denn wir verwenden hier als "private" Datenstruktur

eine etwas komische Collection von Strings,

in die wir die String-Repräsentation der Spec-Payload schreiben.

In der Praxis dagegen würde man wirklich einen privaten Datentyp verwenden,

und dann auch voraussetzen, daß man nur Kinder dieses Typs (oder zuweisungskompatibel) bekommt.

Mein Poblem hier ist, daß ich in dieser Demonstrations-Datenstruktur keine nested scopes repräsentieren kann.

Aber hey!, es ist meine private Datenstruktur -- also kann ich einfach eine Map von nested scopes

daneben auf die grüne Wiese stellen. Ist ja nur ein Test :-D

...dankenswerterweise hat der subscript-Operator von std::Map

die nette Eigenschaft, beim ersten Zugriff auf einen neuen Key

dessen payload per default-konstruktor zu erzeugen.

der Builder in der nested DSL generiert einen sonderbar falschen "this"-Typ,

genauer gesagt, eine TYPID die falsch ist.

Und zwar kommt es da zum "Übersprechen" von einem Typ-Parameter in den anderen.

Im Besonderen hab ich beobachtet, daß, wenn man auf den 3.Typparameter ein Lambda gibt,

dann auf dem 4. oder 5. Typparameter der bisherige /alte Typ des 3.Typparameters auftaucht,

u.U auch eingeschachtelt als ein Argument.

Habe mich aber davon überzeugt, daß die eigentlichen Typ-Parameter in Ordnung sind.

Und zwar habe ich das verifiziert

  • durch Ausgeben der Typen im Konstruktor (mithilfe meiner typeStr<TY>()
  • durch Einbauen einer Static-Assertion mit Signatur-Match

gemeint ist:

die native Datenstruktur ist eine Collection von Elementen,

welche ohne Weiteres direkt in eine GenNode gepackt werden könnten. Denn dann läßt

sich eine einfache Default-Implementierung des Matchers angeben

Typisches Beispiel: eine STL-Collection von Strings.

wir integrieren Attribute nicht, weil es so schön symmetrisch ist,

sondern weil sie essentiell zum Wesen von Objekten gehören.

Wenn wir Änderungen an Objekt-Strukturen als Diff erfassen wollen,

dann müssen Attribute irgendwie sinnvoll integriert sein

immer in der Klasse verankert

⟹ es geht eigentlich nur um den Wert des Attributes

manche Felder sind optional

unter der Maßgabe,

wie ETD ein Objekt repäsentiert

"Anwendung" : meint das Anwenden eines Diffs auf ein Ziel-Objekt

"nicht nutzen" : meint ignorieren und verwerfen der Information

meint: ETD -> Objekt und dann später Objekt -> ETD

warum?

Weil sich in der ETD die Reihenfolge ändern kann,

und aber das Aufspielen eines Diffs auf beiden Seiten

zwingend die gleiche Reihenfolge erfordert!

Objekt -> ETD -> Objekt

warum?

weil das Quellobjekt keinen Diff erzeugen wird,

der sich letztlich nicht auf das Zielobjekt aufspielen läßt

abweisen, was das Kriterium sicher verletzt

mandatory : Wert muß per Konstruktor gegeben sein

default : es gibt einen ausgezeichneten Standardwert

das heißt, in dem ins-Verb ist dann ein komplettes Objekt enthalten,

nicht nur eine leere Record-Hülle, die nachfolgend populiert werden kann (aber nicht muß)

Konstruktor befüllt das Feld halt irgendwie.

Ab dem Punkt verhält es sich aber wie ein normales (mandatory) Feld

das Objekt selber kann erkennen, ob das Feld sich im "default-Zustand" befindet

ohne Prüfen ist emptySrc nicht implementierbar

...weil es für emptySrc keine neutrale Antwort gibt.

Denn dieses Prädikat wird von der typischen Implementierung des Diff-Applikators

in beiden Richtungen verwendet, also sowohl Prüfung auf empty ("expect no further elements"),

alsauch der Check, daß überhaupt noch Quellelemente anstehen

d.h., man kann nur global auf Prüfung verzichten 

und da habe ich mich bereits dagegen entschieden

Feld unterstützt default-Wert

Auslegung der

Primitiven

rationale: object fields are hard wired,

thus always available

Einschränkung: accept_until END

...nämlich indem alle Attribute als "berührt" und akzeptiert markiert werden.

Somit könnten sofort Zuweisungen als Nächstes passieren

analog wie assignElm

das heißt, es findet keine Verifikation statt

zu bindende

Operationen

sieht nach Ober-engineering aus,

zumal das erhebliche Statefulness bewirkt

unterstelle Ziel als konstruierbar aus Payload

da effektiv bereits der Setter diese Funktionalität enthalten kann und muß,

denn der Setter nimmt eine GenNode

injectNew tolerieren

....man könnte genausogut auch beim ersten Mal zuweisen

denn die Diff-Anwendung auf GenNode unterstützt Zuweisung

ausschließlich bei schon existierenden Elementen. Demnach muß dort auch jedes Attribut

  • entweder schon mit dem Konstruktor mit gegeben worden sein
  • oder vorher einmal explizit eingefügt

...denn wir vermeiden dadurch Komplexität.

Der gesendete Diff muß einfach passen!

Genau deshalb haben wir auch in GenNode verschiedene Varianten des gleichen Grundtyps,

damit wir nicht in die ganzen Ungewissheiten der widening conversions laufen!

d.h. der Attributwert hat Wertsemantik und wird einfach zugewiesen

...d.h. der Attributwert ist ein Objekt und damit ein nested Scope

Problem: immutable values

das alles passiert dann im Lambda

Dilemma: defaultable fields

....mit der ETD,

bzw der Anwendung des selben Diffs auf eine GenNode-Struktur.

Konsequenz: wenn ein feld defaulted war, und nun explizit gesetzt wird,

muß dies als INS geschehen, denn eine Zuweisung an nicht aufgeführtes Element ist verboten

folglich ein Problem,

zu erkennen, wenn wir fertig sind

....weil das defaultable field noch nicht vom Diff berührt wurde.

Aber es ist kein optional field, d.h. wir haben keine Flag, die es als "defaulted" kennzeichnet

Lösung: alles immer explizit

diese Lösung war zunächst mein Favorit.

Sie erscheint sehr elegant, weil man im TreeMutator überhaupt nichts dafür tun muß.

Und die Zusatz-Forderung, daß dann eben das Diff richtig gesendet werden muß,

erscheint "geschenkt", da wir ohnehin zunächst einmal die Diffs explizit im Code erzeugen.

Aber, nach längerer Überlegung wurde mir der Ansatz mehr und mehr zweifelhaft.

Das ist die Art von Verkoppelungen, hier die implizite Annahme einer bestimmten Implementierung,

die ein System unerklärbar und schwer wartbar machen. Das ist die Art von "Features",

für die man sich nach einiger Zeit entschuldigen muß.

Und noch schlimmer: eigentlich läuft dieser Ansatz darauf hinaus, die Konsistenzprüfung

am Ende zu deaktivieren. Nur wir machen das nicht explizit, sondern durch die Hintertür.

Also dann besser klar und ehrlich!

...denn unter dem Strich würden wir hiermit volle Unterstützung für opitonale Attribute einführen,

also eine Attribut-Semantik auf eine Feld-Semantik draufpflanzen.

Aber in der vorausgegangenen Analyse habe ich mich schon davon überzeugt,

daß wir keine Attribut-Semantik brauchen. Und wenn doch, dann bietet das Diff-System

immer noch die Möglichkeit, die Attribute explizit als Sammlung darzustellen.

auf die empty-Prüfung am Ende verzichten

denn in den meisten, wichtigsten Fällen get es um einen non-empty-check,

bevor ein anderes Verifikations-Prädikat angewendet wird.

jedwede "bessere" Implementierung muß zwingend einen Container verwenden,

der dann die Lambdas für die einzelnen Setter auf den Heap legt.

Das ist hier tatsächlich viel schlechter, als das bischen lineare Suche

....durch meinen allerersten Draft,

für den ich damals gezwungen war, die GenNode zu erfinden :)

gleiches Argument...

...damit unterstellen wir, daß später eine Symbol-Tabelle aufgebaut wird.

Dann kann man sich immer noch überlegen, ob man dann an dieser Stelle bereinigt

in einem Fall kann man sie aus der Closure abgreifen

im anderen Fall muß es doch der Client leisten.

Keine klare Linie

...das heißt, es gibt nur minimale, themantische Überlappung.

Also ist die Verwendung von Vererbung hier sogar die beste Lösung

Geschachtelte Typdefs lassen sich vermeiden:

BareEntryID speichern!

...das heißt, wie rum man es auch auflöst, wird die Lösung auf einer Seite schlechter

  • wenn wir für den Payload-Typ einen Typ-Parameter nehmen, blähen wir den Standard-Fall (Setter) auf
  • andererseits ist es unbstreitbar einfach so, daß für den Mutator-Builder die Typisierung komplett implizit ist, das muß die Closure mit sich selbst ausmachen, einfach indem in der Closure ein geschachtelter TreeMutator konstruiert wird, der eben mit diesem impliziten Kind-Typ umgehen kann.
  • wenn wir stattdessen nur einen Key-String speichern, wird zum Einen die Match-Prüfung aufwendiger (Stringvergleich statt Vergleich von Hashes), und außerdem wird ein Typ-Mismatch nicht mehr auf der Ebene der Verb-Anwendung entdeckt und entsprechend gekennzeichnet, sondern wir hoffen, daß es dann innerhalb der Closure zu einem Fehlzugriff auf die Payload der GenNode kommt. Noch schlimmer im Mutator-Fall, da sind wir dann schon im geschachtelten Scope und hoffen, daß dann der eingeschachtelte Mutator irgendwo auf Widerspruch läuft.

...gedacht für verschiedene UseCases.

  • Fall 1: String-Key und der Typ muß irgendwie implizit/explizit gegeben sein
  • Fall2: GenNodeID

...die offensichtlichsten Dinge übersieht man nur zu leicht!!!!!

Da es ein nested scope ist, ist es immer ein Objekt,

also repräsentiert als Rec<GenNode>

zwei Bindings

zwei Collection-Bindings

...diese Abkürzung ist nur auf den Konstruktur aufgepflanzt,

nicht aber in der eigentlichen Implementierung verankert.

Das wollte ich nicht, weil ich längerfristig doch davon ausgehe,

daß es einfach einen Metadaten-Scope gibt

Die Inkonsequenz nun ist, daß im Rec::Mutator keine Magie dafür vorgesehen ist

...mit den Lambdas kann ich nur die Sicht auf die Werte steuern,

nicht aber das eigentliche Verhalten des Bindings.

Denn die Lambdas haben keinen Zugriff auf die Ziel-Datenstruktur!

...wir wollen mehrfach geschichtete TreeMutator-Subklassen,

aber tatsächlich liefert jeder DSL-Aufruf einen Builder<TreeMutator<...>>.

Die normalen DSL-Aufrufe sind eben genau so gestrickt, daß jeweils der oberste Builder entfernt wird,

ein neuer Layer darübergebaut und das Ganze wieder in einen Builder eingewickelt wird.

Dadurch ist es schwer bis unmöglich (wg. den Lambdas), den resultierenden Typ anzuschreiben.

Daher bin ich zwingend auf Wrapper-Funktionen angewiesen, die diesen resultierenden Typ

vom konkreten Aufruf wieder "abgreifen". Ich kann daher nicht die DSL-Notation verwenden,

um den Dekorator für die Behandlung des Typ-Feldes einzubringen.

Mut -> Rekursion

Problem: partielle Ordnung

...das heißt,

das AFTER-Verb wird übersetzt in ein skip_until,

und das läuft dann entweder in jedem Layer

oder nur in dem Layer, der auf die Spec paßt.

In jedem Fall gerät dadurch die relative Verzahnung der Elemente untereinander aus dem Takt

...das heißt also, es wird stets der zuerst gebundene Layer komplett durchgespult,

gefolgt dann von dem nächsten Layer.

Die Konsequenz ist, daß es keine Mischung der Typen geben kann.

Es müssen immer zwingend alle Elemente eines Typs von einem Layer behandelt werden

und diese Elemente müssen geschlossen hintereinander in der Reihenfolge liegen

auf Basis des neu geschaffenen TreeMutators

....man könnte später geeignete Automatismen schaffen,

die sich diesen TreeMutator beschaffen

  • indem erkannt wird, daß das eigentliche Zielobjekt ein bestimmtes API bietet
  • indem andere relevante Eigenschaften des Zielobjekts erkannt werden

...das so häufig in C++ auftretende Problem:

wie baue und verwalte ich eine konkrete Implementierung,

ohne gleich ein ganzes Management-Framework einführen zu müssen.

Letzten Endes lief  das auch in diesem Fall auf inline-Storage hinaus...

....daß ein unbedarfter client diesen Trick übershieht

und daher den Rückgabewert wegwirft.

Argument: we soweit einsteigt, die Metaprogramming-Lösung zu nutzen,

sollte auch intelligent genug sein, die API-Doc zu lesen.

Standard == Interface DiffMutable implementieren

Client soll direkt mutatorBinding bieten

nicht generisch: mutatorBinding

Lösungsversuch: extern template

...im Klartext: diesen Zugriff von der generischen Implementierung

auf den eingebauten Stack-Mechanismus benötigen wir nur...

  • einmal zu Beginn, bei der Konstruktion
  • wenn wir in einen geschachtelten Scope eintreten
  • wenn wir einen Solchen verlassen

Zwar sind indirekte Calls aufwendiger, aber letzten Endes auch wieder nicht soooo aufwendig,

daß sie uns im gegebenen Kontext umbringen...

intern: eingebaute initDiffApplication()

...wird automatisch vor Konsumieren eines Diff aufgerufen

Widerspruch: TreeMutator ist Wegwerf-Objekt

Lösungsversuch: doppelte Hülle

kann daher TreeMutator konstruieren

...und zwar per mutatorBinding

implementiert somit initDiffApplication()

TODO: Namensgebung

TreeMutator-Binding muß opaque bleiben

Buffer-Größen-Management vorsehen

das heißt

  • ein sinnvoller Startwert wird heuristisch vorgegeben
  • wenn die Allokation scheitert, die Exception fangen und die tatsächlich benötigte Größe merken

...das heißt:

gegeben ein syntaktisch sinnvoller top-level-Aufruf ("wende das Diff an")

-- wie bzw. von wem bekommen wir dann ein Binding, das einen passenden TreeMutator konstruiert?

erscheint mir die am wenigsten überraschende Lösung.

und zwar per handle.get()

erscheint mir fehleranfällig und irreführend für den Nutzer der Schnittstelle.

Denn er muß zwar das Objekt in das Handle platzieren, dann aber auch noch einen Pointer zurückgeben,

der dann auch noch NULL sein kann, zum Signalisieren von Fehlern.

Ich empfinde das als schlechten Stil

naja, das wäre billig, aber auch wieder beliebig.

Es macht keinen Sinn vom API-Design her, sondern man müßte es halt machen,

weil die Implementierung den Zeiger auf den geschachtelen sub-Mutator umsetzen muß.

die Diff-Sprache verlangt,

daß vor dem Öffnen des geschachtelten Scopes

dieser zumindest einmal per ins "angelegt" wurde.

...das ist ein Versuch, den Code für den Leser verständlich zu halten.

Die Idee ist, daß es einen high-level Unit-Test gibt, der die gesamte Diff-Anwendung durchspielt

und dazu passend einen low-level Unit-Test, der analog die gleichen Operationen macht,

allerdings direkt auf dem TreeDiff-Interface durch Aufruf der passenden Primitiv-Operaionen.

Letztere müssen für jede Art von "onion-layer" (konkretes Binding) erneut implementiert

und daher auch jeweils eigens per Unit-Test abgedeckt werden.

das ist hier sinnvoll. Das Binding sollte komplexer sein,

als in der Praxis auftretende Bindings. Warum? Weil letztere immer etwas einseitg sind

und damit Abkürzungen im Code-Pfad ausnützen. Die Gefahr schlummert aber im Zusammenspiel

der konkreten Bindings mit mehreren "onion layers"!

...denn es ist sehr verwirrend, welche Signatur denn nun die Lambdas haben müssen

...denn es kann keinen Default-Matcher geben....

...sonst wird niemand Lambdas bereitstellen können, oder gar Diff-Nachrichten erzeugen.

Das ist nun kein spezielles Problem der gewählten Implementierungs-Technik, sondern rührt daher,

daß der Client hier eigentlich ein Protokoll implementieren muß.

wenn überhaupt, dann im Matcher im Binding-Layer implementieren

...denn wir haben nun mehrere Layer,

und der Selector kann einfach anhand von Ref::THIS keine sinnvolle Entscheidung treffen.

Daher versuchen dann alle Layer dieses Element zu behandeln, oder gar keiner

Und da der Selector nur die Spec anschauen darf, läßt sich das auch nachher nicht mehr korrigieren

Daher habe ich mich entschlossen, dieses Sprachkonstrukt zu entfernen

entfernt, da schlechtes Design

entfernt, da schlechtes Design

anscheinend nicht notwendig

  • diese wird im Nexus behandelt, in dem die Tangible::mark()-Methode aktiviert wird
  • in dieser wiederum steckt eine Default-Handler-Sequenz, plus ein Strategy-Pattern

Marker-Typ MutationMessage

....denn dann müßte der Benutzer die Mechanik sehr genau verstehen, und stets eine auto-Variable definieren.

Sinnvoll wäre dieser Ansatz nur, wenn das UI-Bus-API eine MutationMessage const& nehmen würde,

denn dann könnte man den Builder-Aufruf inline schreiben.

Da wir aber stets den Diff moven und dann iterieren, scheidet const& aus

Und für eine reine Ref erzeugt C++ niemals eine anonyme Instanz!

...und diesen mit VTable bestücken.

Dafür wird die äußere Hülle non-virtual

und kann noncopyable gemacht werden..

Das erlaubt dem Benutzer, einfach zu schreiben

MutationMessage(blaBlubb())

Abstraktion

nach beiden Setien

...aus gutem Grund!

Der Nexus speichert nämlich eine direkte Referenz in der Routingtabelle

  • Map hat kein emplace_back
  • Map hat kein back()

Beides ist erst mal sinnvoll. Map hat zwar ein emplace, aber das fügt eben irgendwo ein

Und es gibt nicht sowas wie das "zuletzt behandelte" Element

Reihenfolge

erhalten!

...hat eine "zufällige" Reihenfolge, die von den Hash-Werten der gespeicherten Daten abhängt.

Das bricht mit unserem grundsätzlichen Konzept der kongruenten  Daten-Strukturen

Ein Diff, das von einer ETD gezogen wurde,

läßt sich nicht auf eine Map-Implementierung aufspielen

Entscheidung

...zum Beispiel wie grade hier, beim MockElm

das wird vermutlich niemals wirklich in einem vollen Diff-Zusammenhang gebraucht.

Und dann ist unbestreitbar eine Map eine sehr einfache Implementierung

und auch im Diff-Applikator nicht wirklich schwierig zu unterstützen

Problem: InteractionControl

...was andernfalles komplett vermeidbar wäre,

da im Übrigen das UI-Modell nur mit LUIDs und generischen Namen arbeitet

Idee: context-bound

Command und Verhaltensmuster

bleiben zusammen

...was ich einen Monat später schon wieder vergessen hatte...

hier geht es darum, eine Regel zu generieren,

die dann den zugrundeliegenden Command-Prototyp automatisch mit konkreten Aufrufparametern binden kann,

sobald bestimmte Umstände im UI einschlägig werden

Das ist ein erweiterter / komplexerer Anwendungsfall.

Der einfache Standard-Anwendungsfall ist, direkt die Command-ID zu senden

das reicht für die erste Integrationsrunde völlig aus

Instanz-Management ist automatisch

Focus/Spot wird mitbewegt

act, note: Nachricht upstream

mark: Nachricht downstream

Bus-Design is selbstähnlich

Kennzeichen ist die EntryID des zugehörigen Elements

Die Lösung für diese wecheslseitige Abhängigkeit

ist, den Nexus als Member im CoreService zu haben,

weil man dann seine Addresse schon weiß, bevor er erzeugt ist.

Dummerweise rettet mich dieser Trick nicht im Shutdown,

denn hier nun läuft tatsächlicher Code aus dem Destruktor heraus!

bei einem echten Downstream könnte man dafür sorgen,

daß er grundsätzlich vor dem Nexus weggeht. Aber nun kommt, auf dem Umweg

über den Core-Service, der Nexus nach dem Nexus....

ich will nicht damit anfangen, daß man einen Zeiger umsetzen kann....

beendet Deregistrierung,

wenn ein BusTerm sich selbst deregistriert

Mechanismus, der es erlaubt

  • log-Nachrichten aus Mocks zu hinterlassen
  • in der Test-Fixture auf diese zu matchen

Beispiel: Aktionen, die im globalen Menü stehen.

"Add Sequence"

  • wer bildet daraus ein Command?
  • auf welchen Kontext bezieht sich das
  • wen kann die Menü-Registrierung konkret ansprechen (Verdrahtung ist statisch)

konzeptionell: fertig

Implementierung der real-world-Variante fehlt!

Dienste im UI, erreichbar über den Bus.

Sie stellen die Verbindung zu zentralen Belangen her

wie Session- und State-Managment, Commands etc.

Compiler-Bug Gcc-#63723

gelöst in GCC-5 -- backport unwahrscheinlich

eine virtuelle Funktion

pro möglichem Umwandlungs-Pfad

wir verwenden die Basis-VTable

und layern nur die tatsächlich möglichen Umwandlungen drüber

empfängt alle state mark notificatons

nach Perspektive

nach work site

einer könnte für

mehrere Commands zuständig sein

mehrere könnten für

ein Command zuständig sein

....denn wir wollen ja grade

den Widget-Code vom Control-System abstrahieren

und ebenso die Gesten abstrahieren

muß Instanzen einsetzen

...und zwar zwingend, sobald

  • das command Argumente hat, die gebunden sein wollen
  • mehrere Invocations des gleichen Grund-Commands "gleichzeitig" unterwegs sein könnten

...vom Linken her nicht, da wir Gui gegen Proc linken

vom Bauen auch nicht, und außerdem...

...coden wir ja nicht gegen die Implementierung,

sondern gegen eine Abstraction (Command), die eigens dafür geschaffen wurde

...wegen

  • Command-Message via UI-Bus
  • Durchreichen durch das Interface-System

...da die DispatcherQueue direkt Command-Objekte (=frontend handle) speichert

...denn das GUI läuft ja synchron.

D.h. wir wissen, wenn wir das Air-Gap überstanden haben.

Ab diesem Punkt hält der Eintrag in der DispatcherQueue das Command am Leben,

und wenn es stirbt, dann stirbt es halt...

aufruf direkt mit Command-ID -> erzeugt automatisch eine Klon-Kopie

GUI: CmdAccessor

Proc: CmdInstanceManager

es könnte z.B. sein, daß man vom InteractionState

direkt einen Record<GenNode> bezieht, und bei diesem Zugriff

automatisch die Kontext-Accessor-Ausdrücke ausgewertet werden

...da dies ein pervasiv genutzter Service ist,

und wir nicht wollen, daß jedes Widget

mit dem InteractionDirector verdrahtet sein muß!

hat überhaupt nichts mit dem Zugang zu Commands zu tun,

und auch nichts mit der Trennung zwischen Layern und Subsystemen

es geht um Service-Dependencies

aka DependencyInjection + Lifecycle Management

  • man hat ein statisches Front-End, d.h. by-name access
  • hinter dem liegt eine Factory
  • die Instanz kann von innen her wieder geschlossen werden
  • wenn geschlossen, dann Fehler werfen

...nicht klar, ob das notwendig (und gut) ist

es könnte auch ausreichen, einfach die passende InteractionStateManager-Impl zu verwenden

denn InteractionStateManager ist ein Interface!

...das UI weiß,

wer das konkret immer sein wird.

D.h. beim Start des UI wird eine Verbindung irgendwo hinterlegt

Das könnte ein Advice sein

vom Command her ist der Typ festgelegt

auf das "aktuelle Element" wir eine Art Typ-Match gemacht.

Wenn der paßt, kann das aktuelle Element verwendet werden.

In diesem Fall wird das Command enabled

eine Argumentliste mit mehreren Parametern wir Schritt für Schritt geschlossen

wenn mehrere Objekte als Argumente in Frage kommen,

wird das gemäß Scope "nächstgelegne" genommen

die Instanz kommt nicht in der Fixture-Queue an

der Umstand, daß Commands auch ausgeführt werdern können,

gehört nicht zum Thema "Instanz-Management"

...denn ein Command geht dann in die Queue

und kann noch ausgeführt werden, während ein weiteres

schon "in der Mache ist"

...aus gutem Grund

(kann mich erinnern, daß ich mir das überlegt hatte).

Sofern Definitionen wirklich concurrent geändert oder gelöscht werden,

könnte es sein, daß jemand auf einer stale reference arbeitet,

denn das Lock schützt nur den Aufruf innerhalb der CommandRegistry.

Sicher ist der Zugriff nur, wenn im Schutzbereich dieses Locks ein

neues Command-Objekt kopiert wird. Was allerdings den RefCount erhöht.

aber sich mit einem Refcount verrückt machen.....

....künftige Weiterung:

auch in EntryID könnte ein Symbol-Stecken,

mithin in der GenNode::ID

da dieser Zugriff wirklich für jedes Command passiert,

möchte ich mit dem Minimum an Hashtable-Zugriffen auskommen.

Daher prüfen wir als erstes den CommandInstanceManager,

da dies der Regelfall ist. Wenn dies scheitert, suchen wir noch

in der globalen Registry

d.h. wir müßten dann auch noch das Interface brechen

und die Form der ID-Dokoration zur Konvention machen

das heißt, für das ganze Thema InteractionControl

schwebt mir eine Zwischenschicht unabhängig von den Widgets vor

Wenn nun aber das Anfordern einer neuen Instanz über den Bus laufen soll,

dann würde es wohl ehr direkt von den Tangibles (Widget / Controller) ausgehen.

Das wollte ich genau nicht

Tangible sollte InteractionState verwenden

....und demnach sollte InteractionState eben grade nicht von Tangible wissen

Demnach müßte sich InteractionState irgendwo "hinten rum" an den Bus ranmachen,

z.B. über den InteractionDirector. Das ist aber nun wirklich absurrd,

da es letztlich nur darum geht ein ohnehin öffentliches  Interface aufzurufen

...sonst wird die ganze Sache absurd

und unsinnigerweise aufwendig

Instanz öffnen

CommandID und Argumente gegeben

SessionCommandService::trigger

SessionCommandService::bindArg

SessionCommandService::invoke

managed diese Komponente nicht

aufwendiges Nebenthema

...das war der erste Entwurf

  • overengineered
  • am Bedarf vorbei

Symbol ADD_CLIP = CmdAccess::to (cmd::scope_addClip, INTO_FORK);

prepareCommand (cmdAccess(ADD_CLIP).bind (scope(HERE), element(RECENT)))

issueCommand (cmdAccess(ADD_CLIP).execute());

die DSL muß so konstruiert werden,

daß die Syntax-Elemente nahtlos simplifiziert werden können,

in eine Form, die sich unmittelbar jetzt implementieren läßt

und mit einfachen, direkt gegebenen Objekten

...stattdessen einen Fehler-Indikator auslösen

(Beispiel "in-point fehlt")

...das ist eine Reaktion,

die von einem managing Ui-Element ausgeführt wird,

aber von einem externen State-Change getriggert wird

invocationTral wurde aufgegeben.

Insofern löst sich dieser Knoten langsam

allerdings, wenn man eine explizite Instanz-ID angibt,

bleibt es bei der stringenten Fehlerbehandlung

das heißt, es geht um die Haupt-Registry für Commands.

Wenn wir eine Instanz machen, um Parameter zu binden und sie dann schließlich auszuführen,

könnte man dieser Instanz einen Namen geben, und sie in die Haupt-Registry eintragen..

Oder man könnte sie anonym verarbeiten, weil Command selber ein smart-Handle ist.

...ist jetzt geklärt.

InteractionState == Kontext

CommandID.KontextID == Instanz

...wenn es doch offenbar für den "fire-and-forget"-Fall

genauso gut möglich ist, über eine zentrale Stelle zu triggern.

Nebenläufigkeit ist kein Argument (da das UI single-threaded läuft)

aber: Parametrisierung könnte partiell sein

sie wird nicht zum Parameter-Sammeln verwendet

...eine Instanz wird dann erzeugt, wenn sie notwendig wird.

Sie kann vom UI-Command-Framework erzeugt werden,

sie wird automatisch erzeugt, sofern Parameter gebunden werden,

oder ein Command an den Dispatcher übergeben...

grundlegender Widerspruch

zwischen Command-Control-Interface

und Messaging

...und kaum erkennbarer Nutzen.

Der einzige Nutzfall wäre ein "this"-Parameter.

Den kann man aber mit geeigneter Syntax auch direkt angeben

hier müßte der InvocationTrail die aufgesammelten Argumente transportieren.

allein dafür genügt eine GenNode

...weil es zu jedem InvocationPath

zu jeder Zeit nur eine "offene" Instanz gibt.

Also genügt es, einen anonymen Klon dieser Instanz zu halten

gemeint ist:

Ein UI-Control wird aktivierbar, weil das zugrundeliegende Command

alle seine Argumente aus dem aktuellen Kontext befriedigen kann

Beispiel: Menü-Eintrag "create duplicate"

die Idee ist hier,

daß diese generischen Rollen bereits in der Einrichtung der Command-Definition verwendet werden.

Das heißt, für einen bestimmten Invocation-Trail legt man fest,

daß ein bestimmtes Argument an eine gewisse Rolle gebunden wird,

oder andernfalls einen bestimmten Namen bekommt

...eben!

Diese Frage hat dann dazu geführt,

daß ich das ganze Konzept "InvocationTrail"

wieder komplett zurückgebaut habe

vermutlich läuft es immer darauf hinaus

  • daß cmd.hpp die Implementierungs-Einheiten includiert
  • oder daß in einer ausgezeichneten Impl-Einheit das marker-Makro gesetzt wird und dann cmd.hpp includiert wird

...also ist eine Instanz durchaus noch am Leben,

während bereits die nächste Instanz für das GUI ausgegeben wurde.

...damit die Nummer erhalten bleibt

...implementiert "für die Zukunft",

wenn wir context-bound -Commands verwenden

das ist ein grundlegender Beschluß.

InteractionControl ist eine eigene Schicht;

deshalb ist auch der UI-Bus nicht das Universal-Interface schlechthin

d.h. der usage context entscheidet, ob wir einen Wert,

eine Referenz oder einen konstanten Wert verwenden

Record selber ist immuable

aber hat eine Builder-Mechanik

eigentlich fehlte nur die get()-Operation

erledigt... ähm vertagt

aber nicht wirklich; der workaround könnte schon die Lösung sein #963

ich hatte damals beim Variant und zugehörigen Buffer die Sorge,

daß ich die Implikationen einer generischen Lösung nicht durchdringen kann.

Und ich wollte keine Zeit auf einen exzessiven Unit-Test verwenden

generische Lösung verschoben #963

C++11 erlaubt =default

nicht klar, ob wir das überhaupt brauchen

  • entweder nur die unmittelbaren Kinder -> komplexe Logik fällt auf den Client
  • oder nur die Blätter -> man kann die Baum-Struktur nicht wirklich nutzen

Entscheidung

was wir brauchen

geht nicht:

rekursiver Abstieg in der Mitte eines Iterators

das war die Quintessenz der ganzen Entwicklung zum IterExplorer

Nachdem ich die depth-first / breadth-first -Strategien systematisch aufgebaut hatte,

habe ich das dann reduziert und kompakt nochmal geschrieben.

Sehr schön!

übrigens: genau den verwenden wir auch zur Job-Planung

...denn wir müssen den Weg zurück finden.

Wenn also eine Datenstruktur nur einfach verzeigert ist, oder direkt rekursiv (wie bei uns),

dann ist es absolut unmöglich, eine Traversierung mit konstantem Speicher zu machen.

Das geht nur bei einer Struktur mit Rückreferenzen -- diese enthalten dann nämlich genau den Speicher,

der während dem Einstieg in die einfach verzeigerte Struktur auf dem Stack liegt. Aber letztere

braucht nur eine logarithmische Menge an Speicher, und das auch nur während der Traversierung.

Dies ist die Abwägung, und darunter läßt sich nichts weghandeln.

Der einzige verbleibende Freiheitsgrad ist, bei einer unmittelbaren rekursiven Programmierung

direkt den Prozessor-Stack für die Speicherung des Rückweges mitzuverwenden;

in dem Moment, wo ich mich für einen Iterator entscheide, ist diese Möglichkeit weg.

kann genauso effizient werden

aber nur, wenn man die Initialisierung hinbekommt

oder diese Logik

fest verdrahten

da es sich um einen disjunktiven Typ (entweder-oder-Typ) handelt,

könnte man die Storage mit beiden Bedeutungen überlagern.

Voraussetzung wäre, daß man anhand der konkreten Daten gefahrlos  jeweils herausfinden kann,

welcher Zweig grade gilt. Da wir aber keine Introspektion haben (und auch nicht wollen!),

würde das auf Taschenspielertricks mit der Implementierung hinauslaufen

  • GenNode und Record beginnen beide fraktisch mit einem String. Man müßte diesen interpretieren können
  • oder man nutzt die letzten Bits des Pointers, um sich dort eine Flag zu speichern...

Damit ist schon klar: sowas macht man nicht ohne Grund

Entscheidung: falls eingebetteter Record

Begründung: das Durchlaufen und Rekonstruieren eines Baumes

ist letztlich doch ein sehr spezieller Fall, und rechtfertigt nicht,

den HierarchyOrientationIndicator in jeden Iterator einzubetten.

Zumal -- wenn der level zugänglich ist -- kann man diese Mechanik genauso gut

dort direkt ansiedeln, wo sie gebraucht wird.

also keine Monade

Gleichheit

kombiniert den Wert-Match mit der Iteration

Zweck: kompaktes Anschreiben

von literalen Daten

Object builder

Problem ist, wir definieren den Typ Record generisch,

verwenden dann aber nur die Spezialisierung Record<GenNode>

Und die Builder-Funktionen brauchen eigentlich spezielles Wissen über den zu konstruierenden Zieltyp

Mutator selber is noncopyable

Ergebnis move

pro / contra

Move ist gefährlich

aber auch deutlich effizienter,

denn wir müssen sonst das ganze erzeugte Ergebnis einmal kopieren.

Nicht sicher, ob der Optimiser das hinbekommt

nur auf dem Mutator

dieser ist nicht kopierbar

und muß dediziert erstellt werden

möglicherweise schon gelöst,

denn Record ist insgesamt immutable.

Also können wir einen Find mit einem const_iterator machen

was sinnvoll ist,

hängt vom Payload-Typ ab

bei einer 'key = value' -Syntax mit strings

ist nur ein Value-Rückgabewert sinnvoll

...auch kann man auf diesem Weg die Storage konfigurierbar machten

da wir einen IterAdapter verwenden, können wir nur eine 'pos' (einen Quell-Iterator)

als Zustands-Markierung verwenden; die gleiche 'pos' wird aber auch inkrementiert und dereferenziert.

Daher ist die einzige praktikable Lösung, daß die Typ-ID in einem weiteren Vektor gespeichert wird.

Das könnte dann ein Metadaten-Vektor sein.

Natürlich ist dieser Ansatz nur sinnvoll, wenn wir wirklich Metadaten brauchen.

Denn jeder Record zahlt den Preis für die komplexere (zusätzliche) Datenstruktur!

scheidet aus, wegen Wertsemantik

mit speziellem Ref-Typ

-- im DataCap

heißt: in der Diff-Verarbeitung wird dieser spezielle check verwendet

m.E. die einzig saubere Desgin-Variante!

gemeint ist:

  • man kann alternativ auch eine RecordRef direkt in eine elementare GenNode packen
  • diese verhält sich dann nicht transparent, denn sie hat eine andere Identität als ihr Ziel
  • das kann aber als spezielles Ausdrucksmittel genutzt werden

heißt: wird direkt von standard-equality so behandelt

brauche speziellen Builder,

der das so fabriziert

bekomme einen

"ungenutzten" DataCap

Idee: Ref-GenNode

als Ref erkennbar

(Prädikat)

hash-identische

Ziel-ID ableitbar

Verarbeiten

von Teilbäumen

Interpreter definiert Sprache

ROOT

INIT

leeres

Objekt

pick(Ref::CHILD)

würde sagen: ja, aber auch nur für das after-Verb!

allgemein halte ich einen wrap-around für keine gute Idee,

weil er zu Zweideutigekeigen führt und daher Struktur oder Konsistenzfehler überspielt

läßt sich stets duch eine inverse Folge von find und pick  emulieren

vorerst verworfen, da zusätzlicher Prüf-Aufwand

...Grund: sie werden durch einen jeweils komplett anderen Ansatz implementiert

  • "Liste" beruht auf dem Attribut-Iterator und dem Aufbauen einer neuen Attribut-Sammlung
  • "Map" beruht darauf, alle Operationen an die Storage zu delegieren

das heißt, man kann Attribute in einer "sinnvoll lesbaren" Ordnung anschreiben

und später angefügte Attribute bleiben so erkennbar.

Vorteilhaft für Version-Management

profitiert also von allen Verbesserungen des allgemeinen Algorithmus

"hoch effizient", unter der Annahme, daß fast immer nur konforme Änderungen kommen.

Weil dann nämlich die in unserer Implementierung ggfs. kostspieligen Umordnungen entfallen,

kommen wir auf lineare Komplexität für die Verarbeitung

+ NlogN für den Index zur Diff-Erzeugung

unsere Impl der Diff-Erzeugung (!)

baut einen Index auf (N*logN), um Einfügungen/Entfernungen zu erkennen und Umordnungs-Suche zu unterstützen.

Wenn wir aber von ausschließlich konformen Operationen ausgehen,

wird dieser Index nicht benötigt. Leider können wir das aber nicht garantieren, denn

es könnte ja zwischenzeitlich ein Attribut gelöscht und dann später (am Ende) wieder

angehängt worden sein, was dann eben doch einen Index erfordert, um einen

korrekten Listen-Diff zu erzeugen

d.h. wenn die Storage hoch-optimiert ist,

dann überträgt sich das auf die Diff-Behandlung

da wir Attribute in einer Liste speichern,

müssen wir für jede Einfügung eine vollständige Suche machen

...gemeint ist: extra, anders als die normale Listenverarbeitung.

Auch wenn diese andere Implementierung nur delegiert

danach noch auftretende Attribute

erfordern Sonder-Behandlung,

indem sie an die Attributs-Liste angehängt werden

wegen Entscheidung für das "Listen"-Modell zur Attribut-Handhabung

das heißt:

  • es wird einfach vom zuständigen Layer (der für die Attribute) aufgegriffen
  • es hat keinen Einfluß auf die nach außen sichtbare Reihenfolge
  • diese Reihenfolge bleibt gruppiert nach Attributen / Kindern

...da das Kind in der Liste der Attribute nämlich garnicht gefunden wird

...wenn wir am Ende der Attribut-Zone stehen,

und die nächste Operation ein fetch eines Kindes ist, müssen wir implizit den

Wechsel in den Scope vollziehen und die Operation dort ausführen.

Aber an allen anderen Stellen in der Attribut-Zone ist ein solcher Fetch ein Fehler!

standardmäßig strikt

List-Diff

als Spezialfall

kann auch nicht

wegen dem Interpreter

leicht auf generischen Container

zu verallgemeinern

Erkennung hat die Sprache als Parameter,

und verwendet sie zur Token-Generierung

man kann auch dem List-Detector

eine Tree-Diff-Language geben

Frage: in-Place?

entscheidende Frage: wie addressieren?

und wird durch die Diff-Anwendung konsumiert

Immutablility erzwingt

  • persistente Datenstrukturen
  • garbage-collector

Lösung: wir arbeiten auf einem Mutator

auf dem Umweg über einen ContentMutator

Innereien des alten Record verbrauchen

Problem: Rekursion

wenn ein MUT kommt

erzeugt man lokal einen DiffApplikator für den geschachtelten Kontext

und gibt ihm rekursiv den Diff hinein. Wenn dieser Aufruf zurückkehrt

ist der gesammte Diff für den eingeschachtelten Kontext konsumiert

wenn ein MUT kommt,

pusht der Applikator seinen privaten Zustand

auf einen explizit im Heap verwalteten std::stack

und legt einen neuen Mutator an für den nested scope

Entscheidung:

interner Stack

....begründet duch die generische Architektur.

Die Trennung von Diff-Iteration und dem Interpreter ermöglicht verschiedene Sprach-Ebenen.

Allerdings werde ich für die Anwendung auf konkrete Datenstrukturen,

also den TreeMutator, vermutlich das andere Modell (rekursiv konsumieren) verwenden.

Problem sind mal wieder die automatisch generierten IDs.

Die sind natürlich anders, wenn wir die ganze Testsuite ausführen...

Diff ist eine abstrakte Quelle,

die nur einmal verbraucht werden kann

Dekorator-Prinzip.

Paßt hier, da IterSource genau dieses Vorgehen nahelegt

MutationMessage::updateDiagnostics()

...diejenige, die zum Zeitpunkt des updateDiagnostics() noch anstand

schmerzloses C++ API

Performance: guter Schnitt (etw. besser als boost spirit)

hat ein DOM-API und ein SAX-artiges API

das heißt: nicht einmal abhängig von der STL

wie gson

vjson war Google Code;

nach dem Umzug auf Github heißt es gason

lt. eigenen Benchmakrs deutlich schneller als rapitjson, welches eigentlich immer als der "schnelle" JSON-Parser gilt.

d.h. das Parsen schreibt den Eingabepuffer um, und Strings bleiben einfach liegen

kein Repo auffindbar

ich will nicht noch ein Objekt-System

...necessary when closing the session;

we need to wait for the current command or builder run to be completed

...noch nicht implementiert 1/17

Guard beim Zugang über das Interface

nur sie ist atomar

nur ein Thread für Commands und Builder

Ticket #1054

...und "self" == LumieraThrea* == "handle" (im Wrapper).

D.h. solange der Wrapper lebt (!), kann er selber leicht feststellen, ob die aktuelle Ausführung

auch in einem Thread stattfindet, der

  • von unserem Threadpool gestartet wurde
  • ein Thread-Handle hat, das mit dem Handle dieses Wrappers identisch ist.

Das Schöne bei diesem Ansatz ist, daß man dafür weder das Handle exponieren muß,

noch irgendwelche komischen Policies aufmachen. Solange es das Objekt gibt, klappt das.

OO rocks!

Kontrollfluß ist nicht in einer Arbeitsfunktion

...und dann nur noch

  • auf Shutdown reagieren
  • mitbekommen wenn die Sperre aufgehoben wird

billig: unsere Zeit-Lib nutzen

Logik im Looper auf Basis

generischer Überlegungen implementiert

Thema: Monaden

gehört zu dem Themenkomplex "itertools"

Python hat das auch, Java neuerdings auch

...will sagen, es ist klar, wie man sowas machen kann.

Seinerzeit war mir das auch klar, aber ich wollte es nicht gleich ausprogrammieren.

Inzwischen kam dann das Thema UI-Coordinaten, und dort habe ich es ausprogrammiert,

und zwar direkt in die Low-Level-Schicht integriert, was nicht schlecht ist,

da eine Abstraktion hier sehr technisch werden würde

...bindet die Betrachtung auf einen technischen Level,

und führt dazu, daß die Abstraktion undicht wird

genau der Umstand,

daß funktionale Sprachen von einer Zustands-Phobie getrieben sind,

macht Monaden nützlich, um inhärenten Zustand wegzuabstrahieren.

Das kann genutzt werden, um den Zustand einer Wechselwirkung

nach einer Seite der Glieder auszukoppeln.

saugeil

...nicht mehr das klassische gtk::Main

Wozu das?

  • Design: Main war ein Singleton; aber sein dtor hat auch Plattform-Aufräum-Arbeiten gemacht
  • Framework: anscheinend ist hier eine Tendenz in Richtung auf ein integriertes Framework im Gange; im Besonderen will man "Aktionen" direkt aus dem Desktop aufrufen können

warum?

nur wegen ApplictationWindow!

Denn dieses setzte eine "Registrierung" voraus.

Alles in ein Framework zwingen. Alternativlos, capisce?

initialisiert das

Framework

...das heißt, es wurde "retrofitted".

die Lib Gio bietet ein generisches "Main-Loop-Framework",

in dem ein Main-Context gepollt wird, solange, bis ein use-count auf Null geht.

Gtk-Main verwendet inzwischen den gleichen Mechanismus

der springende Punkt mit sigc::trackable ist,

daß Desktuktoren automatisch die Signale abkoppeln.

Dieser Vorgang ist nicht threadsafe. Folglich müssen

auch die Destruktoen im GUI-Thread laufen.

Das ist eine subtile Falle.

alles was von sigc::trackable erbt

für alles aus GTKmm zu verwenden

...gemeint ist:

alles das nicht aus dem GUI-Thread heraus geschieht

...können vom CSS-Stylesheet aus gesetzt werden.

Siehe Beschreibung im Beispiel/Tutorial

....how does the event dispatching deal with partially covered widgets

...for embedded widgets

...meaning, "this event is not yet fully processed",

i.e. the enclosing parent widget also gets a chance to redraw itself

Warning: allocation is the visible area

asked on stackoverflow...

...as can be observed

by printing values from the on_draw() callback

...otherwise adjustment values will cummulate,

causing us to adjust too much

...die anderen, die noch in Frage kommen würden,

sind nur für den Fall, daß ein Widget neu instantiiert wird

oder neu in das Window-System gemappt wird.

on_check_resize() wird nicht aufgerufen

...keine Ahnung, was ich beim ersten Mal falsch gemacht habe.

jedenfalls hab ich da sofort beim ersten Aufruf der Closure einen SEGFAULT bekommen.

Auch im zweiten Anlauf habe ich ein Lambda verwendet.

Möglicherweise ist der einzige Unterschied, daß ich es nun aus dem draw-callback

aufrufe, und daß demgegenüber bei der ersten Verwendung die Allocation des jeweiligen

Kind-Widgets noch gar nicht festgelegt war (denn das passiert erst beim draw).

in der Implementierung, mywidget.cc

ist eine komplette Sequenz, wie man einen CSS-StyleProvider setzt

und auch ein Signal für Parse-Fehler anschließt

Beispiel im Guide

  • left gravity: Marker bleibt beim Einfügen an dieser Stelle links von der Einfügung stehen
  • right gravity: Marker wird durch Einfügen an dieser Stelle nach rechts geschoben

Beachte: der Text-Cursor (Marker "insert") hat right gravity

Multithreded-Beispiel

im Guide demonstriert das

Ticket #886

Lessons

learned

auf incomplete type achten

Vorsicht bei

mutually dependent templates

kann eines der Templates im Zyklus vorrübergehend als "incomplete" gelten.

...wenn man dummerweise auf verschlungenen Pfaden

genau in dieser Phase die Metafunktion anfragt,

kann der betreffende Check stillschweigend scheitern.

Konsequenz: man wählt dann z.B. eine subtil falsche Spezialisierung.

wenn eine getemplatete Klasse zum Qualifizieren eines Feldes verwendet wird,

dann müssen die formalen Template-Parameter in spitzen Klammern mit angegeben werden.

D.h. Doxygen ist hier genauso penibel wie C++ selber

Beispiel

Query<RES>::resolveBy

@param hat stets einen Parameternamen als Argument

...der ist nicht optional

vielmehr wird blindlings immer das erste Wort genommen.

Wenn der Parameter selber nicht benannt ist (z.B. pure virtual function),

kann man ersatzweise einfach einen Typnamen angeben.

Sofern alle Parameter dokumentiert sind, klappt das.

sonst kommt Doxygen durcheinander

....haben in ihrem @file-Kommentar

einen Verweis \ref DieserUnit_test

Und obwohl das der exakte Klassennahme ist,

und obwohl genau diese Klasse im Klassenindex zu finden ist

wird hier kein Link erzeugt

...im Besonderen die guten Diagramme für Pulse, ALSA und Jack

bekannter Bug binutils #16936

Lumiera-Ticket #965

gelöst in 4e8e63ebe

...man "hilft" dem Linker mit

"-Wl,-rpath-link=target/modules"

laufen wieder alle

test.sh Zeile 138

Debian-Bug #724461

nebenbei ohweh:

ulimit -t 1 ist wirkungslos

Christian:  bash -c "ulimit -t 1; while :; do :; done"

und wir verbringen unsere Zeit mit contention

ist klar, hab ich gebrochen

siehe Ticket #587

Kollisionen jetzt bereits nach 4000 lfd. Nummern

Vorher hatte ich erste Kollisionen nach 25000 Nummern

erinnere mich an den

guten alten "Knuth-Trick"

wow: es genügt,

die letzten beiden Zeichen mit der Knuth-Konstante zu spreizen,

und ich komme locker auf 100000 Nummern ohne Kollision

Aug 10 04:51:39 flaucher kernel: gdb[8234]: segfault at 7ffe3fa79f50 ip 0000000000718b95 sp 00007ffe3fa79f40 error 6 in gdb[400000+574000]

Aug 10 04:51:39 flaucher kernel: traps: test-suite[8249] trap int3 ip:7ffff7deb241 sp:7fffffffe5c8 error:0

function gebunden an ein lambda

wobei ein Argument-Typ als vom Template-Argument

der umschließenden Funktion aufgegriffen wird

Bugreport für Debian/Jessie #795445

Git: debBild/Gdb_DEB.git

bison dejagnu flex gobjc libncurses5-dev libreadline-dev liblzma-dev libbabeltrace-dev libbabeltrace-ctf-dev python3-dev

dutzende Tests scheitern

verräterrischer Code im debian/rules

check-stamp:

ifeq ($(run_tests),yes)

        $(MAKE) $(NJOBS) -C $(DEB_BUILDDIR)/gdb check \

          || echo "**Tests failed, of course.**"

endif

        touch $@

au weia LEUTE!

speziell: unused-function bei dem Trick mit dem std::hash macht mir Sorgen.

und tatsächlich: das ist daneben, GCC hat Recht!

aktualisieren und neu bauen

standard hardening-flags setzen #971

wähle Kompatibiltät genau so, daß Ubuntu-Trusty noch unterstützt wird.

...damit man auch im Paketbau-Build-Output wenigstens einmal alle  generischen Platform-Schalter sieht

Ich meine also: zu Beginn vom Build sollte das Buildsystem einmal eine Infozeile ausgeben

...denn die stören jeweils beim erzeugen eines Hotfix/Patch im Paketbau per dpkg --commit

deprecated: auto_ptr

Tests mit TypeIDs scheitern

Doku durchkämmen nach Müll

hier nach offensichtlich obsoleter Info checken

WICHTIG: keine vorgreifende Infor publizieren!!!!!

die explizit angegebenen Paketnamen schon mal vorchecken

die Abschnitte zu den LIbraries prüfen / umschreiben

insgesamt sorgfältig durchlesen

knappe Kennzeichnung des Releases in den Kommentar

hier geht es darum, Konsistenz im Git herzustellen.

Wenn alles korrekt gemacht wurde, dürfte es hier keinen Rückfluß von Änderungen geben.

Bitte auch daran denken, zuerst den DEB-Zweig zu prüfen. Diesen aber nicht zurückmergen,

denn wir wollen keine DEB-Info im Master haben!

einzeilige Kennzeichnung wiederholen

die unmittelbaren Release-Dokumente durchgehen

Merge-commit auf den Release-Zweig.

Sollte konfliktfrei sein

...das heißt bauen und hochladen

Referenz: Debian/Jessie (stable) : i386 and x86_64

Probleme mit der Compile-Reihenfolge  #973

...führt sowohl eine README, alsauch ein Verzeichnis /usr/share/doc/lumiera/html auf, das (noch) nicht existiert

unter Debian/Jessie wird das ignoriert

stelle fest: Fehler auf Trusty,

nur Warnung auf Mint

das heißt, daß ich versuchen kann, das Problem erst mal "unter den Teppich zu kehren"

Die Wahrscheinlichkeit, daß irgend jemand Lumiera unter Ubuntu/Trusty installieren möchte, erscheint mir akademisch

bauen mit gcc-5 scheitert

in lib/hash-standard.hpp

mit gcc-5 gebaute Tests scheitern

bauen mit gcc-4.9 nicht möglich

es gibt Probleme beim Linken mit den Boost-Libraries, die auf Ubuntu/wily mit gcc-5 gebaut sind.

Wichtig: hier nur was wirklich gebaut ist und funktioniert!

eigentlich war die nur notwendig für das Video-Viewer Widget,

was nun leider tot ist. Wir haben noch keinen Ersatz. Deshalb lasse ich die Abhängigkeit

bestehen, aber irgendwann müssen wir das schon glattziehen

hardening-flags! #971

Ticket #722

seit gcc-4.8 ist kein static_assert mehr in der STDlib

Probleme mit der Compile-Reihenfolge  #973