diff --git a/doc/technical/howto/crackNuts.txt b/doc/technical/howto/crackNuts.txt index 96f753f5b..5695ee798 100644 --- a/doc/technical/howto/crackNuts.txt +++ b/doc/technical/howto/crackNuts.txt @@ -7,17 +7,17 @@ Some nasty problems are recurring time and again. Maybe a trick could be found s in the net, and a library function was created to settle this damn topic once and for all. Maybe even a nice test and demo is provided. And then the whole story will be forgotten. -_Sounds familiar?_ => ☹☻☺ ~[red]#then please leave a note here...#~ +_Sounds familiar?_ => ☹☻☺👻 ~[red]#... then please consider to leave some traces here ...#~ Methods ------- Mathematics ~~~~~~~~~~~ -- some basic descriptive statistics computation are defined in 'lib/stat/statistic.hpp' +- some basic descriptive statistics computations are defined in 'lib/stat/statistic.hpp' - the simple case for _linear regression_ is also implemented there - Gnuplot provides also common statistics functions, which may come in handy when the - goal is anyway to create a visualisation (-> see <>) + goal is anyway to create a visualisation (-> see <<_investigation,below>>) @@ -43,6 +43,50 @@ visualisation:: * 'lib/gnuplot-gen.hpp' provides some pre-canned scripts for statistics plots * used by the 'stress-test-rig.cpp', in combination with 'data.hpp' to collect measurement results +Testing +~~~~~~~ +verify structured data:: + build a diagnostic output which shows the nesting + - use configuration by template parameters and use simple numbers or strings as part components + - render the output and compare it with `""_expect` (the `ExpectStr` -> see 'lib/test/test-helper.hpp') + - break the expect-string into parts with indentation, by exploiting the C _string gaps_ + +However, this works only up to a certain degree of complexity. + +A completely different approach is to transform the structured result-data into an ETD (`GenNode` tree) +and then directly compare it to an ETD that is created in the test fixture with the DSL notation (`MakeRec()`) + +verify floating point data:: ++ +- either use approximate comparison + + * `almostEqual()` -> see 'lib/test/test-helper.hpp' + * 'util-quant.hpp' has also an `almostEqual(a,b, ulp)` + * [yellow-background]#TODO 2024# should be sorted out -> https://issues.lumiera.org/ticket/1360[#1360] + +- or render the floating point with the diagnostic-output functions, which deliberately employ + a built-in rounding to some sensible amount of places (which in most cases helps to weed-out the + ``number dust'') +- simply match it against an `ExpectStr` -- which implicitly converts to string, thereby + also applying the aforementioned implicit rounding ++ +-> See also <<_formatting,about formatting>> below; in a nutshell, `#include 'lib/format-output.hpp'` + +verify fluctuating values:: + A first attempt should be made to bring them into some regular band. This can be achieved by + automatically calibrating the measurement function (e.g. do a timing calibration beforehand). + Then the actual value can be matched by the `isLimited(l, val, u)` notation (see 'lib/uitl.hpp') +test with random numbers:: ++ +- Control the seed! Invoke the seedRand() function once in each test instance. + This draws an actually random number as seed and re-seeds the `lib::defaultGen`. The seed is written to the log. +- But the seed can be set to a fixed value with the `--seed` parameter of the test runner. + This is how you reproduce a broken test case. +- Moreover, be sure either to draw all random values from the `defaultGen` or to build a well organised + tree of PRNG instances, which seed from each other. This is especially valuable when the test starts + several threads; each thread should use its own generator then (yet this can be handled sloppy if + the _quality_ of the random number actually does not matter!) + Common Tasks @@ -51,6 +95,44 @@ Data handling ~~~~~~~~~~~~~ persistent data set:: use the `lib::stat::DataTable` ('data.hpp') with CSV rendering -> see 'data-csv-test.cpp' +structured data:: + represent it as Lumiera ETD, that is as a tree of `GenNode` elements. ++ +- be sure to understand the _essential idea:_ the receiver should act based on _knowledge_ + about the structure -- not by _introspection and case-switching_ +- however -- for the exceptional case that you _absolutely must discover_ the structure, + then use the visitor feature. This has the benefit of concentrating the logic at one place. +- can represent the notion of a nested scope, with iteration +- provides a convenient DSL notation, especially for testing, which helps to explain + the expected structure visually . +- also useful as output format, both for debugging and because it can be matched against + an expected structure, which is generated with the DSL notation. +- [yellow-background]#(planned)# will be able to map this from/to JSON easily. + +Iterating +~~~~~~~~~ +Lumiera iterators:: + They are designed for convenient usage with low boilerplate (even while this means wasting + some CPU cycles or memory). They are deliberately much simpler than STL iterators, can be + iterated only once, can be bool checked for iteration end, and can be used both in a for-each + construct and in while-loops. +IterExplorer:: + The function `lib::explore(IT)` builds on top of these features and is meant to basically + iterate anything that is iterable -- so use it to abstract away the details. ++ +- can be filtered and transformed +- can be reduced or collected into a vector +- can be used to build complex layered search- and evaluation schemes +STL-adaptors:: + A set of convenience functions like `eachElm(CON)` -- available in 'iter-adapter-stl.hpp'. + Also allows to iterate _each key_ or _value_, and to take snapshots +IterSource:: + This is a special Lumiera iterator implementation which delegates through a virtual (OO) interface. + Thus the source of the data can be abstracted away and switched transparently at runtime. + Especially relevant for APIs, to produce a data sequence once and without coupling to details; + even the production-state can be hooked up into the IterSource instance (with a smart-ptr). + This allows e.g. to send a Diff to the UI through the UI-Bus, while the actual generation + and application both happen demand-driven or _lazy..._ Formatting @@ -87,8 +169,8 @@ build-from-anything:: A ready-made templated typedef `lib::metadisable_if_self` can be found in 'lib/meta/util.hpp' -Varargs -~~~~~~~ +Variadics +~~~~~~~~~ pick and manipulate individually:: The key trick is to define an _index sequence template,_ which can then be matched against a processing template for a single argument; and the latter can have partial specialisations @@ -99,11 +181,12 @@ pick and manipulate individually:: into the `lib::meta::Types` ([yellow-background]#⚠ still broken in 2024# see https://issues.lumiera.org/ticket/987[#987], use `lib::meta::TySeq` from 'variadic-helper.hpp' as workaround... + -apply functor to each:: +apply functor to each tuple element:: A common trick is to use `std::apply` in combination with a _fold-expression_ + -- provided as `lib::meta::forEach` in 'lib/meta/util.hpp +- provided as `lib::meta::forEach` in 'lib/meta/tuple-helper.hpp - The design of the `DataTable` with CSV-Formatting is based on this technique, see 'lib/stat/data.hpp' +- 'lib/iter-zip.hpp' uses this to construct a tuple-of-iterators - 'test-rand-ontology.cpp' uses this in `manipulateFrame()` to accept an arbitrary number of input chains + unpack iterator into tuple:: diff --git a/src/lib/format-util.hpp b/src/lib/format-util.hpp index b77c178ca..f0f48b413 100644 --- a/src/lib/format-util.hpp +++ b/src/lib/format-util.hpp @@ -160,7 +160,7 @@ namespace util { _RangeIter(IT&& srcIter) : iter(std::forward(srcIter)) { } - _RangeIter(IT const& srcIter) + _RangeIter(IT const& srcIter) // note: copy here : iter(srcIter) { } @@ -170,7 +170,9 @@ namespace util { /** * enumerate a collection's contents, separated by delimiter. - * @param coll something that is standard-iterable + * @param coll something that is standard- or Lumiera-iterable + * @note Lumiera-iterator is copied when given by ref, otherwise moved, + * while in all other cases the source container is taken by const& * @return all contents converted to string and joined into * a single string, with separators interspersed. * @remarks based `ostringstream`; additionally, we use our @@ -191,7 +193,7 @@ namespace util { join (CON&& coll, string const& delim =", ") { using Coll = typename lib::meta::Strip::TypePlain; - _RangeIter range(std::forward(coll)); + _RangeIter range(std::forward(coll)); // copies when CON is reference auto strings = stringify (std::move (range.iter)); if (!strings) return ""; diff --git a/src/lib/iter-adapter.hpp b/src/lib/iter-adapter.hpp index c87de30eb..313f50509 100644 --- a/src/lib/iter-adapter.hpp +++ b/src/lib/iter-adapter.hpp @@ -936,7 +936,7 @@ namespace lib { bool isValid () const { - return (i_!= INT()) && (i_ < e_); // NOTE: use comparison to detect iteration end + return i_ < e_; // NOTE: use comparison to detect iteration end } bool diff --git a/src/lib/iter-explorer.hpp b/src/lib/iter-explorer.hpp index e1c91f2e9..73bb2b539 100644 --- a/src/lib/iter-explorer.hpp +++ b/src/lib/iter-explorer.hpp @@ -517,8 +517,8 @@ namespace lib { BaseAdapter(SRC const& src) : SRC{src} { } BaseAdapter(SRC && src) : SRC{forward (src)} { } - void expandChildren() { } - size_t depth() const { return 0; } + void expandChildren() { } ///< collaboration: recurse into nested scope + size_t depth() const { return 0; } ///< collaboration: number of nested scopes }; diff --git a/src/lib/iter-zip.hpp b/src/lib/iter-zip.hpp new file mode 100644 index 000000000..a3bfd88f7 --- /dev/null +++ b/src/lib/iter-zip.hpp @@ -0,0 +1,54 @@ +/* + ITER-ZIP.hpp - join several iterators into a iterator-of-tuples + + Copyright (C) + 2024, Hermann Vosseler + +  **Lumiera** 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. See the file COPYING for further details. + +*/ + +/** @file iter-zip.hpp + ** Iterator builder to combine several iterables into a tuple sequence. + ** + ** @see IterZip_test + ** @see iter-explorer.hpp + ** + */ + + +#ifndef LIB_ITER_ZIP_H +#define LIB_ITER_ZIP_H + + +#include "lib/error.hpp" +#include "lib/iter-explorer.hpp" +#include "lib/meta/tuple-helper.hpp" +//#include "lib/util.hpp" + +//#include +//#include + + +namespace lib { + +// using util::unConst; + + namespace iter { + /** */ + } // namespace lib::iter + + + + /** */ + + + /** convenience free function to build an iterable sequence */ + + + +} // namespace lib +#endif /*LIB_ITER_ZIP_H*/ diff --git a/src/lib/meta/tuple-helper.hpp b/src/lib/meta/tuple-helper.hpp index c7b371bd7..4ec4b1120 100644 --- a/src/lib/meta/tuple-helper.hpp +++ b/src/lib/meta/tuple-helper.hpp @@ -48,6 +48,7 @@ #include "lib/meta/util.hpp" #include +#include namespace util { // forward declaration @@ -61,24 +62,76 @@ namespace util { // forward declaration namespace lib { namespace meta { + /** trait to detect tuple types */ + template + struct is_Tuple + : std::false_type + { }; + + template + struct is_Tuple> + : std::true_type + { }; + + template + struct is_Tuple> + : std::true_type + { }; + + template + using enable_if_Tuple = lib::meta::enable_if>>; + + template + using disable_if_Tuple = lib::meta::disable_if>>; + + /** - * Helper: perform some arbitrary operation on each element of a tuple. + * Tuple iteration: perform some arbitrary operation on each element of a tuple. * @note the given functor must be generic, since each position of the tuple * may hold a data element of different type. * @remark credits to David Vandevoorde (member of C++ committee) for using * std::apply to unpack the tuple's contents into an argument pack and * then employ a fold expression with the comma operator. */ - template - void forEach (std::tuple&& tuple, FUN fun) + template> + void + forEach (TUP&& tuple, FUN fun) { - std::apply ([&fun](auto&... elms) - { - (fun(elms), ...); - } - ,tuple); + std::apply ([&fun](auto&&... elms) + { + (fun (std::forward (elms)), ...); + } + ,std::forward (tuple)); } + /** + * Apply some arbitrary function onto all elements of a tuple. + * @return a new tuple constructed from the results of this function + * @note the functor must be generic and able to work with all element types + * @warning pay attention to references; the function can take arguments by-ref + * and manipulate them (side-effect), and it may return references, + * which will be placed as such into the result tuple; furthermore, + * the argument tuple can also be taken as reference... + * @remark The tuple constructor invocation is preceded by a regular argument evaluation, + * which has unspecified evaluation order (even in C++17); _no assumptions_ can be + * made regarding the order the functor will see the source tuple elements. + * Notably this differs from #forEach, where a fold-expression with comma-operator + * is used, which is guaranteed to evaluate from left to right. + */ + template> + auto + mapEach (TUP&& tuple, FUN fun) + { + return std::apply ([&fun](auto&&... elms) + { //..construct the type explicitly (make_tuple would decay fun result types) + using Tuple = std::tuple (elms))) ...>; + return Tuple (fun (std::forward (elms)) ...); + } + ,std::forward (tuple)); + } + + + namespace { // rebinding helper to create std::tuple from a type sequence @@ -153,17 +206,6 @@ namespace meta { }; - /** trait to detect tuple types */ - template - struct is_Tuple - : std::false_type - { }; - - template - struct is_Tuple> - : std::true_type - { }; - diff --git a/tests/12metaprogramming.tests b/tests/12metaprogramming.tests index 4ac6980d1..2533b0d01 100644 --- a/tests/12metaprogramming.tests +++ b/tests/12metaprogramming.tests @@ -356,6 +356,11 @@ return: 0 END +PLANNED "Tuple-zipped iterators" IterZip_test < : Yes out: can_STL_ForEach : Yes diff --git a/tests/library/iter-explorer-test.cpp b/tests/library/iter-explorer-test.cpp index 07e41743a..18444728f 100644 --- a/tests/library/iter-explorer-test.cpp +++ b/tests/library/iter-explorer-test.cpp @@ -184,11 +184,11 @@ namespace test{ }; - /** Diagnostic helper: join all the elements from a _copy_ of the iterator */ + /** Diagnostic helper: join all the elements from the iterator */ template inline string materialise (II&& ii) - { + { // note: copy here when given by-ref return util::join (std::forward (ii), "-"); } diff --git a/tests/library/iter-zip-test.cpp b/tests/library/iter-zip-test.cpp new file mode 100644 index 000000000..5143df1b9 --- /dev/null +++ b/tests/library/iter-zip-test.cpp @@ -0,0 +1,266 @@ +/* + IterZip(Test) - verify the iterator-combining iterator + + Copyright (C) + 2024, Hermann Vosseler + +  **Lumiera** 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. See the file COPYING for further details. + +* *****************************************************************/ + +/** @file iter-stack-test.cpp + ** unit test \ref IterZip_test + */ + + + +#include "lib/test/run.hpp" +#include "lib/iter-zip.hpp" +#include "lib/test/test-helper.hpp" +#include "lib/test/diagnostic-output.hpp"/////////////TODO +#include "lib/format-util.hpp" +#include "lib/util.hpp" + +#include + + +namespace lib { +namespace test{ + +// using ::Test; +// using util::isnil; +// using LERR_(ITER_EXHAUST); + using lib::meta::forEach; + using lib::meta::mapEach; + using std::make_tuple; + using std::tuple; + using std::get; + + namespace {// Test Fixture ... + + auto num5() { return NumIter{0,5}; } + + template + auto numS() { return explore(num5()).transform([](int i){ return i*N + S; }); } + auto num31(){ return numS<3,1>(); } + auto num32(){ return numS<3,2>(); } + auto num33(){ return numS<3,3>(); } + + auto hexed = [](int i){ return util::showHash(i,1); }; + + + /** Diagnostic helper: join all the elements from the iterator */ + template + inline string + materialise (II&& ii) + { + return util::join (std::forward (ii), "-"); + } + } + + + +///////////////////////////////////////// +///////////////////////////////////////// + #define TYPE(_EXPR_) showType() + + + + + + /*********************************************************************************//** + * @test demonstrate construction and verify behaviour of a combined-iterator builder. + * - construction from arbitrary arguments by tuple-mapping a builder function + * + * @see IterExplorer + * @see IterExplorer_test + */ + class IterZip_test : public Test + { + + virtual void + run (Arg) + { + test_Fixture(); + demo_mapToTuple(); + demo_construction(); + UNIMPLEMENTED ("nebbich."); + } + + + /** @test demonstrate how the test Fixture is used */ + void + test_Fixture() + { + CHECK (materialise (num5() ) == "0-1-2-3-4"_expect); + CHECK (materialise (num31() ) == "1-4-7-10-13"_expect); + CHECK (materialise (num33() ) == "3-6-9-12-15"_expect); + CHECK (materialise (num32() + .transform(hexed) + ) == "02-05-08-0B-0E"_expect); + } + + + /** @test demonstrate to apply a function to tuple contents */ + void + demo_mapToTuple() + { + auto t1 = make_tuple (41u, 0.61803, '6'); + CHECK (t1 == "«tuple»──(41,0.61803,6)"_expect ); + + auto t1f = mapEach (t1, [](auto v){ return v+1; }); + CHECK (t1f == "«tuple»──(42,1.61803,55)"_expect ); // ASCII('6') ≙ 54 promoted to int + + auto t1ff = mapEach (t1, [](auto& v){ v += 1; return v; }); + CHECK (t1ff == "«tuple»──(42,1.61803,7)"_expect ); + CHECK (t1f == "«tuple»──(42,1.61803,55)"_expect ); + CHECK (t1 == "«tuple»──(42,1.61803,7)"_expect ); // src-tuple t1 affected by side-effect + + // tuple may hold a reference.... + tuple t2{get<2>(t1),get<2>(t1ff)}; + CHECK (t2 == "«tuple»──(7,7)"_expect ); + + auto t2f = mapEach (t2, [](auto& v){ v -= 1; return v; }); + CHECK (t2f == "«tuple»──(6,6)"_expect ); // function-result is value, thus res-tuple holds values + CHECK (t2 == "«tuple»──(6,6)"_expect); // ...but src-tuple t2 was affected by side-effect + CHECK (t1ff == "«tuple»──(42,1.61803,6)"_expect ); // ...which in turn holds a ref, so value in t1ff changed + CHECK (t1 == "«tuple»──(42,1.61803,7)"_expect ); // ...while the other one was picked by value => t1 unchanged + + // function may return references.... + auto refr = [](auto&& v) -> decltype(auto) { return v; }; + int five = 5; + CHECK (TYPE (refr(five)) == "int&"_expect); + CHECK (TYPE (refr(5 )) == "int&"_expect); + + auto t2r = mapEach (t2, refr); + CHECK (t2r == "«tuple»──(6,6)"_expect ); // function yields references, which are placed into res-tuple + + forEach (t2r, [](auto& v){ v +=23; }); + CHECK (t2r == "«tuple»──(M,M)"_expect ); // apply operation with side-effect to the last res-tuple t2r + CHECK (t2 == "«tuple»──(M,M)"_expect ); // the referred src-tuple t2 is also affected + CHECK (t2f == "«tuple»──(6,6)"_expect ); // (while previously constructed t2f holds values unaffected) + CHECK (t1 == "«tuple»──(42,1.61803,7)"_expect ); // the first elm in t2 was bound by value, so no side-effect + CHECK (t1ff =="«tuple»──(42,1.61803,M)"_expect ); // but the second elm in t2 was bound by ref to t1ff + } + + + template + auto + buildIterTuple (ITS&& ...iters) + { + return make_tuple (lib::explore (std::forward (iters)) ...); + } + + + /** @test demonstrate how a tuple-zipping iterator can be constructed */ + void + demo_construction() + { + // let's start with the basics... + // We can use lib::explore() to construct a suitable iterator, + // and thus we can apply it to each var-arg and place the results into a tuple + auto arry = std::array{3u,2u,1u}; + auto iTup = buildIterTuple (num5(), arry); + + CHECK (TYPE(iTup) == "tuple > >, " + "IterExplorer&> > > >"_expect); + + // and we can use them as iterators... + auto iterate_it = [](auto& it){ ++it; }; + auto access_val = [](auto& it){ return *it; }; + + forEach (iTup, iterate_it); + auto vTup = mapEach (iTup, access_val); + CHECK (vTup == "«tuple»──(1,2)"_expect); + + using ITup = decltype(iTup); + + // Next step: define a »product iterator« + // by mapping down each of the base operations onto the tuple elements + struct ProductCore + { + ITup iters_; + + ProductCore(ITup&& iterTup) + : iters_{move (iterTup)} + { } + + /* === »state core« protocol API === */ + bool + checkPoint() const + { + bool active{true}; // note: optimiser can unroll this + forEach (iters_, [&](auto& it){ active = active and bool(it); }); + return active; + } + + ITup& + yield() const + { + return unConst(iters_); // ◁─────────────── note: we expose the iterator-touple itself as »product« + } + + void + iterNext() + { + forEach (iters_, [](auto& it){ ++it; }); + } + }; + + // ....and now we're essentially set! + // use library building blocks to construct a tuple-iter-explorer... + auto ii = explore (ProductCore{buildIterTuple (num5(), arry)}) + .transform ([&](ITup& iTup){ return mapEach (iTup, access_val); }) + ; + + // hold onto your hat!!! + CHECK (TYPE(ii) == "IterExplorer<" + "IterableDecorator<" + "tuple, " // ◁──────────────────────────────── this is the overall result type + "CheckedCore<" + "iter_explorer::Transformer<" // ◁──────────────────────────────── the top-layer is a Transformer (to access the value from each src-iter) + "iter_explorer::BaseAdapter<" + "IterableDecorator<" // ◁──────────────────────────── the product-iterator we constructed + "tuple<" // ◁──────────────────────────── ....exposing the iterator-tuple as „result“ + "IterExplorer<" // ◁───────────────────────────────── the first source iterator (directly wrapping NumIter) + "iter_explorer::BaseAdapter > >, " + "IterExplorer<" // ◁───────────────────────────────── the second source iterator (based on a STL collection) + "iter_explorer::BaseAdapter<" + "iter_explorer::StlRange&> " + "> " + "> " + ">, " + "CheckedCore<" // ◁──────────────────────────── ....and using the given ProductCore as »state core« + "IterZip_test::demo_construction()::ProductCore> > >, " + "tuple " // ◁──────────────────────────────── back to top-layer: result-type of the Transformer + "> " + "> " + "> " + ">"_expect); + + // .... + // This is indeed a valid iterator, + // that can be iterated for three steps + // (limited by the shorter sequence from the array) + // (first value from num5(), second from the array) + CHECK (materialise (ii) == "«tuple»──(0,3)-" + "«tuple»──(1,2)-" + "«tuple»──(2,1)"_expect); + } +/* +SHOW_EXPR + (materialise ( + num32().transform(hexed) + ) + ) +*/ + }; + + + LAUNCHER (IterZip_test, "unit common"); + + +}} // namespace lib::test diff --git a/tests/library/text-template-test.cpp b/tests/library/text-template-test.cpp index ea3fd7543..fce2f2617 100644 --- a/tests/library/text-template-test.cpp +++ b/tests/library/text-template-test.cpp @@ -393,7 +393,7 @@ for} tail... TextTemplate t3{" 1 ${if a} 2 ${if b} 3 ${else} ${b} ${endif b} 4 ${else}${if a} 5 ${else} ${a} ${endif a}${endif a} 6 "}; CHECK (t3.render("a=2,b=3") == " 1 2 3 4 6 "_expect ); // ^^^^^ Note can never be true here CHECK (t3.render("a=2,b=0") == " 1 2 0 4 6 "_expect ); - CHECK (t3.render("a=0,b=3") == " 1 0 6 "_expect ); // thus if a ≙ false we see only 1 §{a} 6 + CHECK (t3.render("a=0,b=3") == " 1 0 6 "_expect ); // thus if a ≙ false we see only 1 ${a} 6 CHECK (t3.render("a=0,b=0") == " 1 0 6 "_expect ); } diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 80e158df1..8a096cc92 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -18826,9 +18826,7 @@ - - - +

