From 35ff53a716fa25bd1ed68cac471d9b7fa0d33b20 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 24 Sep 2023 02:45:43 +0200 Subject: [PATCH] Library: generalise pipeline summation into fold-left Using the same building blocks, this operation can be generalised even more, leading to a much cleaner implementation (also with better type deduction). The feature actually used here, namely summing up all values, can then be provided as a convenience shortcut, filling in std::plus as a default reduction operator. --- src/lib/iter-explorer.hpp | 43 +- tests/11concurrency.tests | 7 +- tests/15library.tests | 5 + tests/library/iter-explorer-test.cpp | 31 +- .../library/sync-barrier-performance-test.cpp | 146 ++ tests/library/sync-barrier-test.cpp | 6 +- wiki/thinkPad.ichthyo.mm | 1624 +++++------------ 7 files changed, 698 insertions(+), 1164 deletions(-) create mode 100644 tests/library/sync-barrier-performance-test.cpp diff --git a/src/lib/iter-explorer.hpp b/src/lib/iter-explorer.hpp index 6b61fcabc..c60867712 100644 --- a/src/lib/iter-explorer.hpp +++ b/src/lib/iter-explorer.hpp @@ -483,6 +483,19 @@ namespace lib { } + /** + * @internal derive suitable result value types when reducing elements into an accumulator. + */ + template + struct _ReduceTraits + { + using Result = typename iter_explorer::_FunTraits::Res; + using ResVal = typename lib::meta::RefTraits::Value; + }; + + + + /** * @internal Base of pipe processing decorator chain. @@ -1602,26 +1615,36 @@ namespace lib { consumeFun (pipeline); } + /** - * _terminal builder_ to invoke sum up resulting number values from the pipeline. - * @return accumulation of all results from the pipeline, combined by `std::plus` + * _terminal builder_ to sum up or reduce values from the pipeline. + * In the general case a _fold-left_ operation is performed; default values for the + * joining operation and the initial value however allow to fall back on summation of values. + * @param accessor a functor working on the pipeline result values or the iterator + * @param junctor (optional) binary operation, joining the sum with the next result of the junctor + * @param seedVal (optional) initial value to start accumulation from + * @return accumulation of all results from the pipeline, combined with the junctor */ - template - auto - resultSum (FUN&& accessor) + template()) + ,typename VAL =typename iter_explorer::_ReduceTraits::ResVal> + VAL + reduce (FUN&& accessor + ,COMB junctor =COMB() + ,VAL seedVal =VAL()) { auto accessVal = iter_explorer::_FunTraits::adaptFunctor (forward (accessor)); - value_type sum{}; - SRC& pipeline = *this; - for ( ; pipeline; ++pipeline) - sum += accessVal (pipeline); + + VAL sum{move(seedVal)}; + IterExplorer::foreach ([&](SRC& srcIter){ sum = junctor (sum, accessVal(srcIter)); }); return sum; } + /** simplified _terminal builder_ to [reduce](\ref #reduce) by numeric sum. */ auto resultSum() { - return IterExplorer::resultSum ([](const reference val){ return val; }); + return IterExplorer::reduce ([](const reference val){ return val; }); } diff --git a/tests/11concurrency.tests b/tests/11concurrency.tests index fcff233a7..a81050cb4 100644 --- a/tests/11concurrency.tests +++ b/tests/11concurrency.tests @@ -12,7 +12,12 @@ return: 0 END -TEST "Thread-local diagnostic context" DiagnosticContext_test < int .resultSum(); using Res = decltype(accumulated); CHECK (lib::test::showType() == "int"_expect); auto expectedSum = [](auto N){ return N*(N+1) / 2; }; - CHECK (accumulated == expectedSum(5)); + CHECK (accumulated == expectedSum(29)); + + // In the general case an accessor and a junctor can be given... + CHECK (explore(CountDown{10}) + .reduce([](int i){ return i - 0.5; } // accessor: produce a double + ,[](string accu, float val) + { + return accu+">"+util::toString(val); // junctor: convert to String and combine with separator char + } + , string{">-"} // seedVal: starting point for the reduction; also defines result type + ) + == ">->9.5>8.5>7.5>6.5>5.5>4.5>3.5>2.5>1.5>0.5"_expect); + + // If only the accessor is given, values are combined by std::plus... + CHECK (explore(CountDown{9}) + .reduce([](auto it) -> string + { + return _Fmt{"○%s●"} % *it; // accessor: format into a string + }) + == "○9●○8●○7●○6●○5●○4●○3●○2●○1●"_expect); } diff --git a/tests/library/sync-barrier-performance-test.cpp b/tests/library/sync-barrier-performance-test.cpp new file mode 100644 index 000000000..fc6bf7c64 --- /dev/null +++ b/tests/library/sync-barrier-performance-test.cpp @@ -0,0 +1,146 @@ +/* + SyncBarrierPerformance(Test) - investigate performance of yield-waiting synchronisation + + Copyright (C) Lumiera.org + 2023, Hermann Vosseler + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2 of + the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +* *****************************************************/ + +/** @file sync-barrier-performance-test.cpp + ** unit test \ref SyncBarrierPerformance_test + */ + + +#include "lib/test/run.hpp" +#include "lib/sync-barrier.hpp" +#include "lib/iter-explorer.hpp" +#include "lib/util-foreach.hpp" + +#include +#include +#include +#include + +using test::Test; +using util::and_all; +using lib::explore; +using std::array; + +using std::atomic_uint; +using std::this_thread::sleep_for; +using namespace std::chrono_literals; + + +namespace lib { +namespace test { + + namespace {// Test setup for a concurrent calculation with checksum.... + + const uint NUM_THREADS = 1024; + + atomic_uint stage1{0}; + atomic_uint stage2{0}; + atomic_uint finish{0}; + + SyncBarrier interThread{NUM_THREADS }; + SyncBarrier afterThread{NUM_THREADS+1}; + + /** + * A test thread to perform a summation protocol including synchronisation points + * - build a compound sum of random numbers in the first stage + * - wait for the compound sum to build up completely + * - book in the compound sum plus a further random number + */ + class TestThread + : std::thread ////////////////////////////////////////////////////////////////////OOO TOD-oh + { + public: + TestThread() + : thread{[&]() + { //-STAGE-1------------------------------ + localSum = rand() % 1000; // generate local value + stage1.fetch_add (localSum); // book in local value + interThread.sync(); // wait for all other threads to have booked in + + //-STAGE-2------------------------------ + uint sync = stage1; // pick up compounded sum from STAGE-1 + localSum += rand() % 1000; // add further local value for STAGE-2 + stage2.fetch_add (localSum+sync); // book in both local values and synced sum + afterThread.sync(); // wait for other threads and supervisor + + finish.fetch_add(1); // mark completion of this thread + thread::detach(); //////////////////////////////////////////////OOO Wech-oh + }} + { } + + uint localSum; // *deliberately* not initialised to avoid race + bool isRunning() const { return thread::joinable(); } ///////////////////////OOO Wack-oh + }; + + + /** sum up all `localSum` fields from all TestThread instances in a container */ + template + uint + sumLocals (CON const& threads) + { + return explore (threads) + .reduce ([&](TestThread const& t){ return t.localSum; }); + } + }//(End)Test setup + + + + + /*******************************************************************//** + * @test investigate performance of N-fold thread synchronisation. + * - start a _huge number_ of TestThread + * - all those pick up the partial sum from stage1 + * @remark without coordinated synchronisation, some threads would see + * an incomplete sum and thus the stage2 checksum would be lower + * @see lib::SyncBarrier + * @see steam::control::DispatcherLoop + */ + class SyncBarrierPerformance_test : public Test + { + + virtual void + run (Arg) + { + array threads; + + CHECK (0 == finish); + CHECK (and_all (threads, [](auto& t){ return t.isRunning(); })); + + afterThread.sync(); + sleep_for (5ms); // give the threads a chance to terminate + + CHECK (NUM_THREADS == finish); // all threads have passed out.... + CHECK (0 < stage1); + CHECK (stage1 < stage2); + CHECK (stage2 > sumLocals(threads)); + CHECK (stage2 == sumLocals(threads) + NUM_THREADS*stage1); // this holds only if all threads waited to get the complete stage1 sum + } + }; + + + + /** Register this test class... */ + LAUNCHER (SyncBarrierPerformance_test, "function common"); + + + +}} // namespace lib::test diff --git a/tests/library/sync-barrier-test.cpp b/tests/library/sync-barrier-test.cpp index 289e03b5d..6d4fb5567 100644 --- a/tests/library/sync-barrier-test.cpp +++ b/tests/library/sync-barrier-test.cpp @@ -97,10 +97,8 @@ namespace test { uint sumLocals (CON const& threads) { - uint sum{0}; - explore (threads) - .foreach ([&](TestThread const& t){ sum += t.localSum; }); - return sum; + return explore (threads) + .reduce ([&](TestThread const& t){ return t.localSum; }); } }//(End)Test setup diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 9cc64103d..1584ed246 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -54077,9 +54077,7 @@ - - - +

