Library: a subtle fix to iterator value-type deduction
After some investigation, I seem to understand the problem better now.
This builds upon the Introduction of our `ValueTraits´ two years ago:
e176e54004
The root cause to these ongoing sporadic problems with type deduction
seems to be that we encounter a **clash of concepts**: What Lumiera calls
an »Iterator« is essentially different to the order of terms used by the STL.
In the STL, an iterator can be seen as an ''abstracted pointer'', and is
thus only meaningful in conjunction with a backing container. It is also
this backing STL container that provides the value-type definitions.
In Lumiera however, an ''Iterator is a promise to retrieve a sequence of evaluations''.
For that reason, the Iterator itself provides value-type definitions,
and the notion of "iterable" can largely be conflated with "Lumiera Iterator".
Now, the `lib::RangeIter` can act as a bridge between both usage patterns,
and thus it is possible to create a `RangeIter` both from a Lumiera Iterator
or from a STL container. (As an aside, this is a similar idea as promoted
by the "Range TS" in C++20 -- yet the Lumiera iterator concept is much more
abstract and allows evaluations like tree expansion and search algorithms,
which would be never possible with the STL based notion of iteration)...
Thus the value type binding in `RangeIter` is the only correct place
where to mitigate this ''clash of concepts:''
* if the `IT` parameter is a Lumiera iterator, we need to retrieve
the type bindings defined therein
* otherwise, we can assume that `IT` is an STL iterator and thus
essentially a pointer, and we must strip that pointer and work from there.
This commit is contained in:
parent
bf77e53b4c
commit
68460d4900
2 changed files with 238 additions and 6 deletions
|
|
@ -782,17 +782,22 @@ namespace lib {
|
|||
|
||||
|
||||
/**
|
||||
* Accessing a STL element range through a Lumiera forward iterator,
|
||||
* Access a STL element range through a »Lumiera Forward Iterator«.
|
||||
* An instance of this iterator adapter is completely self-contained
|
||||
* and allows to iterate once over the range of elements, until
|
||||
* `pos==end`. Thus, a custom container may expose a range of
|
||||
* elements of an embedded STL container, without controlling
|
||||
* the details of the iteration (as is possible using the
|
||||
* the details of the iteration (as would be possible using the
|
||||
* more generic IterAdapter).
|
||||
* @note
|
||||
* - when IT is just a pointer, we use the pointee as value type
|
||||
* - but when IT is a class, we expect the usual STL style nested typedefs
|
||||
* `value_type`, `reference` and `pointer`
|
||||
* - when IT is a STL iterator, we use the pointee as value type
|
||||
* - but when IT is a »Lumiera Forward Iterator«, we need to evaluate
|
||||
* nested typedefs `value_type`, `reference` and `pointer`
|
||||
* @remark the reason for this tricky distinction is a _clash of concepts:_
|
||||
* In Lumiera, »Iterator« has a special meaning and can used on itself,
|
||||
* while in the STL, an »Iterator« is a pointer in disguise and is
|
||||
* only meaningful when the backing container is known. This adaptor,
|
||||
* RangeIter, acts as a bridge and levels those usage styles.
|
||||
*/
|
||||
template<class IT>
|
||||
class RangeIter
|
||||
|
|
@ -800,7 +805,9 @@ namespace lib {
|
|||
IT p_;
|
||||
IT e_;
|
||||
|
||||
using _ValTrait = meta::ValueTypeBinding<meta::remove_pointer_t<IT>>;
|
||||
static constexpr bool _is_LumieraIter = meta::can_IterForEach<IT>::value;
|
||||
using _ValTrait = std::conditional_t<_is_LumieraIter, meta::ValueTypeBinding<IT>
|
||||
, meta::ValueTypeBinding<meta::remove_pointer_t<IT>>>;
|
||||
|
||||
public:
|
||||
using pointer = _ValTrait::pointer;
|
||||
|
|
|
|||
|
|
@ -53256,6 +53256,231 @@
|
|||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#fafe99" COLOR="#fa002a" CREATED="1774470739518" ID="ID_1513532591" MODIFIED="1774475574352" TEXT="Typ-Ableitung biegt in den Value-Traits falsch ab">
|
||||
<icon BUILTIN="broken-line"/>
|
||||
<node BACKGROUND_COLOR="#e1b4a0" COLOR="#690f14" CREATED="1774470739518" ID="ID_1029964723" MODIFIED="1774473599797" TEXT="unsere Value-Traits sind vermutlich nicht auf nested Iterators ausgelegt">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Wobei mir nicht klar ist, ob das Problem grundsätzlicher Natur ist.
|
||||
</p>
|
||||
<p>
|
||||
Jedenfalls haben wir in den ValueTypeTraits (aber <i>nicht </i>in den RefTraits) eine Verzweigung, die prüft, ob nested-typedefs da sind. Wenn nicht, gehen wir in die RefTraits (d.h. wir leveln den Einstieg über Referenz oder Value). Wenn aber ja, dann nehmen wir die nested Typedefs
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="clanbomber"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#690f14" CREATED="1774470901308" ID="ID_1506170632" MODIFIED="1774471215712" TEXT="was hier nun passiert...">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<ul>
|
||||
<li>
|
||||
wir steigen ein über den RangeIter<NestedIter *>. Warum? weil im Testfall die Quelle ein Array ist, d.h. VAL* ist der "iterator"
|
||||
</li>
|
||||
<li>
|
||||
erstes Problem ist schon mal, daß wir einen Pointer strippen. Warum habe ich das so geschrieben?  Ohne dieses Pointer-strippen würde (vermutlich) die Weiche in ValueTypeBindings niemals ansprechen können
|
||||
</li>
|
||||
<li>
|
||||
und dann ist das andere Problem (Folgeprolem? oder die eigentliche Ursache?) daß der pointer-gestrippte Wert eben ein nested Iterator ist, d.h. die Weiche in ValueTypeBinding spricht an
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
⟹ wir bekommen bereits auf der Ebene den <b>nested value Type</b>, aber unser Code würde erwarten, an der Stelle einen Nested-Iterator in die Hand zu bekommen. Damit steigt die ganze weitere Typenbehandlungs-Logik aus
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="broken-line"/>
|
||||
</node>
|
||||
<node CREATED="1774471570043" ID="ID_1241566646" MODIFIED="1774471605751" TEXT="Bauchgefühl ⟹ das strip-pointer in RangeIter ist eine »verschleppte Altlast«">
|
||||
<node CREATED="1774471625212" ID="ID_451783687" MODIFIED="1774471702711" TEXT="dafür spricht das Changeset e176e540043177">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
commit e176e540043177b57b9dfd00ac1b1c837d689a6c
|
||||
</p>
|
||||
<p>
|
||||
Author: Ichthyostega <prg@ichthyostega.de>
|
||||
</p>
|
||||
<p>
|
||||
Date:   Sun <b><font color="#5012cb">May 21 02:15:02 2023</font></b> +0200
|
||||
</p>
|
||||
<p>
|
||||
|
||||
</p>
|
||||
<p>
|
||||
    Library: adjust and fix semantics of nested 'value_type' binding
|
||||
</p>
|
||||
<p>
|
||||
   
|
||||
</p>
|
||||
<p>
|
||||
    This is a subtle and far reaching fix, which hopefully removes
|
||||
</p>
|
||||
<p>
|
||||
    a roadblock regarding a Dispatcher pipeline: Our type rebinding
|
||||
</p>
|
||||
<p>
|
||||
    template used to pick up nested type definitions, especially
|
||||
</p>
|
||||
<p>
|
||||
    'value_type' and 'reference' from iterators and containers,
|
||||
</p>
|
||||
<p>
|
||||
    took an overly simplistic approach, which was then fixed
|
||||
</p>
|
||||
<p>
|
||||
    at various places driven by individual problems.
|
||||
</p>
|
||||
<p>
|
||||
   
|
||||
</p>
|
||||
<p>
|
||||
    Now:
|
||||
</p>
|
||||
<p>
|
||||
     - value_type is conceptually the "thing" exposed by the iterator
|
||||
</p>
|
||||
<p>
|
||||
     - and pointers are treated as simple values, and no longer linked
|
||||
</p>
|
||||
<p>
|
||||
       to their pointee type; rather we handle the twist regarding
|
||||
</p>
|
||||
<p>
|
||||
       STL const_iterator direcly (it defines a non const value_type,
|
||||
</p>
|
||||
<p>
|
||||
       which is sensible from the STL point of view, but breaks our
|
||||
</p>
|
||||
<p>
|
||||
       generic iterator wrapping mechanism)
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<icon BUILTIN="info"/>
|
||||
</node>
|
||||
<node CREATED="1774471707012" ID="ID_222828708" MODIFIED="1774471731713" TEXT="(erst) vor zwei Jahren habe ich das Problem versucht bei der Wurzel zu packen"/>
|
||||
<node CREATED="1774471733381" ID="ID_8755833" MODIFIED="1774472109854" TEXT="aber exakt in diesem Changeset habe ich den remove-ptr von früher praktisch übernommen">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Obwohl diese Stelle, <i>rein logisch betrachtet,</i> direkt im Widerspruch steht zu dem Argument, das ich in der Commit-Message aufgeführt habe: hier behandle ich nämlich einen Pointer nicht als »einfach gegebenen Wert«, sondern versuche ihn bezüglich seines Pointee zu »interpretieren«. Das mag an der Stelle naheliegend scheinen, da man in einen RangeIter praktisch immer nur STL-container einwickelt, und die typischen Testfälle arbeiten mit <i>einfachen Werten im Container</i>. Für diese gibt es aber keine Weiche in den Value-Traits, und deshalb ist diese alte Logik scheinbar redundant, so nach dem Motto „kann ja nicht schaden“
|
||||
</p>
|
||||
<p>
|
||||
|
||||
</p>
|
||||
<p>
|
||||
Mein Bauchgefühl (oder die Erinnerung) sagt mir, daß ich an der Stelle nicht wußte, warum ich da dieses remove-ptr eingebaut hatte, mich aber erinnern konnte, daß es das Ergebnis einer extrem tückischen Situation war. Unter der Annahme, daß STL-container „ein einfacher Fall“ sind, erscheint es dann defensiv richtig, diese alte Qualifizierung mal drinnen zu lassen
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
<node COLOR="#5b280f" CREATED="1774472125473" ID="ID_308643784" MODIFIED="1774472840137" TEXT="ohne den strip-ptr würden wir an der Stelle korrekt abbiegen">
|
||||
<icon BUILTIN="idea"/>
|
||||
<icon BUILTIN="button_cancel"/>
|
||||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#bd1256" CREATED="1774472842137" HGAP="21" ID="ID_44160619" MODIFIED="1774472860956" TEXT="das ist ein Kurzschluß!" VSHIFT="6">
|
||||
<icon BUILTIN="broken-line"/>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1774472152381" ID="ID_1172897058" MODIFIED="1774472867948" TEXT="Experiment: entferne den strip-ptr">
|
||||
<icon BUILTIN="forward"/>
|
||||
<node CREATED="1774472869933" ID="ID_1252763757" MODIFIED="1774472892212" TEXT="bricht massiv"/>
|
||||
<node CREATED="1774472893514" ID="ID_276836465" MODIFIED="1774472921127">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
ist auch logisch: T* ist <b>hier</b> ein »Iterator«
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<node CREATED="1774472936587" ID="ID_894773701" MODIFIED="1774473005367" TEXT="Bedeutet: T wäre der »value«"/>
|
||||
<node CREATED="1774472959721" ID="ID_179295366" MODIFIED="1774473016083">
|
||||
<richcontent TYPE="NODE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
<i>speziell hier</i> <u>müssen</u> wir den Pointer strippen
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
<node CREATED="1774473022015" ID="ID_1145919596" MODIFIED="1774473045137" TEXT="sonst deklarieren wir fälschlich T* als »value«"/>
|
||||
<node CREATED="1774473046510" ID="ID_1757759806" MODIFIED="1774473061329" TEXT="⟹ massive Compile-Probleme, deren Ursache nicht offensichtlich ist"/>
|
||||
</node>
|
||||
<node CREATED="1774473065347" ID="ID_644087481" MODIFIED="1774473505092" TEXT="also war es zwar ein pragmatischer Fix — aber ein Spezial-Eingriff ist hier notwendig">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Die Beobachtung ist korrekt: ich hatte früher an der Stelle diesen Mismatch lokal gefixt, ohne ihn zu verstehen. Und diesen alten Fix habe ich mitgenommen; rein zufällig hat es sich in meinem neuen Framework „fast korrekt“ verhalten...
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
</node>
|
||||
<node CREATED="1774473105365" ID="ID_863796636" MODIFIED="1774473770697" TEXT="Grund: Mismatch zwischen STL-»Iteraor«-Begriff und unserem »Forward-Iterator«-Konzept">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
In der STL ist ein Iterator lediglich ein verkappter Pointer, und die Semantik bleibt operational. Dazu paßt, daß zwar eine convenience-Typedef "iterator" bzw. "const_iterator" gegeben ist, diese aber keinen eigenständigen Typ (Wrapper) bezeichnet, sondern nur ein Alias ist. Demnach ist es im STL-Framework nicht möglich (und auch nicht sinnvoll), für einen Iterator einen »value_type« zu defineren: Der Iterator ist stets konzeptionell an einen Container gebunden, und <b>der Container definiert die Typen</b>.
|
||||
</p>
|
||||
<p>
|
||||
|
||||
</p>
|
||||
<p>
|
||||
Ganz anders in Lumiera: dort ist »Iterator« ein eigenständiges Konzept. Das geht so weit, daß »Iterator« under »Lumiera-Iterable« kongruent gemacht werden können (weil wir keinen Reset und keine Navigation erlauben). Insofern inspieriert von Python. Und weil das so ist, <b>müssen</b>  wir <i>ausgehend vom Iterator nach den Typen suchen</i> (value, reference...). Denn was anderes haben wir gar nicht in der Hand. Es gibt nicht den »eigentlichen Container«.
|
||||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<arrowlink COLOR="#d11222" DESTINATION="ID_588223326" ENDARROW="Default" ENDINCLINATION="112;5;" ID="Arrow_ID_480662640" STARTARROW="None" STARTINCLINATION="-29;56;"/>
|
||||
<icon BUILTIN="idea"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node CREATED="1774473642118" ID="ID_1995855949" MODIFIED="1774475476839" TEXT="Also: es sind nicht die Value-Traits das Problem...">
|
||||
<icon BUILTIN="forward"/>
|
||||
<node CREATED="1774473656226" ID="ID_397410933" MODIFIED="1774473662921" TEXT="...sondern die Verwendung im RangeIter"/>
|
||||
<node CREATED="1774473663683" ID="ID_588223326" MODIFIED="1774473770699" TEXT="denn RangeIter ist ein Adapter zum »Iterator«/Range-System der STL">
|
||||
<linktarget COLOR="#d11222" DESTINATION="ID_588223326" ENDARROW="Default" ENDINCLINATION="112;5;" ID="Arrow_ID_480662640" SOURCE="ID_863796636" STARTARROW="None" STARTINCLINATION="-29;56;"/>
|
||||
</node>
|
||||
<node CREATED="1774473725860" ID="ID_950552681" MODIFIED="1774473740500" TEXT="Konsequenz: muß gezielt den RangeIter flexibel gestalten">
|
||||
<node CREATED="1774473741840" ID="ID_542386747" MODIFIED="1774473755625" TEXT="der Fall »STL-Iterator« muß eigens erkannt werden">
|
||||
<node CREATED="1774473758945" ID="ID_1285126274" MODIFIED="1774473766009" TEXT="Kriterium: IT ist ein Pointer"/>
|
||||
</node>
|
||||
<node CREATED="1774473775445" ID="ID_984431807" MODIFIED="1774473804324" TEXT="wenn dagegen IT ein Lumiera-Iterator ist, wären die Value-Traits anzuwenden"/>
|
||||
</node>
|
||||
<node COLOR="#435e98" CREATED="1774475424051" ID="ID_1227959256" MODIFIED="1774475473698" TEXT="mit aktuellem Toolkit leicht möglich">
|
||||
<icon BUILTIN="idea"/>
|
||||
<node CREATED="1774475452700" ID="ID_1325042492" MODIFIED="1774475458760" TEXT="unser Trait can_IterForEach"/>
|
||||
<node CREATED="1774475461337" ID="ID_489959783" MODIFIED="1774475465447" TEXT="std::conditional_t"/>
|
||||
</node>
|
||||
<node COLOR="#338800" CREATED="1774475482817" ID="ID_1997734195" MODIFIED="1774475490057" TEXT="Build mit dieser Anpassung läuft durch">
|
||||
<icon BUILTIN="button_ok"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1689102663805" ID="ID_1911278306" MODIFIED="1689182022572" TEXT="Ausbau der Teminal-Ausdrücke">
|
||||
<icon BUILTIN="pencil"/>
|
||||
<node COLOR="#338800" CREATED="1689102690854" FOLDED="true" ID="ID_590768203" MODIFIED="1689182022574" TEXT="Terminal: effuse()">
|
||||
|
|
|
|||
Loading…
Reference in a new issue