ersichtlich aus Trace-Meldungen, welche ich zur Analyse in den get_preferred_*-Funktionen und beim Setzen der Allokation hatte. Auch auf dem Kind-Elementen sehe ich keine erneuten Aufrufe @@ -19097,9 +19095,7 @@ - - - +

Assignment-Operatoren binden weniger stark als Wertevergleiche, daher ist er immer nach dem ersten Reduktionsschritt ausgestiegen @@ -19562,9 +19558,7 @@ - - - +

...wobei das eigentlich nur für den einfachen Use-Case relevant ist; denn im Falle eines komplexen Content-Containers wird die Steuerung, in welche der Container ohnehin eingebunden sein muß (z.B. der Clip-Controller) wohl auf direktem Wege agieren, ohne durch das ElementBox-Widget zu gehen @@ -19992,9 +19986,7 @@ - - - +

0000001052: INFO: steam-dispatcher.cpp:301: worker_2: processCommands: +++ dispatch Command("test_fake_injectSequence_1") {exec} @@ -20782,9 +20774,7 @@ - - - +

von Element-Typ A nach Element-Typ B... @@ -22463,9 +22453,7 @@ - - - +

grundsätzliches @@ -25747,9 +25735,7 @@ - - - +

Fazit: DisplayFrame kann sich auf Parent verlassen @@ -37769,9 +37755,7 @@ - - - +

