Library: allow for a stop condition in iterator pipeline

...introduced in preparation for building the Dispatcher pipeline,
which at its core means to iterate over a sequence of frame positions;
thus we need a way to stop rendering at a predetermined point...
This commit is contained in:
Fischlurch 2023-06-01 04:09:29 +02:00
parent fbfbd2a078
commit ad173540d9
4 changed files with 358 additions and 11 deletions

View file

@ -326,7 +326,7 @@ namespace lib {
* -# \c yield realises the given state, yielding an element of result type `T&`
* @tparam T nominal result type (maybe const, but without reference).
* The resulting iterator will yield a reference to this type T
* @tparam ST type of the "state core", defaults to T.
* @tparam ST type of the »state core«, defaults to T.
* The resulting iterator will hold an instance of ST, which thus
* needs to be copyable and default constructible to the extent
* this is required for the iterator as such.
@ -447,6 +447,53 @@ namespace lib {
/**
* Adapter to dress up an existing »Lumiera Forward Iterator« as »state core«.
* This building block achieves the complement of \ref IterStateWrapper by providing
* the API functions expected by the latter's _state protocol;_ a combination of
* IterStateCore and IterStateWrapper layered on top behaves identical to the
* original iterator. This can be used to change some aspects of the behaviour.
* @remark directly layered by inheritance, thus public functions of the
* wrapped iterator remain visible (contrary to IterStateWrapper)
*/
template<class IT>
class IterStateCore
: public IT
{
static_assert (lib::meta::can_IterForEach<IT>::value
,"Lumiera Iterator required as source");
protected:
IT&
srcIter() const
{
return unConst(*this);
}
public:
using IT::IT;
/* === state protocol API for IterStateWrapper === */
bool
checkPoint() const
{
return bool(srcIter());
}
typename IT::reference
yield() const
{
return *srcIter();
}
void
iterNext()
{
++ srcIter();
}
};

View file

@ -1213,6 +1213,51 @@ namespace lib {
); // wrap the extension predicate in a similar way
_Filter::pullFilter(); // then pull to re-establish the Invariant
}
}; /////////////////////////////////////TICKET #1305 shouldn't we use std::move(_Filter::predicate_) ???
/**
* @internal Decorator for TreeExplorer to cut iteration once a predicate ceases to be true.
* Similar to Filter, the given functor is adapted as appropriate, yet is required to yield
* a bool convertible result. The functor will be evaluated whenever the »exhausted« state
* of the resulting iterator is checked, on each access and before iteration; this evaluation
* is not cached (and thus could also detect ongoing state changes by side-effect).
* @note usually an _exhausted iterator will be abandoned_ however, since the test is
* not cached, the iterator might become active again, if for some reason the
* condition becomes true again (e.g. as result of `expandChildern()`)
*/
template<class SRC>
class StopTrigger
: public IterStateCore<SRC>
{
static_assert(can_IterForEach<SRC>::value, "Lumiera Iterator required as source");
using Core = IterStateCore<SRC>;
using Cond = function<bool(SRC&)>;
Cond whileCondition_;
public:
StopTrigger() =default;
// inherited default copy operations
template<typename FUN>
StopTrigger (SRC&& dataSrc, FUN&& condition)
: Core{move (dataSrc)}
, whileCondition_{_FunTraits<FUN,SRC>::adaptFunctor (forward<FUN> (condition))}
{ }
/** adapt the iteration control API for IterableDecorator:
* check the stop condition first and block eventually */
bool
checkPoint() const
{
return Core::checkPoint()
and whileCondition_(Core::srcIter());
}
};
@ -1511,6 +1556,44 @@ namespace lib {
}
/** adapt this TreeExplorer to iterate only as long as a condition holds true.
* @return processing pipeline with attached [stop condition](\ref iter_explorer::StopTrigger)
*/
template<class FUN>
auto
iterWhile (FUN&& whileCond)
{
iter_explorer::static_assert_isPredicate<FUN,SRC>();
using ResCore = iter_explorer::StopTrigger<SRC>;
using ResIter = typename _DecoratorTraits<ResCore>::SrcIter;
return TreeExplorer<ResIter> (ResCore {move(*this), forward<FUN>(whileCond)});
}
/** adapt this TreeExplorer to iterate until a condition becomes first true.
* @return processing pipeline with attached [stop condition](\ref iter_explorer::StopTrigger)
*/
template<class FUN>
auto
iterUntil (FUN&& untilCond)
{
iter_explorer::static_assert_isPredicate<FUN,SRC>();
using ResCore = iter_explorer::StopTrigger<SRC>;
using ResIter = typename _DecoratorTraits<ResCore>::SrcIter;
using ArgType = typename iter_explorer::_FunTraits<FUN,SRC>::Arg;
return TreeExplorer<ResIter> (ResCore { move(*this)
,[whileCond = forward<FUN>(untilCond)](ArgType val)
{
return not whileCond(val);
}
});
}
/** adapt this TreeExplorer to filter results, by invoking the given functor to approve them.
* The previously created source layers will be "pulled" to fast-forward immediately to the
* next element confirmed this way by the bound functor. If none of the source elements

View file

@ -281,6 +281,7 @@ namespace test{
verify_combinedExpandTransform();
verify_customProcessingLayer();
verify_scheduledExpansion();
verify_untilStopTrigger();
verify_FilterIterator();
verify_FilterChanges();
verify_asIterSource();
@ -829,6 +830,42 @@ namespace test{
/** @test control end of iteration by a stop condition predicate.
* When decorating the pipeline with this adapter, iteration end depends not only on
* the source iterator, but also on the end condition; once the condition flips, the
* overall pipeline iterator is exhausted and can never be re-activated again (unless
* some special trickery is done by conspiring with the data source)
*/
void
verify_untilStopTrigger()
{
CHECK (materialise (
treeExplore(CountDown{10})
.iterUntil([](uint j){ return j < 5; })
)
== "10-9-8-7-6-5"_expect);
CHECK (materialise (
treeExplore(CountDown{10})
.iterWhile([](uint j){ return j > 5; })
)
== "10-9-8-7-6"_expect);
CHECK (materialise (
treeExplore(CountDown{10})
.iterWhile([](int j){ return j > -5; })
)
== "10-9-8-7-6-5-4-3-2-1"_expect);
CHECK (materialise (
treeExplore(CountDown{10})
.iterWhile([](uint j){ return j > 25; })
)
== ""_expect);
}
/** @test add a filtering predicate into the pipeline.
* As in all the previously demonstrated cases, also the _filtering_ is added as decorator,
* wrapping the source and all previously attached decoration layers. And in a similar way,
@ -843,7 +880,7 @@ namespace test{
treeExplore(CountDown{10})
.filter([](uint j){ return j % 2; })
)
== "9-7-5-3-1");
== "9-7-5-3-1"_expect);
// Filter may lead to consuming util exhaustion...
@ -875,7 +912,7 @@ namespace test{
.transform([](float f){ return 0.55 + 2*f; })
.filter([](CountDown& core){ return core.p % 2; })
)
== "18.55-14.55-10.55");
== "18.55-14.55-10.55"_expect);
@ -887,7 +924,7 @@ namespace test{
.filter([](uint i){ return i%2 == 0; })
.expandAll() // Note: sends the expandChildren down through the filter
)
== "10-8-6-4-2-2-6-4-2-2");
== "10-8-6-4-2-2-6-4-2-2"_expect);
@ -911,7 +948,7 @@ namespace test{
});
CHECK (materialise(kk)
== "14-12-10-8-6-4-2-14-12");
== "14-12-10-8-6-4-2-14-12"_expect);
// Explanation:
// The source starts at 10, but since the toggle is false,
// none of the initial values makes it though to the result.
@ -923,7 +960,7 @@ namespace test{
// the rest of the original sequence, 7,6 (which stops above 5).
CHECK (materialise(kk.filter([](long i){ return i % 7; }))
== "12-10-8-6-4-2-12");
== "12-10-8-6-4-2-12"_expect);
// Explanation:
// Since the original TreeExplorer was assigned to variable kk,
// the materialise()-Function got a lvalue-ref and thus made a copy

View file

@ -56926,6 +56926,135 @@
<node CREATED="1536015513076" ID="ID_849722427" MODIFIED="1557498707236" TEXT="den Regex-Include opaque halten"/>
<node CREATED="1536015525154" ID="ID_1039059758" MODIFIED="1557498707236" TEXT="soll mal in util.hpp"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1685583627381" ID="ID_1193075176" MODIFIED="1685585363445" TEXT="iterierbare Integer-Sequenz">
<linktarget COLOR="#bc6489" DESTINATION="ID_1193075176" ENDARROW="Default" ENDINCLINATION="-872;80;" ID="Arrow_ID_49892110" SOURCE="ID_1477861363" STARTARROW="None" STARTINCLINATION="-1069;-69;"/>
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1685583658136" ID="ID_208810625" MODIFIED="1685583674253" TEXT="Basis-Baustein f&#xfc;r diverse Iterations-Schemata">
<icon BUILTIN="yes"/>
</node>
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1685584188194" ID="ID_1817394648" MODIFIED="1685584223246" TEXT="NumIter gibt&apos;s schon in iter-adapter.hpp &#x2014; ausreichend?">
<icon BUILTIN="idea"/>
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1685584668136" ID="ID_1575464239" MODIFIED="1685584688433" TEXT="nebenbei: warum in iter-adapter #include &lt;iterator&gt; ?">
<font NAME="SansSerif" SIZE="12"/>
<icon BUILTIN="help"/>
</node>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1685584919441" ID="ID_340863958" MODIFIED="1685584940405" TEXT="NumIter wird bisher noch &#xfc;berhaupt nicht verwendet!">
<icon BUILTIN="idea"/>
<node CREATED="1685584987258" ID="ID_92718284" MODIFIED="1685584996537" TEXT="doch: in FormatHelper_test">
<icon BUILTIN="stop-sign"/>
</node>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1685585015569" ID="ID_808211730" MODIFIED="1685585032068" TEXT="hilfsfunktion eachNum()">
<icon BUILTIN="messagebox_warning"/>
<node CREATED="1685585034287" ID="ID_1701190023" MODIFIED="1685585152408" TEXT="ein gef&#xe4;hrlich allgemeiner Name">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
das hei&#223;t, diese Funktion darf es nur einmal geben, und das mu&#223; dann &#187;die&#171; einschl&#228;gige Implementierung sein, ohne Wenn und Aber
</p>
</body>
</html></richcontent>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1685584692820" ID="ID_1638518253" MODIFIED="1685584731304" TEXT="ggfs eine Variante ohne Stop-Bedingung schaffen?">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1685584741200" ID="ID_1542512900" MODIFIED="1685584756229" TEXT="ggfs einen IteratorCore-Adapter">
<icon BUILTIN="flag-yellow"/>
<node CREATED="1685584762627" ID="ID_336248643" MODIFIED="1685584825968" TEXT="das Gegenst&#xfc;ck zu IterStateWrapper">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
also...
</p>
<ul>
<li>
nimmt einen Lumiera Forward-Iterator
</li>
<li>
verpackt ihn als &#187;state-core&#171;
</li>
</ul>
</body>
</html></richcontent>
</node>
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1685584828250" ID="ID_500985590" MODIFIED="1685584841833" TEXT="sinnvoll hier? oder doch in TreeExplorer?">
<icon BUILTIN="help"/>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1685583679366" ID="ID_132953496" MODIFIED="1685583701466">
<richcontent TYPE="NODE"><html>
<head>
</head>
<body>
<p>
<b>optional</b>&#160;die M&#246;glichkeit f&#252;r einen getriggerten Stop vorsehen
</p>
</body>
</html></richcontent>
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1685583707964" ID="ID_1762574264" MODIFIED="1685583830423" TEXT="&#x201e;optional&#x201c; meint compile-time">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
denn der Standard-Fall ist die <i>unendliche Sequenz</i>&#160;&#8212; und diese darf keinerlei unn&#246;tigen Ballast haben; weder einen Funktor (storage!), noch einen letztlich unn&#246;tigen Pr&#228;dikat-Aufruf (denn sie l&#228;uft &#8222;f&#252;r immer&#8220;)
</p>
</body>
</html></richcontent>
<icon BUILTIN="yes"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1685583844103" ID="ID_1089487517" MODIFIED="1685583862869" TEXT="until-Dekorator">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#f0d5c5" COLOR="#990033" CREATED="1685584282950" ID="ID_537873518" MODIFIED="1685584289539" TEXT="wo ansiedeln?">
<icon BUILTIN="help"/>
<node CREATED="1685584292265" ID="ID_1449788515" MODIFIED="1685584302429" TEXT="iter-adapter w&#xe4;re sch&#xf6;n..."/>
<node CREATED="1685584303269" ID="ID_535538930" MODIFIED="1685584316100" TEXT="aber der hat kein #include &lt;functional&gt;">
<icon BUILTIN="stop-sign"/>
</node>
<node CREATED="1685584323374" ID="ID_553274601" MODIFIED="1685584469308" TEXT="itertools w&#xe4;re OK (mit Einschr&#xe4;nkung)">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<ul>
<li>
Hilfsfunktionen fehlen (z.B. Adaptieren des Funktors an StateCore)
</li>
<li>
TreeExplorer sollte itertools nicht includieren (gegenw&#228;rtig tut er's aber nur wegen IterSource, und das k&#246;nnte man refactorn)
</li>
</ul>
</body>
</html></richcontent>
</node>
<node CREATED="1685584350243" ID="ID_72915504" MODIFIED="1685584473955" TEXT="sonst: direkt in TreeExplorer">
<icon BUILTIN="yes"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1685584477881" ID="ID_1127172208" MODIFIED="1685584544101" TEXT="sollte eine Trigger-Funktion auswerten und daraufhin destruktiv stoppen">
<icon BUILTIN="info"/>
</node>
<node CREATED="1685584554975" ID="ID_1326522890" MODIFIED="1685584586183" TEXT="w&#xe4;re sehr einfach als StateCore zu implementieren">
<node CREATED="1685584586562" ID="ID_1932788934" MODIFIED="1685584592110" TEXT="ganz analog wie Filter"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1685584593570" ID="ID_1531711213" MODIFIED="1685584614109" TEXT="k&#xf6;nnte von einer &#xbb;IteratorCore&#xab; profitieren">
<icon BUILTIN="idea"/>
</node>
</node>
</node>
</node>
</node>
</node>
</node>
<node CREATED="1573230287134" ID="ID_1958309835" MODIFIED="1573230291318" TEXT="Concepts">
@ -73242,6 +73371,10 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1685585183914" ID="ID_1477861363" MODIFIED="1685585363445" TEXT="einfache Integer-Sequenz iterieren (meist &#x221e;)">
<arrowlink COLOR="#bc6489" DESTINATION="ID_1193075176" ENDARROW="Default" ENDINCLINATION="-872;80;" ID="Arrow_ID_49892110" STARTARROW="None" STARTINCLINATION="-1069;-69;"/>
<icon BUILTIN="flag-yellow"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1685063128431" ID="ID_1958871740" MODIFIED="1685063140918" TEXT="Startpunkt gewinnen">
<icon BUILTIN="flag-yellow"/>
@ -73259,11 +73392,15 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<node CREATED="1685236006596" ID="ID_844227051" MODIFIED="1685236120369" TEXT="sonst &#x27f9; aktuelle Zeit + Puffer f&#xfc;r Latency"/>
</node>
</node>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1685063142277" ID="ID_1900307953" MODIFIED="1685406733269" TEXT="Problem: mu&#xdf; die Frame-Nummer an diese Stelle bekommen">
<node COLOR="#435e98" CREATED="1685063142277" ID="ID_1900307953" MODIFIED="1685497387571" TEXT="Problem: mu&#xdf; die Frame-Nummer an diese Stelle bekommen">
<arrowlink COLOR="#fb1618" DESTINATION="ID_548686360" ENDARROW="Default" ENDINCLINATION="-59;-351;" ID="Arrow_ID_567963048" STARTARROW="None" STARTINCLINATION="-369;18;"/>
<icon BUILTIN="messagebox_warning"/>
<node CREATED="1685497366726" ID="ID_1549936323" MODIFIED="1685497373377" TEXT="nein ist kein Problem (mehr)"/>
<node CREATED="1685497375539" ID="ID_1837741010" MODIFIED="1685497384679" TEXT="wir z&#xe4;hlen einfach weiter"/>
</node>
<node COLOR="#5b280f" CREATED="1685231918246" ID="ID_1408660753" MODIFIED="1685497362335" TEXT="stellt zugleich den Link zur Wall-Clock-Time her">
<icon BUILTIN="button_cancel"/>
</node>
<node CREATED="1685231918246" ID="ID_1408660753" MODIFIED="1685231930041" TEXT="stellt zugleich den Link zur Wall-Clock-Time her"/>
<node CREATED="1685406699651" ID="ID_789896275" MODIFIED="1685406708225" TEXT="das machen aber die Timings auch bereits"/>
<node BACKGROUND_COLOR="#ccb59b" COLOR="#6e2a38" CREATED="1685406743623" ID="ID_1294162294" MODIFIED="1685406767041" TEXT="TimeAnchor ist ohne sinnvollen Gehalt">
<font ITALIC="true" NAME="SansSerif" SIZE="14"/>
@ -73328,6 +73465,35 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1685583450800" ID="ID_9210672" MODIFIED="1685583462389" TEXT="frame-step Core">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1685583477662" ID="ID_1520687206" MODIFIED="1685583545340" TEXT="den einfachst m&#xf6;glichen Frame-Step als &#xbb;state core&#xab; verpacken">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
...denn eine &#187;state core&#171; wird automatisch von TreeExplorer erkannt und adaptiert...
</p>
</body>
</html></richcontent>
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1685583497277" ID="ID_1911386159" MODIFIED="1685583620738" TEXT="sinnvolles Erg&#xe4;nzungs-API bereitstellen">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
...denn TreeExplorer hat die Eigenschaft, von der &#187;state-core&#171; zu erben, und damit ihr public-API nach au&#223;en durchzureichen &#8212; im Besonderen auch f&#252;r obere Layer in der Pipeline
</p>
</body>
</html></richcontent>
<icon BUILTIN="flag-yellow"/>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1685066463050" ID="ID_1613334312" MODIFIED="1685066472930" TEXT="Mock-Setup schaffen">
<icon BUILTIN="flag-yellow"/>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1685066492396" ID="ID_1906962794" MODIFIED="1685066501049" TEXT="API f&#xfc;r Zugriff kl&#xe4;ren">
@ -73476,7 +73642,7 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</html></richcontent>
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1685062686774" ID="ID_548686360" MODIFIED="1685063188122">
<node COLOR="#5b280f" CREATED="1685062686774" ID="ID_548686360" MODIFIED="1685497417257">
<richcontent TYPE="NODE"><html>
<head>
@ -73491,12 +73657,13 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
</body>
</html></richcontent>
<linktarget COLOR="#fb1618" DESTINATION="ID_548686360" ENDARROW="Default" ENDINCLINATION="-59;-351;" ID="Arrow_ID_567963048" SOURCE="ID_1900307953" STARTARROW="None" STARTINCLINATION="-369;18;"/>
<icon BUILTIN="flag-yellow"/>
<icon BUILTIN="button_cancel"/>
<node CREATED="1685062714666" ID="ID_1331730136" MODIFIED="1685062746213" TEXT="bisher habe ich mit der InvocationInstaceID &quot;getrickst&quot;">
<icon BUILTIN="info"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1685062757986" ID="ID_1983441100" MODIFIED="1685062769556" TEXT="TimeAnchor braucht die Frame-Nr">
<node COLOR="#5b280f" CREATED="1685062757986" ID="ID_1983441100" MODIFIED="1685497399474" TEXT="TimeAnchor braucht die Frame-Nr">
<icon BUILTIN="messagebox_warning"/>
<icon BUILTIN="button_cancel"/>
<node CREATED="1685231530086" ID="ID_1373995955" MODIFIED="1685231549597" TEXT="sollte man nicht ohnehin neu re-Quantisieren?">
<icon BUILTIN="idea"/>
</node>
@ -73515,6 +73682,19 @@ Date:&#160;&#160;&#160;Thu Apr 20 18:53:17 2023 +0200<br/>
<icon BUILTIN="info"/>
</node>
</node>
<node CREATED="1685497428581" ID="ID_708086030" MODIFIED="1685497517768" TEXT="mit dem Verzicht auf TimeAnchor l&#xf6;st sich auch dieses Problem">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<body>
<p>
die aktuelle Frame-Nummer ist nun nur noch ein lokales Detail in der Planning-Pipeline; f&#252;r den re-Trigger-Job gen&#252;gt eine Zeitangabe, aus der dann eine realTime-Deadline abgeleitet wird
</p>
</body>
</html></richcontent>
<icon BUILTIN="stop-sign"/>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1684967502141" ID="ID_470129345" LINK="https://issues.lumiera.org/ticket/1301" MODIFIED="1684975281405" TEXT="#1301 Render-Drive extrahieren">