schmerzloses C++ API @@ -54092,9 +54090,7 @@ - - - +

Performance: guter Schnitt (etw. besser als boost spirit) @@ -54109,9 +54105,7 @@ - - - +

hat ein DOM-API und ein SAX-artiges API @@ -54122,9 +54116,7 @@ - - - +

das heißt: nicht einmal abhängig von der STL @@ -54135,9 +54127,7 @@ - - - +

wie gson @@ -54150,9 +54140,7 @@ - - - +

vjson war Google Code; @@ -54164,9 +54152,7 @@ - - - +

lt. eigenen Benchmakrs deutlich schneller als rapidjson, welches eigentlich immer als der "schnelle" JSON-Parser gilt. @@ -54178,9 +54164,7 @@ - - - +

d.h. das Parsen schreibt den Eingabepuffer um, und Strings bleiben einfach liegen @@ -54201,9 +54185,7 @@ - - - +

kein Repo auffindbar @@ -54229,9 +54211,7 @@ - - - +

ich will nicht noch ein Objekt-System @@ -54271,9 +54251,7 @@ - - - +

man hätte genausogut std::future und std::async verwenden können. @@ -54304,9 +54282,7 @@ - - - +

habe einen usleep(1000) getimed @@ -54325,9 +54301,7 @@ - - - +

daher messen wir die Loop als Ganzes. @@ -54358,9 +54332,7 @@ - - - +

volatile Variable außen, im Aufrufkontext @@ -54374,9 +54346,7 @@ - - - +

...was sehr schön beweist, @@ -54405,9 +54375,7 @@ - - - +

das ist der wichtigste Part: ich grenze mich hier explizit von der STL ab;  ein »Lumiera Forward Iterator« ist nicht eine besondere Art von Pointer — wir wissen sonst wirklich nichts, und wir verzichten auf ein absolut optimale Performance. Und wir bieten keinerlei  weitere Features, wie Rückwärts-Iteration, random-access, Löschen und Einfügen. Einen einmal verbrauchten Iterator kann man nur noch wegwerfen. @@ -54428,9 +54396,7 @@ - - - +

offensichtlich habe ich das gleiche Problem hier mehrfach gelöst und das dann wieder vergessen @@ -54470,9 +54436,7 @@ - - - +

Ein Pointer wird in diesem Kontext wie ein einfacher primitiver value behandelt, d.h. value_type ≡ der Pointer selber, reference  ≡ eine Referenz auf den Pointer, pointer ≡ ein Pointer auf den Pointer. Und: aus einem Pointer werden niemals nested type bindings abgegriffen. @@ -54526,9 +54490,7 @@ - - - +

Das Sandwich-Design tritt durchaus häufig auf, wenn man einen Builder hat, oder eine Pipeline mit einem terminalen Adapter. Aber beim TreeExplorer habe ich das viel klarer gelöst, indem der Kern im Sandwich selbst wieder ein »Lumiera Forward Iterator« ist, nach dem »State Core«-Modell. Im Grunde laufen die »Itertools« auf den gleichen Ansatz hinaus, nur habe ich das damals noch nicht erkannt und stattdessen ein privates Protokoll entwickelt, wodurch die gesamte Implementierung relativ schwer zu verstehen ist, und dennoch weder kompakter, noch performanter wird. @@ -54612,11 +54574,9 @@ - + - - - +

seit längerer Zeit ein Thema, @@ -54637,9 +54597,7 @@ - - - +

Thema: Monaden @@ -54651,9 +54609,7 @@ - - - +

sind Monaden @@ -54678,9 +54634,7 @@ - - - +

»AboutMonads« : das könnte mal eine Seite im Theorieteil werden ("more about...") @@ -54701,9 +54655,7 @@ - - - +

...bindet die Betrachtung auf einen technischen Level, @@ -54722,9 +54674,7 @@ - - - +

genau der Umstand, @@ -54791,9 +54741,7 @@ - - - +

gehört zu dem Themenkomplex "itertools" @@ -54828,9 +54776,7 @@ - - - +

...will sagen, es ist klar, wie man sowas machen kann. @@ -54892,7 +54838,7 @@ - + @@ -54928,9 +54874,7 @@ - - - +

...von der entsprechenden Methode im Transformer @@ -54941,9 +54885,7 @@ - - - +

...hatte die Notwendigkeit hierfür seinerzeit während der Tests entdeckt, @@ -54965,9 +54907,7 @@ - - - +

sofern längerfristig Itertools durch TreeExplorer abgelöst werden könnte @@ -54985,9 +54925,7 @@ - - - +

...und das hängt an einem hauchdünnen Faden, @@ -55021,9 +54959,7 @@ - - - +

...muß den Chain-Funktor aus dem Template-Argument erzeugen, @@ -55047,9 +54983,7 @@ - - - +

...es soll bloß einfach funktionieren!!!!!!!!!!! @@ -55063,9 +54997,7 @@ - - - +

keine gute Idee. @@ -55098,9 +55030,7 @@ - - - +

Name: mutableFilter() @@ -55122,9 +55052,7 @@ - - - +