das bedeutet:
@@ -40963,9 +40947,7 @@ - - - +

...einmal wirklich sicheren Code schreiben... @@ -42637,9 +42619,7 @@ - - - +

eindeutig: wrap-around in der relativen Änderung @@ -43559,9 +43539,7 @@ - - - +

es wäre schon überraschend, wenn der Scrollbalken plötzlich nicht mehr reagiert.... @@ -43970,9 +43948,7 @@ - - - +

  • @@ -45616,9 +45592,7 @@ - - - +

    ...es kommt (vielleicht) überhaupt nicht darauf an, @@ -45879,9 +45853,7 @@ - - - +

    (GlobalCtx)->WindowLocator->UIComponentAccessor @@ -52769,6 +52741,236 @@ + + + + + + + +

    + das heißt, der erste erschöpfte Iterator terminiert die gesamte Sequenz +

    + + +
    +
    + + + + +

    + damit man auch die Fälle erschlagen kann, in denen irgendwo eine Index-Variable gebraucht wird; oft ist das nämlich der einzige Grund, dann doch eine klassische For-Iteration zu machen +

    + + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + stelle fest: ich darf kein Quell-Tupel konstruieren +

    + + +
    +
    + + + + + + + + + + + + + +

    + Es ist eigentlich nur eine Verpackung für std::apply, welches man mit dem gleichen Aufruf-Aufwand stets auch direkt aufrufen könnte. Hinzu kommt, daß nun die Argument-Ordnung auf dem API entweder links-rum oder rechts-rum nicht paßt und verwirrend ist +

    + + +
    +
    + + + + +

    + der spezielle Trick mit dem inneren Lambda ist nur für ein Tupel notwendig, um die Tupel-Elemente flexibel aber korrekt wieder in die Hand zu bekommen (alternativ mußte man vor C++17 mit einem Index-Seq-Iterator arbeiten, was sehr undurchsichtig ist) +

    + +
    + +
    + + + + + + +
    +
    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

    + ...und damit sind nämlich beide Ansätze letzlich wieder versöhnt. Das war die wichtige Einsicht beim zweiten Design-Anlauf. Beim ersten Anlauf wollte ich »brav« sein und Werte liefern, und das ganze Design wurde dadurch extrem undurchschaubar und wackelig, denn natürlich wurde beim ersten Kontakt mit der Realität klar, daß man dann smart-Pointer durch die Gegend schiebt. +

    + + +
    +
    + + + + +

    + ....aber auch der Punkt, an dem es immer wieder gefährlich wird. +

    +

    + Warum ist das so wichtig? +

    +

    + Antwort: weil nur dadurch ein Iterator durch eine Abstraktions-Barriere hindurch arbeiten kann. Der Aufbau eines komplexen Such-Algorithmus erfolgt meist in mehreren Layern, und man möchte eben nicht, daß irgendwo von unten Werte geliefert werden müssen, die dann oben »passen«. Sondern man möchte mehrere (oft virtuelle) Funktionsaufrufe aufeinander mappen. Und nicht zuletzt dadurch kann man geziehlt einzelne Werte in einem anderen Implementierungs-Realm zur Manipulation exponieren. Denn tatsächlich ist ein solcher IterExplorer eine View in eine State-Core +

    + +
    +
    +
    + + + + +

    + Im Normalfall liefert schon der Quell-Iterator eine Referenz (oder const-Referenz). Und die wird dann durch das Value-Tuple durchgemappt und zeigt eigentlich in die State-Core oder einen unterliegenden Container. Wenn aber an irgend einer Stelle tatsächlich ein Wert geliefert werden muß, so wird das auf diesem Level stets abgefedert, und die stabile Storage für den Wert liegt dann im Iterator selber (im ItemWrapper) +

    + +
    +
    +
    +
    + + + + + + + + + + + + +

    + (ob dieserer test portabel ist....) +

    + + +
    + + +
    +
    + + + +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + +
    @@ -92905,7 +93107,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
    - + @@ -92913,9 +93115,61 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
    Im Datenblock liegt lediglich eine Sequenz von 1014 Bytes — man möchte darauf jedoch flexibel aber getypt zugreifen können

    +
    + +
    + + + + +

    + TrackingHeapBlockProvider_test +

    +

    + BufferProviderProtocol_test +

    +

    + OutputSlotProtocol_test +

    +
    - + + + + + +

    + 0000000515: INFO: testframe.cpp:168: thread_1: getFrame: Growing channel #0 of test frames 0 -> 1 elements. +

    +

    + 0000000516: CHECK: buffer-provider-protocol-test.cpp:107: thread_1: verifySimpleUsage: (testData(0) == checker.accessMemory (0)) +

    + + +
    + +
    + + + + +
      +
    • + könnte daran liegen, daß ich für testData die Argumente verkehrt herum verdrahtet hatte; da alle drei Tests direkt mit testData arbeiten und selber etwas in den Speicher legen, muß sich dort diese Verdrehung auch irgendwo im Code niedergeschlagen haben. Ist natürlich jetzt nicht einfach zu finden, ohne diese Custom_Allocatoren zu verstehen.... +
    • +
    • + es könnte aber auch am anderen Memory-Layout liegen.... +
    • +
    • + oder am zusätzlichen Header-Check, der  nun stattfindet +
    • +
    + + +
    + +