...sieht gut aus. @@ -55174,9 +55102,7 @@ - - - +

...für das dort hineingereichte Funktor-Objekt wird der Argument-Accessor ausgewählt (Metaprogrammierung). @@ -55227,9 +55153,7 @@ - - - +

Problem: std::function in FilterPredicate @@ -55238,9 +55162,7 @@ - - - +

entweder Val -> bool oder Iter -> bool @@ -55274,9 +55196,7 @@ - - - +

...ist einfach und offensichtlich; @@ -55304,6 +55224,85 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ junktor ist asymetrisch: (string, float) ⟼ string +

+ +
+
+
+ + + + @@ -55312,9 +55311,7 @@ - - - +

...für das dort hineingereichte Funktor-Objekt wird der Argument-Accessor ausgewählt (Metaprogrammierung). @@ -55344,9 +55341,7 @@ - - - +

der Aufruf vom äußeren in den inneren Wrapper ist 1:1 und sollte vom Compiler wegoptimiert werden @@ -55388,9 +55383,7 @@ - - - +

...ich verbrenne Stunden beim Debuggen von neuem Code @@ -55439,9 +55432,7 @@ - - - +

Konsequenz: brauche Template Funktions-Operator @@ -55465,9 +55456,7 @@ - - - +

der Funktor für den Expander wird explizit als Sonderfall  aufgefaßt @@ -55475,9 +55464,7 @@ - - - +

das eigentliche Problem mit der bestehenden Lösung ist, @@ -55510,9 +55497,7 @@ - - - +

...wo der volle Typ des Funktors FUN bekannt ist @@ -55524,9 +55509,7 @@ - - - +

...und in dem besonders wichtigen Fall, @@ -55590,9 +55573,7 @@ - - - +

da sich die Iteratoren wirklich unterscheiden können dürfen @@ -55616,9 +55597,7 @@ - - - +

...wobei der konkrete Overhead noch nicht wirklich klar ist; @@ -55646,9 +55625,7 @@ - - - +

und das ist das Argument, das sticht @@ -55659,9 +55636,7 @@ - - - +

...will sagen, wenn schon eine neue Lösung, dann von A bis Z @@ -55699,9 +55674,7 @@ - - - +

FAIL___expectation___________ @@ -55720,9 +55693,7 @@ - - - +

Lösung: Quelle erst weiter iterieren, @@ -55738,9 +55709,7 @@ - - - +

Bedeutung der Invariante @@ -55752,9 +55721,7 @@ - - - +

technisch: @@ -55783,9 +55750,7 @@ - - - +

Und zwar wegen der Objektorientierung: die gesamte Logik ist verkapselt, und nur wenige Operationen sind für abgeleitete Klassen zugänglich; im Besonderen ist incrementCurrent() private und das ist der einzige Weg um an die Iteration des Quell-Iterators zu kommen, aber genau dort wird auch dropExhaustedChildren aufgerufen() @@ -55799,9 +55764,7 @@ - - - +

insofern kann das Verwerfen des Quell-Objekts verzögert werden, @@ -55821,9 +55784,7 @@ - - - +

  • @@ -55844,9 +55805,7 @@ - - - +

    bisher stand hier das dropExhaustedChildren(), aber das fällt in disem Fall leer durch, wenn wir uns bereits wieder auf Basis-Level befinden — was bisher kein Problem war, denn es war bereits das Quell-Element konsumiert worden. Da wir aber diesen Fall klar zu fassen bekommen direkt in expandChildren()  (weil eben die Expansion sich leer ergibt), können wir explizit in diesem Fall das Konsumieren der Quelle direkt machen. Ist im Ergebnis sogar logischer. @@ -55863,9 +55822,7 @@ - - - +

    ...ich hatte es seinerzeit wohl bemerkt, aber für nicht relevant bzw sogar „eigentlich logisch“ gehalten...  jedenfalls war es im Testfall  verify_IterSource()  dokumentiert, da dort auch die Source-Pipeline (hinter dem IterSource-Interface) sichtbar ist und mit beobachtet wird. Dieser Fall (Zeile 1229) hat nun angesprochen und wurde entsprechend korrigiert @@ -55889,9 +55846,7 @@ - - - +

    • @@ -55906,9 +55861,7 @@ - - - +

      EventLog ist ein Test-Hilfsmittel, @@ -55946,9 +55899,7 @@ - - - +

      ...so daß es nur noch wenige Zugangs-Punkte zum unterliegenden Iterator gibt @@ -55962,9 +55913,7 @@ - - - +

      TreeExplorer macht das Wrappen für uns automatisch, @@ -55989,9 +55938,7 @@ - - - +

      ...denn durch das Backtracking @@ -56017,9 +55964,7 @@ - - - +

      ...um "Hängenbleiben" auf dem gleichen Element auszuschließen. @@ -56037,9 +55982,7 @@ - - - +

      weil wir uns bisher bei allen vorausgegangenen Bedingungen @@ -56064,9 +56007,7 @@ - - - +

      ...d.h es findet zwar ein Backtracking statt, aber wenn alle konjunktiven Klauseln gesetzt sind, @@ -56085,9 +56026,7 @@ - - - +

      est-event-log-test.cpp:228:  verify_callLogging: (log.ensureNot("fun").after("fun").after("fun2")) @@ -56098,9 +56037,7 @@ - - - +

      Verdacht: Negation @@ -56113,9 +56050,7 @@ - - - +

      fun after fun matcht auf den immer gleichen Record @@ -56177,9 +56112,7 @@ - - - +

      und zwar mit abgeschaltetem Filter @@ -56187,9 +56120,7 @@ - - - +

      für diesen einen Schritt @@ -56232,9 +56163,7 @@ - - - +

      die Search-Engine bläht die Debug-Infos gewaltig auf @@ -56316,9 +56245,7 @@ - - - +

      geht gar nicht anders, denn diese sind templates @@ -56343,9 +56270,7 @@ - - - +

      Invariante: pullMatch() @@ -56404,9 +56329,7 @@ - - - +

      ...d.h. die einzelnen Steps in der Pipeline direkt wrappen. @@ -56424,9 +56347,7 @@ - - - +

      ...um den Expand-Funktor zu zwingen, eine Kopie zu machen; @@ -56447,9 +56368,7 @@ - - - +

      ...damit man stets weitere Builder-Funktionen auf der Pipeline aufrufen kann @@ -56459,9 +56378,7 @@ - - - +

      weil wir diesen manipulieren @@ -56506,9 +56423,7 @@ - - - +

      ...nach der »reinen Lehre« @@ -56531,9 +56446,7 @@ - - - +

      ...insofern wir nämlich zwingend auf den jeweilign Kind-Iterator zugreifen müssen. @@ -56562,9 +56475,7 @@ - - - +

      wir müssen hier eine Festlegung treffen @@ -56572,9 +56483,7 @@ - - - +

      ...weil die Ergebnisse der einzelnen Schritt-Funktoren, @@ -56594,9 +56503,7 @@ - - - +

      im typischen Fall kopiert man den Basis-Iterator, @@ -56625,9 +56532,7 @@ - - - +

      ...den ich mache, um den Adapter in jedem Einzelfall zu bekommen @@ -56656,9 +56561,7 @@ - - - +

      ....und fällt dann beim Instantiieren des Template auf die Schnautze @@ -56675,9 +56578,7 @@ - - - +

      sofern eine Initialisierung ausidem Source-Iterator möglich ist @@ -56690,9 +56591,7 @@ - - - +

      und sonst ist das nicht wirklich "kniffelig" @@ -56700,9 +56599,7 @@ - - - +

      ...bloß sind die zig-fach geschachtelten Template-Typen, @@ -56735,9 +56632,7 @@ - - - +

      Konsequenz: jede Argument-Funktion wird nochmal gewrappt @@ -56785,9 +56680,7 @@ - - - +

      wir sind irgendwo im Baum, nicht auf dem Basis-Layer. @@ -56852,9 +56745,7 @@ - - - +

      ...denn @@ -56875,9 +56766,7 @@ - - - +

      Filter leer == alles durchlassen @@ -56945,9 +56834,7 @@ - - - +

      ...d.h. direkt das Prädikat, und nicht eine Funktion, die den Filter konfiguriert @@ -56972,9 +56859,7 @@ - - - +

      ...habe ich noch gar nicht gemerkt, daß das geht, @@ -57031,9 +56916,7 @@ - - - +

      weil ich dann auf dem IterChainSearch unmittelbar die builder-Funktionen definieren kann @@ -57044,9 +56927,7 @@ - - - +

      denn: TreeExplorer == IterableDecorator< Pipeline > @@ -57056,9 +56937,7 @@ - - - +

      der oberste Layer, also hier IterChainSearch @@ -57086,9 +56965,7 @@ - - - +

      konfiguriert danach direkt den Filter @@ -57101,9 +56978,7 @@ - - - +

      ...weil der Basis-Iterator (also der Template-Parameter SRC) @@ -57134,9 +57009,7 @@ - - - +

      sausage-bacon-tomato-and-sausage-spam-spam-bacon-spam-tomato-and-spam-spam-bacon-tomato-and-spam-bacon-tomato-and-spam-tomato-and-spam @@ -57154,9 +57027,7 @@ - - - +

      sausage-bacon-tomato-and-spam-spam-bacon-spam-tomato-and-spam-bacon-tomato-and-bacon-tomato-and-tomato-and @@ -57231,9 +57102,7 @@ - - - +

      das heißt, diese Funktion darf es nur einmal geben, und das muß dann »die« einschlägige Implementierung sein, ohne Wenn und Aber @@ -57251,9 +57120,7 @@ - - - +

      also... @@ -57277,9 +57144,7 @@ - - - +

      einfach und minimalistisch implementiert — und zudem sind undendliche Folgen nur etwas für Mathematiker und Freunde der funktionalen Programmierung; in der Praxis braucht man zwingend stets eine Abbruch-Bedingung, allein schon weil sonst die Engine „Amok laufen“ könnte (man bedenke nur, daß die interne Zeit-Repräsentation bereits lange vor der Frame-Nummer wrappt) @@ -57292,9 +57157,7 @@ - - - +

      optional die Möglichkeit für einen getriggerten Stop vorsehen @@ -57312,9 +57175,7 @@ - - - +

      • @@ -57372,9 +57233,7 @@ - - - +

        ...wie bekommt man dann den konvertierten Wert 'raus. @@ -57411,9 +57270,7 @@ - - - +

        • @@ -57473,9 +57330,7 @@ - - - +

          Man würde also neben das Standard-Format ein toleriertes zweites Format stellen, welches dann ein Bürger zweiter Klasse wäre, aber auf allen wichtien APIs als 2.Alternative mit auftaucht. Zudem würde man gewisse Abkürzungs-Pfade schaffen, auf denen die alternative Spec dann verlustfrei durchgereicht werden kann. @@ -57498,9 +57353,7 @@ - - - +

          wenn man zum µ-Grid eine eindeutige Rundungs-Regel hinzufügt, @@ -57530,9 +57383,7 @@ - - - +

          im Besonderen bei den pragmatischen Lösungen @@ -57562,9 +57413,7 @@ - - - +

          und ganz im Besonderen: wir stützen uns für die Zeitbehandlung nicht auf libGavl ab @@ -57588,9 +57437,7 @@ - - - +

          FSecs durch einen neuen Wrapper RSec ersetzen @@ -57610,9 +57457,7 @@ - - - +

          nicht implizit konstruierbar aus int64_t @@ -57636,9 +57481,7 @@ - - - +

          ...seinerzeit fand ich diese Art »Offenheit« noch gut; auch weil ich mir erhoffte, damit mehr Contributors zu bekommen. Die Erfahrungen sprechen dagegen; klar, die Leute mögen erst einmal gerne „move fast and break things“ — aber wenn dann Aufräumen oder anstrengende Konzeptions-Arbeit notwendig würde, bleibt alles liegen und man verschwindet aus dem Projekt. @@ -57696,9 +57539,7 @@ - - - +

          Begründung: sie entstehen als Delta aus validen Zeitpunkten @@ -57834,9 +57675,7 @@ - - - +

          d.h. der usage context entscheidet, ob wir einen Wert, @@ -57853,9 +57692,7 @@ - - - +

          Record selber ist immuable @@ -57870,9 +57707,7 @@ - - - +

          eigentlich fehlte nur die get()-Operation @@ -57888,9 +57723,7 @@ - - - +

          erledigt... ähm vertagt @@ -57907,9 +57740,7 @@ - - - +

          ich hatte damals beim Variant und zugehörigen Buffer die Sorge, @@ -57925,9 +57756,7 @@ - - - +

          generische Lösung verschoben #963 @@ -57939,9 +57768,7 @@ - - - +

          C++11 erlaubt =default @@ -57962,9 +57789,7 @@ - - - +

          nicht klar, ob wir das überhaupt brauchen @@ -57983,9 +57808,7 @@ - - - +

          Entscheidung @@ -58009,9 +57832,7 @@ - - - +

          geht nicht: @@ -58026,9 +57847,7 @@ - - - +

          das war die Quintessenz der ganzen Entwicklung zum IterExplorer @@ -58079,9 +57898,7 @@ - - - +

          ...denn wir müssen den Weg zurück finden. @@ -58122,9 +57939,7 @@ - - - +

          kann genauso effizient werden @@ -58140,9 +57955,7 @@ - - - +

          oder diese Logik @@ -58171,9 +57984,7 @@ - - - +

          da es sich um einen disjunktiven Typ (entweder-oder-Typ) handelt, @@ -58217,9 +58028,7 @@ - - - +

          Entscheidung: falls eingebetteter Record @@ -58240,9 +58049,7 @@ - - - +

          Begründung: das Durchlaufen und Rekonstruieren eines Baumes @@ -58267,9 +58074,7 @@ - - - +

          also keine Monade @@ -58283,9 +58088,7 @@ - - - +

          Gleichheit @@ -58299,9 +58102,7 @@ - - - +

          kombiniert den Wert-Match mit der Iteration @@ -58322,9 +58123,7 @@ - - - +

          Zweck: kompaktes Anschreiben @@ -58337,9 +58136,7 @@ - - - +

          Object builder @@ -58348,9 +58145,7 @@ - - - +

          Problem ist, wir definieren den Typ Record generisch, @@ -58369,9 +58164,7 @@ - - - +

          Mutator selber is noncopyable @@ -58382,9 +58175,7 @@ - - - +

          Ergebnis move @@ -58395,9 +58186,7 @@ - - - +

          Move ist gefährlich @@ -58415,9 +58204,7 @@ - - - +

          nur auf dem Mutator @@ -58427,9 +58214,7 @@ - - - +

          dieser ist nicht kopierbar @@ -58452,9 +58237,7 @@ - - - +

          möglicherweise schon gelöst, @@ -58470,9 +58253,7 @@ - - - +

          was sinnvoll ist, @@ -58485,9 +58266,7 @@ - - - +

          bei einer 'key = value' -Syntax mit strings @@ -58501,9 +58280,7 @@ - - - +

          ...auch kann man auf diesem Weg die Storage konfigurierbar machten @@ -58531,9 +58308,7 @@ - - - +

          da wir einen IterAdapter verwenden, können wir nur eine 'pos' (einen Quell-Iterator) @@ -58603,9 +58378,7 @@ - - - +

          scheidet aus, wegen Wertsemantik @@ -58618,9 +58391,7 @@ - - - +

          mit speziellem Ref-Typ @@ -58653,9 +58424,7 @@ - - - +

          heißt: in der Diff-Verarbeitung wird dieser spezielle check verwendet @@ -58675,9 +58444,7 @@ - - - +

          m.E. die einzig saubere Desgin-Variante! @@ -58694,9 +58461,7 @@ - - - +

          gemeint ist: @@ -58723,9 +58488,7 @@ - - - +

          heißt: wird direkt von standard-equality so behandelt @@ -58734,9 +58497,7 @@ - - - +

          brauche speziellen Builder, @@ -58749,9 +58510,7 @@ - - - +

          bekomme einen @@ -58766,9 +58525,7 @@ - - - +

          Idee: Ref-GenNode @@ -58779,9 +58536,7 @@ - - - +

          als Ref erkennbar @@ -58794,9 +58549,7 @@ - - - +

          hash-identische @@ -58820,9 +58573,7 @@ - - - +

          Verarbeiten @@ -58899,9 +58650,7 @@ - - - +

          diese Delegates sind anderswo in eine Collection eingebunden @@ -58911,9 +58660,7 @@ - - - +

          und deren Reihenfolge ist dort von Bedeutung @@ -58933,9 +58680,7 @@ - - - +

          brauche Detektor für structural change @@ -58951,9 +58696,7 @@ - - - +

          ...man könnte auch auf die Idee kommen, es nur in das Collection-Binding einzuhängen. @@ -58981,9 +58724,7 @@ - - - +

          und zwar etwas wirklich Einfaches @@ -58993,9 +58734,7 @@ - - - +

          denn "elementar" bedeutet in diesem Fall, es wird ziemlich technisch und komplex @@ -59008,9 +58747,7 @@ - - - +

          oh ja.... @@ -59075,9 +58812,7 @@ - - - +

          ...indem man nur teilweise PICK-Verben sendet, und dann einfach weggeht. @@ -59090,9 +58825,7 @@ - - - +

          um das zu unterbinden, müßten alle Binding-Layer kollaborieren @@ -59100,9 +58833,7 @@ - - - +

          ..weil man diesen Umstand nicht generisch erkennen kann. @@ -59123,9 +58854,7 @@ - - - +

          d.h. es prüft, ob keine Elemente im Arbeitspuffer übrig sind @@ -59172,9 +58901,7 @@ - - - +

          denn einfacher wird der Code dadurch nicht, nur kürzer. @@ -59190,9 +58917,7 @@ - - - +

          da muß man auch erst mal drauf kommen, was einem der Compiler da so sagen will. @@ -59228,9 +58953,7 @@ - - - +

          selbst wenn er über x Basisklassen von MoveOnly erbt @@ -59288,9 +59011,7 @@ - - - +

          matchElement([](GenNode const& spec, ELM const& elm) @@ -59368,9 +59089,7 @@ - - - +

          • @@ -59405,9 +59124,7 @@ - - - +

            Reihenfolge @@ -59427,9 +59144,7 @@ - - - +

            ...hat eine "zufällige" Reihenfolge, die von den Hash-Werten der gespeicherten Daten abhängt. @@ -59443,9 +59158,7 @@ - - - +

            Ein Diff, das von einer ETD gezogen wurde, @@ -59461,9 +59174,7 @@ - - - +

            Entscheidung @@ -59479,9 +59190,7 @@ - - - +

            ...zum Beispiel wie grade hier, beim MockElm @@ -59525,9 +59234,7 @@ - - - +

            Interpreter definiert Sprache @@ -59541,9 +59248,7 @@ - - - +

            ROOT @@ -59557,9 +59262,7 @@ - - - +

            INIT @@ -59569,9 +59272,7 @@ - - - +

            leeres @@ -59596,9 +59297,7 @@ - - - +

            pick(Ref::CHILD) @@ -59610,9 +59309,7 @@ - - - +

            würde sagen: ja, aber auch nur für das after-Verb! @@ -59651,9 +59348,7 @@ - - - +

            läßt sich stets duch eine inverse Folge von find und pick  emulieren @@ -59703,9 +59398,7 @@ - - - +

            vorerst verworfen, da zusätzlicher Prüf-Aufwand @@ -59749,9 +59442,7 @@ - - - +

            ...Grund: sie werden durch einen jeweils komplett anderen Ansatz implementiert @@ -59773,9 +59464,7 @@ - - - +

            das heißt, man kann Attribute in einer "sinnvoll lesbaren" Ordnung anschreiben @@ -59791,9 +59480,7 @@ - - - +

            profitiert also von allen Verbesserungen des allgemeinen Algorithmus @@ -59803,9 +59490,7 @@ - - - +

            "hoch effizient", unter der Annahme, daß fast immer nur konforme Änderungen kommen. @@ -59827,9 +59512,7 @@ - - - +

            unsere Impl der Diff-Erzeugung (!) @@ -59864,9 +59547,7 @@ - - - +

            d.h. wenn die Storage hoch-optimiert ist, @@ -59879,9 +59560,7 @@ - - - +

            da wir Attribute in einer Liste speichern, @@ -59894,9 +59573,7 @@ - - - +

            ...gemeint ist: extra, anders als die normale Listenverarbeitung. @@ -59925,9 +59602,7 @@ - - - +

            danach noch auftretende Attribute @@ -59980,9 +59655,7 @@ - - - +

            wegen Entscheidung für das "Listen"-Modell zur Attribut-Handhabung @@ -59998,9 +59671,7 @@ - - - +

            das heißt: @@ -60033,9 +59704,7 @@ - - - +

            ...da das Kind in der Liste der Attribute nämlich garnicht gefunden wird @@ -60045,9 +59714,7 @@ - - - +

            ...wenn wir am Ende der Attribut-Zone stehen, @@ -60075,9 +59742,7 @@ - - - +

            standardmäßig strikt @@ -60111,9 +59776,7 @@ - - - +

            List-Diff @@ -60131,9 +59794,7 @@ - - - +

            kann auch nicht @@ -60152,9 +59813,7 @@ - - - +

            leicht auf generischen Container @@ -60168,9 +59827,7 @@ - - - +

            Erkennung hat die Sprache als Parameter, @@ -60183,9 +59840,7 @@ - - - +

            man kann auch dem List-Detector @@ -60208,9 +59863,7 @@ - - - +

            Frage: in-Place? @@ -60230,9 +59883,7 @@ - - - +

            entscheidende Frage: wie addressieren? @@ -60261,9 +59912,7 @@ - - - +

            und wird durch die Diff-Anwendung konsumiert @@ -60283,9 +59932,7 @@ - - - +

            Immutablility erzwingt @@ -60304,9 +59951,7 @@ - - - +

            Lösung: wir arbeiten auf einem Mutator @@ -60322,9 +59967,7 @@ - - - +

            auf dem Umweg über einen ContentMutator @@ -60344,9 +59987,7 @@ - - - +

            Innereien des alten Record verbrauchen @@ -60392,9 +60033,7 @@ - - - +

            Problem: Rekursion @@ -60411,9 +60050,7 @@ - - - +

            wenn ein MUT kommt @@ -60446,9 +60083,7 @@ - - - +

            wenn ein MUT kommt, @@ -60479,9 +60114,7 @@ - - - +

            Entscheidung: @@ -60492,9 +60125,7 @@ - - - +

            ....begründet duch die generische Architektur. @@ -60517,9 +60148,7 @@ - - - +

            Problem sind mal wieder die automatisch generierten IDs. @@ -60564,9 +60193,7 @@ - - - +

            • @@ -60587,9 +60214,7 @@ - - - +

              ...d.h. die bis jetzt geschriebene TreeApplikator-Implementierung @@ -60611,9 +60236,7 @@ - - - +

              ...da wir eine verb-basierte Sprache implementieren, @@ -60632,9 +60255,7 @@ - - - +

              weil wir den Anwendungs-Kontext noch überhaupt nicht kennen. @@ -60651,9 +60272,7 @@ - - - +

              Beschluß: akzeptiert @@ -60667,9 +60286,7 @@ - - - +

              im Sinn von "polymorpic value" ist das Backend virtuell @@ -60682,9 +60299,7 @@ - - - +

              ....wenngleich auch dieser aus einem Template generiert wird @@ -60701,9 +60316,7 @@ - - - +

              wir verzichten auf Introspektion der Elemente @@ -60716,9 +60329,7 @@ - - - +

              denn genau zu diesem Zweck haben wir die "External Tree Description" @@ -60764,9 +60375,7 @@ - - - +

              ...d.h, @@ -60802,9 +60411,7 @@ - - - +

              streng genommen ist es nur erlaubt, das ID-Symbol auszuwerten @@ -60821,9 +60428,7 @@ - - - +

              Visitor bedeutet zwei Indirektionen @@ -60831,9 +60436,7 @@ - - - +

              ...und das ist nicht akzeptabel für ein reines Selektor-Prädikat! @@ -60848,9 +60451,7 @@ - - - +

              denkbar nur bei Sub-Objekten @@ -60862,9 +60463,7 @@ - - - +

              gilt für alle praktischen Anwendungen @@ -60874,9 +60473,7 @@ - - - +

              ....auch wenn man zehnmal meinen könnte, @@ -60945,9 +60542,7 @@ - - - +

              "target matches spec" @@ -60967,9 +60562,7 @@ - - - +

              aber existiert nominell und kontext-abhängig @@ -61021,9 +60614,7 @@ - - - +

              das sind die konkreten Implementierungen @@ -61045,9 +60636,7 @@ - - - +

              kann niemals geschachtelte sub-Mutatoren modellieren @@ -61055,9 +60644,7 @@ - - - +

              ja wirklich, das wäre nicht sinnvoll!!!!! @@ -61092,9 +60679,7 @@ - - - +

              immer Mitwirkung des Elements @@ -61136,9 +60721,7 @@ - - - +

              weil beim Assignment die Spec (=GenNode) eben @@ -61221,9 +60804,7 @@ - - - +

              generische Repräsentaton ist so gewählt, @@ -61236,9 +60817,7 @@ - - - +

              wenn also ein Teil der diff-Funkttionalität nicht verfügbar ist, @@ -61255,9 +60834,7 @@ - - - +

              zwar erscheint es nicht sonderlich sinnvoll, @@ -61304,9 +60881,7 @@ - - - +

              ...will sagen, @@ -61345,9 +60920,7 @@ - - - +

              erfordert wirklich Kooperation @@ -61364,9 +60937,7 @@ - - - +

              ...denn wir verwenden hier als "private" Datenstruktur @@ -61404,9 +60975,7 @@ - - - +

              ...dankenswerterweise hat der subscript-Operator von std::Map @@ -61426,9 +60995,7 @@ - - - +

              der Builder in der nested DSL generiert einen sonderbar falschen "this"-Typ, @@ -61480,9 +61047,7 @@ - - - +

              gemeint ist: @@ -61515,9 +61080,7 @@ - - - +

              das heißt, wir können Elemente in der gleichen Reihenfolge anfügen, in der sie später dann in der Iteration erscheinen @@ -61545,9 +61108,7 @@ - - - +

              • @@ -61569,9 +61130,7 @@ - - - +

                MockElm, Z 260 : .attach (collection(attrib) @@ -61604,9 +61163,7 @@ - - - +

                wir integrieren Attribute nicht, weil es so schön symmetrisch ist, @@ -61633,9 +61190,7 @@ - - - +

                immer in der Klasse verankert @@ -61646,9 +61201,7 @@ - - - +

                ⟹ es geht eigentlich nur um den Wert des Attributes @@ -61664,9 +61217,7 @@ - - - +

                manche Felder sind optional @@ -61706,9 +61257,7 @@ - - - +

                unter der Maßgabe, @@ -61724,9 +61273,7 @@ - - - +

                "Anwendung" : meint das Anwenden eines Diffs auf ein Ziel-Objekt @@ -61748,9 +61295,7 @@ - - - +

                meint: ETD -> Objekt und dann später Objekt -> ETD @@ -61775,9 +61320,7 @@ - - - +

                Objekt -> ETD -> Objekt @@ -61801,9 +61344,7 @@ - - - +

                abweisen, was das Kriterium sicher verletzt @@ -61820,9 +61361,7 @@ - - - +

                mandatory : Wert muß per Konstruktor gegeben sein @@ -61836,9 +61375,7 @@ - - - +

                das heißt, in dem ins-Verb ist dann ein komplettes Objekt enthalten, @@ -61855,9 +61392,7 @@ - - - +

                Konstruktor befüllt das Feld halt irgendwie. @@ -61870,9 +61405,7 @@ - - - +

                das Objekt selber kann erkennen, ob das Feld sich im "default-Zustand" befindet @@ -61901,9 +61434,7 @@ - - - +

                ohne Prüfen ist emptySrc nicht implementierbar @@ -61911,9 +61442,7 @@ - - - +

                ...weil es für emptySrc keine neutrale Antwort gibt. @@ -61932,9 +61461,7 @@ - - - +

                d.h., man kann nur global auf Prüfung verzichten  @@ -61944,9 +61471,7 @@ - - - +

                und da habe ich mich bereits dagegen entschieden @@ -61960,9 +61485,7 @@ - - - +

                Feld unterstützt default-Wert @@ -61991,9 +61514,7 @@ - - - +

                Auslegung der @@ -62013,9 +61534,7 @@ - - - +

                rationale: object fields are hard wired, @@ -62070,9 +61589,7 @@ - - - +

                Einschränkung: accept_until END @@ -62081,9 +61598,7 @@ - - - +

                ...nämlich indem alle Attribute als "berührt" und akzeptiert markiert werden. @@ -62119,9 +61634,7 @@ - - - +

                analog wie assignElm @@ -62138,9 +61651,7 @@ - - - +

                das heißt, es findet keine Verifikation statt @@ -62160,9 +61671,7 @@ - - - +

                zu bindende @@ -62216,9 +61725,7 @@ - - - +

                sieht nach Ober-engineering aus, @@ -62248,9 +61755,7 @@ - - - +

                unterstelle Ziel als konstruierbar aus Payload @@ -62261,9 +61766,7 @@ - - - +

                da effektiv bereits der Setter diese Funktionalität enthalten kann und muß, @@ -62302,9 +61805,7 @@ - - - +

                injectNew tolerieren @@ -62314,9 +61815,7 @@ - - - +

                ....man könnte genausogut auch beim ersten Mal zuweisen @@ -62326,9 +61825,7 @@ - - - +

                denn die Diff-Anwendung auf GenNode unterstützt Zuweisung @@ -62378,9 +61875,7 @@ - - - +

                und die Keys der Attribute stecken in der GenNode selber! @@ -62410,9 +61905,7 @@ - - - +

                ...denn wir vermeiden dadurch Komplexität. @@ -62444,9 +61937,7 @@ - - - +

                d.h. der Attributwert hat Wertsemantik und wird einfach zugewiesen @@ -62456,9 +61947,7 @@ - - - +

                ...d.h. der Attributwert ist ein Objekt und damit ein nested Scope @@ -62471,9 +61960,7 @@ - - - +

                Problem: immutable values @@ -62498,9 +61985,7 @@ - - - +

                das alles passiert dann im Lambda @@ -62513,9 +61998,7 @@ - - - +

                Dilemma: defaultable fields @@ -62530,9 +62013,7 @@ - - - +

                ....mit der ETD, @@ -62557,9 +62038,7 @@ - - - +

                folglich ein Problem, @@ -62577,9 +62056,7 @@ - - - +

                ....weil das defaultable field noch nicht vom Diff berührt wurde. @@ -62597,9 +62074,7 @@ - - - +

                Lösung: alles immer explizit @@ -62620,9 +62095,7 @@ - - - +

                diese Lösung war zunächst mein Favorit. @@ -62673,9 +62146,7 @@ - - - +

                ...denn unter dem Strich würden wir hiermit volle Unterstützung für opitonale Attribute einführen, @@ -62699,9 +62170,7 @@ - - - +

                auf die empty-Prüfung am Ende verzichten @@ -62718,9 +62187,7 @@ - - - +

                denn in den meisten, wichtigsten Fällen get es um einen non-empty-check, @@ -62785,9 +62252,7 @@ - - - +

                jedwede "bessere" Implementierung muß zwingend einen Container verwenden, @@ -62808,9 +62273,7 @@ - - - +

                ....durch meinen allerersten Draft, @@ -62865,9 +62328,7 @@ - - - +

                gleiches Argument... @@ -62877,9 +62338,7 @@ - - - +

                ...damit unterstellen wir, daß später eine Symbol-Tabelle aufgebaut wird. @@ -62933,9 +62392,7 @@ - - - +

                in einem Fall kann man sie aus der Closure abgreifen @@ -62956,9 +62413,7 @@ - - - +

                ...das heißt, es gibt nur minimale, themantische Überlappung. @@ -62972,9 +62427,7 @@ - - - +

                Geschachtelte Typdefs lassen sich vermeiden: @@ -62988,9 +62441,7 @@ - - - +

                ...das heißt, wie rum man es auch auflöst, wird die Lösung auf einer Seite schlechter @@ -63012,9 +62463,7 @@ - - - +

                ...gedacht für verschiedene UseCases. @@ -63033,9 +62482,7 @@ - - - +

                ...die offensichtlichsten Dinge übersieht man nur zu leicht!!!!! @@ -63089,9 +62536,7 @@ - - - +

                zwei Bindings @@ -63114,9 +62559,7 @@ - - - +

                zwei Collection-Bindings @@ -63154,9 +62597,7 @@ - - - +

                ...diese Abkürzung ist nur auf den Konstruktur aufgepflanzt, @@ -63202,9 +62643,7 @@ - - - +

                ...mit den Lambdas kann ich nur die Sicht auf die Werte steuern, @@ -63240,9 +62679,7 @@ - - - +

                ...wir wollen mehrfach geschichtete TreeMutator-Subklassen, @@ -63284,9 +62721,7 @@ - - - +

                Mut -> Rekursion @@ -63324,9 +62759,7 @@ - - - +

                Problem: partielle Ordnung @@ -63342,9 +62775,7 @@ - - - +

                ...das heißt, @@ -63367,9 +62798,7 @@ - - - +

                ...das heißt also, es wird stets der zuerst gebundene Layer komplett durchgespult, @@ -63400,9 +62829,7 @@ - - - +

                auf Basis des neu geschaffenen TreeMutators @@ -63416,9 +62843,7 @@ - - - +

                ....man könnte später geeignete Automatismen schaffen, @@ -63441,9 +62866,7 @@ - - - +

                ...das so häufig in C++ auftretende Problem: @@ -63505,9 +62928,7 @@ - - - +

                ...zumindest im GUI, wo parktisch alle Empfänger auch ein Tangible (Widget oder Controller) sind @@ -63518,9 +62939,7 @@ - - - +

                ...und genau diese virtuelle Builder-Methode ist üblicherweise der Ort, wo mit einer DSL und über Lambdas das Mapping auf die internen Strukturen des DiffMutable hergestellt wird; deshalb kann auch DiffMutable selber ein sehr schlankes Interface sein; der resultierende TreeMutator ist dann eine für den jeweiligen konkreten Typ aus Bausteinen generierte Hilfsklasse @@ -63530,9 +62949,7 @@ - - - +

                ...und dort per smart-Ptr.
                Für diesen Fall kann die Verdrahtung weitgehend automatisch konfiguiert werden, und man muß eigentlich nur noch den Konstruktor-Aufruf explizit (per Lambda) in das TreeMuator-Binding integrieren. Wenn es mehrere »onion layer« gibt, muß allerdings auch noch ein »Selector« definiert werden, um zu steuern, wo genau dieses Binding angewendet wird, und wo sonst auf einen anderen Layer delegiert wird, z.B. für explizit gebundene Objekt-Attribute. @@ -63553,9 +62970,7 @@ - - - +

                ....daß ein unbedarfter client diesen Trick übershieht @@ -63590,9 +63005,7 @@ - - - +

                Client soll direkt mutatorBinding bieten @@ -63618,9 +63031,7 @@ - - - +

                nicht generisch: mutatorBinding @@ -63632,9 +63043,7 @@ - - - +

                Lösungsversuch: extern template @@ -63669,9 +63078,7 @@ - - - +

                ...im Klartext: diesen Zugriff von der generischen Implementierung @@ -63709,9 +63116,7 @@ - - - +

                intern: eingebaute initDiffApplication() @@ -63719,9 +63124,7 @@ - - - +

                ...wird automatisch vor Konsumieren eines Diff aufgerufen @@ -63731,9 +63134,7 @@ - - - +

                Widerspruch: TreeMutator ist Wegwerf-Objekt @@ -63750,9 +63151,7 @@ - - - +

                Lösungsversuch: doppelte Hülle @@ -63783,9 +63182,7 @@ - - - +

                kann daher TreeMutator konstruieren @@ -63795,9 +63192,7 @@ - - - +

                ...und zwar per mutatorBinding @@ -63807,9 +63202,7 @@ - - - +

                implementiert somit initDiffApplication() @@ -63821,9 +63214,7 @@ - - - +

                TODO: Namensgebung @@ -63849,9 +63240,7 @@ - - - +

                TreeMutator-Binding muß opaque bleiben @@ -63861,9 +63250,7 @@ - - - +

                Buffer-Größen-Management vorsehen @@ -63874,9 +63261,7 @@ - - - +

                das heißt @@ -63903,9 +63288,7 @@ - - - +

                ...das heißt: @@ -63956,9 +63339,7 @@ - - - +

                erscheint mir die am wenigsten überraschende Lösung. @@ -63973,9 +63354,7 @@ - - - +

                erscheint mir fehleranfällig und irreführend für den Nutzer der Schnittstelle. @@ -63995,9 +63374,7 @@ - - - +

                naja, das wäre billig, aber auch wieder beliebig. @@ -64022,9 +63399,7 @@ - - - +

                die Diff-Sprache verlangt, @@ -64081,9 +63456,7 @@ - - - +

                Das ist verständlich aus der Historie: Zunächst einmal war der TreeMutator gedacht als ein Interface, das der client des Diff-Frameworks zu implementieren hat. Nachdem ich diese Übung aber drei mal gemacht hatte, war mir klar, daß dies zu viel verlangt ist. Denn der TreeMutator ist notwendigerweise stark an den Implementierungs-Ansatz im Diff-Framework gebunden. Das heißt, man kann dieses Interface nur implementieren, wenn man diese interne Funktionsweise verstanden hat. Und das Diff-Framework würde seinen Zweck verfehlen, wenn der Nutzer dieses Wissen haben müßte. Also habe ich über den TreeMutator ein Baukastensystem errichtet, welches über Lambdas in den Anwendungskontext gebunden wird. @@ -64123,9 +63496,7 @@ - - - +

                ...falls die clientseitige Datenstruktur einen Index verwendet, um per ID die eigentliche Zieldatenstruktur zu betreten. @@ -64152,9 +63523,7 @@ - - - +

                erwarte eine Funktion getID() @@ -64171,9 +63540,7 @@ - - - +

                ...d.h. man muß dann halt doch noch den Matcher explizit in der DSL konfigurieren @@ -64198,9 +63565,7 @@ - - - +

                ...das ist ein Versuch, den Code für den Leser verständlich zu halten. @@ -64225,9 +63590,7 @@ - - - +

                das ist hier sinnvoll. Das Binding sollte komplexer sein, @@ -64262,9 +63625,7 @@ - - - +

                ...denn es ist sehr verwirrend, welche Signatur denn nun die Lambdas haben müssen @@ -64276,9 +63637,7 @@ - - - +

                ...denn es kann keinen Default-Matcher geben.... @@ -64304,9 +63663,7 @@ - - - +

                ...sonst wird niemand Lambdas bereitstellen können, oder gar Diff-Nachrichten erzeugen. @@ -64364,9 +63721,7 @@ - - - +

                wenn überhaupt, dann im Matcher im Binding-Layer implementieren @@ -64377,9 +63732,7 @@ - - - +

                ...denn wir haben nun mehrere Layer, @@ -64407,9 +63760,7 @@ - - - +

                entfernt, da schlechtes Design @@ -64425,9 +63776,7 @@ - - - +

                entfernt, da schlechtes Design @@ -64453,9 +63802,7 @@ - - - +

                anscheinend nicht notwendig @@ -64506,9 +63853,7 @@ - - - +

                es ist wohl nicht ein spezieller Diff. Es tritt sogar auf, wenn man zweimal den gleichen Diff schickt @@ -64568,9 +63913,7 @@ - - - +

                Diff ist eine abstrakte Quelle, @@ -64588,9 +63931,7 @@ - - - +

                Dekorator-Prinzip. @@ -64603,9 +63944,7 @@ - - - +

                MutationMessage::updateDiagnostics() @@ -64622,9 +63961,7 @@ - - - +

                ...diejenige, die zum Zeitpunkt des updateDiagnostics() noch anstand @@ -64800,9 +64137,7 @@ - - - +

                und zwar typischerweise per Indirektion an einen low-level Allocator, bei dem es sich aber auch um einen Slot in einem Allocation-Cluster handeln kann @@ -64814,9 +64149,7 @@ - - - +

                • @@ -64859,9 +64192,7 @@ - - - +
                  • @@ -64888,9 +64219,7 @@ - - - +

                    bisher "GUI" @@ -64907,9 +64236,7 @@ - - - +

                    bisher "Proc-Layer" @@ -64926,9 +64253,7 @@ - - - +

                    bisher "Backend" @@ -64981,9 +64306,7 @@ - - - +

                    wenn im Einzelfall bereits mit der INS-Nachricht bestimmte global sichtbare Properties mit gegeben sein müssen @@ -65042,9 +64365,7 @@ - - - +

                    Es fällt auf, daß ich in dem Entwurf von 2011 die Allocation selber nur als Referenz rausgegeben habe, und dann für jeden darauf eröffneten Slot ein smart-Handle. Zunächst einmal sieht das vernünftig  aus, weil die Slots typischerweise sofort noch im gleichen Thread belegt werden. Was ist aber wenn...↯ — kann sich dann die belegte Allocation verklemmen? @@ -65090,9 +64411,7 @@ - - - +

                    GUI und Session schicken Nachrichten. @@ -79735,6 +79054,13 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
                    + + + + + + + @@ -79758,7 +79084,9 @@ Date:   Thu Apr 20 18:53:17 2023 +0200

                    - + + + @@ -79863,6 +79191,16 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
                    + + + + + + + + + +