From a8231150a52347c7f247c1d3347aea1c923f089f Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Wed, 1 Jan 2025 22:02:08 +0100 Subject: [PATCH] Library: need remapping of variadic sequence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a rather obnoxious limitation of C++ variadics: the inability to properly match against a mixed sequence with variadics. The argument pack must always be the last element, which precludes to match the last or even the penultimate element (which we need here). After some tinkering, I found a way to recast this as ''rebinding to a remoulded sequence'', and could package a multitude of related tools into a single helper-template, which works without any further library dependencies. 🠲 extract into a separate header (`variadic-rebind.hpp`) for ease of use. --- src/lib/meta/variadic-helper.hpp | 4 +- src/lib/meta/variadic-rebind.hpp | 488 +++++++++++++++++++++ src/lib/parse.hpp | 27 +- src/steam/engine/param-weaving-pattern.hpp | 2 +- tests/vault/gear/activity-detector.hpp | 2 +- wiki/thinkPad.ichthyo.mm | 134 ++++++ 6 files changed, 651 insertions(+), 6 deletions(-) create mode 100644 src/lib/meta/variadic-rebind.hpp diff --git a/src/lib/meta/variadic-helper.hpp b/src/lib/meta/variadic-helper.hpp index 681138bce..88dcece3d 100644 --- a/src/lib/meta/variadic-helper.hpp +++ b/src/lib/meta/variadic-helper.hpp @@ -46,6 +46,7 @@ #include "lib/meta/typelist.hpp" #include "lib/meta/typelist-util.hpp" #include "lib/meta/typeseq-util.hpp" +#include "lib/meta/variadic-rebind.hpp" #include "lib/meta/util.hpp" namespace lib { @@ -166,7 +167,7 @@ namespace meta { - +#if false ////////////////////////////////////////////////////////////////////////////////TODO reorder /* ==== Rebinding Variadic Arguments ==== **/ /** @@ -192,6 +193,7 @@ namespace meta { +#endif ////////////////////////////////////////////////////////////////////////////////TODO reorder diff --git a/src/lib/meta/variadic-rebind.hpp b/src/lib/meta/variadic-rebind.hpp new file mode 100644 index 000000000..b6eefc0e1 --- /dev/null +++ b/src/lib/meta/variadic-rebind.hpp @@ -0,0 +1,488 @@ +/* + VARIADIC-REBIND.hpp - metaprogramming utilities for parameter- and type sequences + + Copyright (C) + 2023, 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 variadic-rebind.hpp + ** Metaprogramming support to rebuild and rebind variadic templates. + ** The type rebinding- and helper templates in this header allow to perform + ** simple sequence manipulations on sequences of template parameters extracted + ** from variadic parameter packs. The goal is to (pre)process flexible argument + ** lists _at compile time,_ driven by template instantiation, allowing to specialise + ** and react specifically on some concrete pattern of argument types. + ** + ** @warning the metaprogramming part of Lumiera to deal with type sequences is in a + ** state of transition, since C++11 now offers direct language support for + ** processing of flexible template parameter sequences ("parameter packs"). + ** It is planned to regroup and simplify our homemade type sequence framework + ** to rely on variadic templates and integrate better with std::tuple. + ** It is clear that we will _retain some parts_ of our own framework, + ** since programming with _Loki-style typelists_ is way more obvious + ** and straight forward than handling of template parameter packs, + ** since the latter can only be rebound through pattern matching. + ** @todo transition lib::meta::Types to variadic parameters /////////////////////////////////TICKET #987 + ** + ** @see control::CommandDef usage example + ** @see TupleHelper_test + ** @see typelist.hpp + ** @see function.hpp + ** @see generator.hpp + ** + */ + + +#ifndef LIB_META_VARIADIC_REBIND_H +#define LIB_META_VARIADIC_REBIND_H + +//#include "lib/meta/typelist.hpp" +//#include "lib/meta/typelist-util.hpp" +//#include "lib/meta/typeseq-util.hpp" +//#include "lib/meta/util.hpp" + +namespace lib { +namespace meta { + +#if false ////////////////////////////////////////////////////////////////////////////////TODO reorder + + + /* ==== Build Variadic Sequences ==== **/ + + + /** Hold a sequence of index numbers as template parameters */ + template + struct IndexSeq + { + template + using AppendElm = IndexSeq; + + template + using PrependElm = IndexSeq; + }; + + /** + * build regular sequences of index number + * e.g. `IndexSeq<0, 1, 2, ..., n-1>` + */ + template + struct BuildIndexSeq + { + using Ascending = typename BuildIndexSeq::Ascending::template AppendElm; + using Descending = typename BuildIndexSeq::Descending::template PrependElm; + + template + using OffsetBy = typename BuildIndexSeq::template OffsetBy::template AppendElm; + + template + using FilledWith = typename BuildIndexSeq::template FilledWith::template AppendElm; + + template + using First = typename BuildIndexSeq::Ascending; + + template + using After = typename BuildIndexSeq< (n>c)? n-c : 0>::template OffsetBy; + }; + + template<> + struct BuildIndexSeq<0> + { + using Empty = IndexSeq<>; + + using Ascending = Empty; + using Descending = Empty; + + template + using OffsetBy = Empty; + + template + using FilledWith = Empty; + + template + using First = Empty; + + template + using After = Empty; + }; + + + + /** + * build a sequence of index numbers based on a type sequence + */ + template + struct BuildIdxIter + { + enum {SIZ = sizeof...(TYPES) }; + using Builder = BuildIndexSeq; + + using Ascending = typename Builder::Ascending; + using Descending = typename Builder::Descending; + + template + using OffsetBy = typename Builder::template OffsetBy; + + template + using FilledWith = typename Builder::template FilledWith; + + template + using First = typename Builder::template First; + + template + using After = typename Builder::template After; + }; + + /** build an index number sequence from a type sequence */ + template + struct BuildIdxIter> + { + ///////////////////////TICKET #987 : since Types is not variadic, need to strip NullType here (instead of just using sizeof...(TYPES) + enum {SIZ = lib::meta::count::List>::value }; + using Builder = BuildIndexSeq; + + using Ascending = typename Builder::Ascending; + using Descending = typename Builder::Descending; + + template + using OffsetBy = typename Builder::template OffsetBy; + + template + using FilledWith = typename Builder::template FilledWith; + + template + using First = typename Builder::template First; + + template + using After = typename Builder::template After; + }; + +#endif ////////////////////////////////////////////////////////////////////////////////TODO reorder + + + + + /* ==== Rebinding Variadic Arguments ==== **/ + + /** + * Metaprogramming helper to transfer variadic arguments. + * - builds a new type instantiation from the Template \a X + * - possibly picks up the variadic argument pack from a given + * source template `U` + * @tparam X a variadic template + */ + template class X, typename...ARGS> + struct RebindVariadic + { + using Type = X; + }; + + template class X + ,template class U + ,typename...ARGS> + struct RebindVariadic> + { + using Type = X; + }; + + + + + + /* ==== Rebuild with remoulded variadic sequence ==== **/ + + template class L, typename...XS> + struct _Vari; + + template class L, typename X, typename...XS> + struct _Vari> + { + using Prepend = L; + using Append = L; + }; + + template class L, typename X1, typename X2> + struct _Vari + { + using Penult = X1; + using Ultima = X2; + using Prefix = L; + using Remain = L; + using Revers = L; + }; + + template class L, typename X> + struct _Vari + { + using Ultima = X; + using Penult = NullType; + using Remain = L; + using Revers = L; + using Prefix = L<>; + }; + + template class L, typename X, typename...XS> + struct _Vari + { + using Penult = typename _Vari::Penult; + using Ultima = typename _Vari::Ultima; + + using _Tail_Pre_ = typename _Vari::Prefix; + using _Tail_Rev_ = typename _Vari::Revers; + + using Remain = L; + using Prefix = typename _Vari::Prepend; + using Revers = typename _Vari::Prepend; + }; + +#if false ////////////////////////////////////////////////////////////////////////////////TODO reorder + + /* ==== Build and Rebuild variadic type sequences ==== **/ + + /** + * Variadic type sequence builder. + * This metaprogramming helper template provides an unified view + * to handle _»tuple-like« types and variadic _type sequences._ + * - the constant #SIZ gives the number of elements + * - the nested type #Idx can be used as _index sequence_ + * - #Seq is a _variadic type sequence_ with the extracted types + * - #Tup is a std::tuple over these types + * - the nested template #Apply wraps each type into another template + * - #Rebind likewise instantiates another template with the element types + * - #AndAll applies a predicate and combines the result with _logical and_ + * - #OrAll similarly evaluates _logical or_ on the application results + */ + template + struct ElmTypes + { + static constexpr size_t SIZ = 1; + using Idx = std::index_sequence; + using Seq = TySeq; + using Tup = std::tuple; + + template class META> + using Apply = TySeq>; + template class O> + using Rebind = O; + template class PRED> + using AndAll = std::__and_>; + template class PRED> + using OrAll = std::__or_>; + }; + + /** Partial specialisation to handle type sequences */ + template + struct ElmTypes> + { + static constexpr size_t SIZ = sizeof...(TYPES); + using Idx = std::make_index_sequence; + using Seq = TySeq; + using Tup = std::tuple; + + template class META> + using Apply = TySeq...>; + + template class O> + using Rebind = typename lib::meta::RebindVariadic::Type; + + template class PRED> + using AndAll = typename ElmTypes>::template Rebind; + + template class PRED> + using OrAll = typename ElmTypes>::template Rebind; + }; + + /** partial specialisation to handle types + * supporting the C++ »tuple protocol« + */ + template + struct ElmTypes> + { + template + struct Extract; + template + struct Extract> + { + using ElmTypes = TySeq::type ...>; + }; + + static constexpr size_t SIZ = std::tuple_size::value; + + using Idx = std::make_index_sequence; + using Seq = typename Extract::ElmTypes; + using Tup = typename RebindVariadic::Type; + + template class META> + using Apply = typename ElmTypes::template Apply; + + template class O> + using Rebind = typename RebindVariadic::Type; + + template class PRED> + using AndAll = typename ElmTypes>::template Rebind; + + template class PRED> + using OrAll = typename ElmTypes>::template Rebind; + }; + + + + + + + /* ==== Invoke with index from variadic ==== **/ + + /** helper to invoke a functor, passing instances of std::integral_constant + * @tparam N size of the index-sequence to use for instantiation + */ + template + class WithIdxSeq + { + template + static void + invoke (FUN&& fun, std::index_sequence) + { + (fun (std::integral_constant{}), ...); + } + + public: + template + static void + invoke (FUN&& fun) + { + invoke (std::forward(fun), std::make_index_sequence{}); + } + }; + + /** + * Invoke a function (or λ) with index numbers derived from some variadic count. + * Notably this construct can be used for compile-time iteration over a structure. + * Instances of `std::integral_constant` are passed in sequence to the functor. + * The _size_ of the index sequence is derived from the following sources + * - if the type \a TTX is _tuple-like,_ then std::tuple_size is used + * - otherwise, if the type is a loki-style type sequence or type list, + * the number of type nodes is used + * - otherwise, as fall-back the number of template parameters is used + */ + template + inline void + forEachIDX (FUN&& fun) + { + auto N = []{ + if constexpr (is_Structured()) + return size_t(std::tuple_size::value); + else + if constexpr (lib::meta::is_Typelist::value) + return lib::meta::count::value; + else + { // Fallback: rebind template arguments into a type sequence + using Seq = typename RebindVariadic::Type; + return size_t(count::value); + } + }; + + WithIdxSeq::invoke (std::forward (fun)); + } + + + + + + + + + /* ==== Manipulation of variadic arguments ==== **/ + + namespace { // Implementation delegate template... + /** + * @internal pick a single argument from a variadic parameter pack + * @tparam i the index number (zero based) of the argument to select + * @warning i must be smaller than the number of arguments available + */ + template + struct SelectVararg + { + template + static auto + get (ARG, ARGS&& ...args) + { + return SelectVararg::get (std::forward (args)...); + } + }; + + template<> + struct SelectVararg<0> + { + template + static auto + get (ARG&& a, ARGS...) + { + return std::forward(a); + } + }; + + /** + * @internal helper to decide if SelectVararg shall be applied. + * When the boolean condition does not hold, then, instead of selecting + * from the argument list, an element of type DEFAULT is created as fallback. + */ + template + struct SelectOrInit + : SelectVararg + { }; + + template + struct SelectOrInit + { + template + static DEFAULT + get (ARGS&&...) + { + return DEFAULT{}; + } + }; + }//(End)Implementation + + + /** + * Helper to single out one argument from a variadic argument pack. + * @tparam idx the index number (zero based) of the argument to select + * @remark typically this function is used "driven" by an likewise variadic index sequence, + * where the index sequence itself is picked up by a pattern match; this usage pattern + * allows arbitrarily to handle some of the arguments of a variable argument list, + * as determined by the index sequence passed in. + */ + template + constexpr inline auto + pickArg (ARGS&&... args) + { + static_assert (idx < sizeof...(args), "insufficient number of arguments"); + + return SelectVararg::get (std::forward (args)...); + } + + /** + * Helper to pick one initialisation argument from a variadic argument pack, + * falling back to a default constructed element of type `DEFAULT` in case of + * insufficient number of variadic arguments. + * @tparam idx the index number (zero based) of the argument to select + * @tparam DEFALUT type of the default element to construct as fallback + */ + template + constexpr inline auto + pickInit (ARGS&&... args) + { + return SelectOrInit<(idx < sizeof...(args)), DEFAULT, idx>::get (std::forward (args)...); + } + + + +#endif ////////////////////////////////////////////////////////////////////////////////TODO reorder +}} // namespace lib::meta +#endif /*LIB_META_VARIADIC_REBIND_H*/ diff --git a/src/lib/parse.hpp b/src/lib/parse.hpp index bc2a11b3e..cdd0ba508 100644 --- a/src/lib/parse.hpp +++ b/src/lib/parse.hpp @@ -28,6 +28,7 @@ #include "lib/branch-case.hpp" +#include "lib/meta/variadic-rebind.hpp" #include "lib/meta/function.hpp" #include "lib/meta/trait.hpp" #include "lib/regex.hpp" @@ -47,6 +48,7 @@ namespace util { using lib::meta::_Fun; using lib::meta::has_Sig; using lib::meta::NullType; + using lib::meta::_Vari; using std::decay_t; using std::tuple; using std::array; @@ -176,6 +178,7 @@ namespace util { Tup&& extractTuple() { return move(*this); } }; + /** * Sum Model : results from a disjunction of parsing clauses, * which are are tested and accepted as alternatives, one at least. @@ -184,13 +187,19 @@ namespace util { struct AltModel : lib::BranchCase { - using _Model = lib::BranchCase; + using Alt = lib::BranchCase; template> AltModel (INIT&& init) - : _Model{_Model::TOP, forward (init)} + : Alt{Alt::TOP, forward (init)} { } - + + + + using SubSeq = typename _Vari::Prefix; + using Penult = typename _Vari::Penult; + using Ultima = typename _Vari::Ultima; + template using Additionally = AltModel; @@ -201,6 +210,18 @@ namespace util { Additionally& upFaked = reinterpret_cast&> (*this); return {move (upFaked)}; } + + AltModel (SubSeq&& leftCases) + : AltModel{leftCases.template addBranch()} + { } + + AltModel (Penult&& leftCase) + : Alt{Alt::TOP-1, move(leftCase)} + { } + + AltModel (Ultima&& rightCase) + : Alt{Alt::TOP, move(rightCase)} + { } }; diff --git a/src/steam/engine/param-weaving-pattern.hpp b/src/steam/engine/param-weaving-pattern.hpp index c0d3a490e..b43eb933a 100644 --- a/src/steam/engine/param-weaving-pattern.hpp +++ b/src/steam/engine/param-weaving-pattern.hpp @@ -106,7 +106,7 @@ #include "steam/engine/turnout.hpp" #include "steam/engine/turnout-system.hpp" #include "lib/uninitialised-storage.hpp" -#include "lib/meta/variadic-helper.hpp" +#include "lib/meta/variadic-rebind.hpp" #include "lib/meta/tuple-helper.hpp" #include "lib/meta/function.hpp" diff --git a/tests/vault/gear/activity-detector.hpp b/tests/vault/gear/activity-detector.hpp index 1e93875ee..76fffe9e6 100644 --- a/tests/vault/gear/activity-detector.hpp +++ b/tests/vault/gear/activity-detector.hpp @@ -70,7 +70,7 @@ #include "vault/gear/activity.hpp" #include "vault/gear/nop-job-functor.hpp" #include "lib/time/timevalue.hpp" -#include "lib/meta/variadic-helper.hpp" +#include "lib/meta/variadic-rebind.hpp" #include "lib/meta/function.hpp" #include "lib/wrapper.hpp" #include "lib/format-util.hpp" diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 46e234a8d..79ab70f3b 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -56478,6 +56478,140 @@ + + + + + + + +

+ ...hier ist die Logik umgedreht +

+
    +
  • + für den Produkt-Fall müssen alle Zweige erfüllt sein; erst danach wird das Model gebaut bzw. um die neuen Zweige ergänzt. Sonst fallen wir leer raus +
  • +
  • + hier kann jeder der Zweige greifen, nur wenn kein einziger Zweig greift, fallen wir leer raus. Das heißt, hier müssen wir bereits in den einzelnen Zweigen je nach Fall ein unterschiedliches Modell aufbauen +
  • +
+ + +
+
+ + + + + + + + +

+ linker Zweig: ein sub-Model, in dem irgend ein Zweig gematched hat +

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

+ Der Argument-Pack muß stets am Ende stehen +

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

+ Grundidee: man baut die neue, umgebaute Typ-Sequenz in den variadischen Argumenten eines beliebigen Templates, das selbst als Template-Template-Parameter gegeben wird. Damit kann man unmittelbar in einem einzigen Zug das redefinierte Ziel-Template konstruieren, ohne erst in eine andere Verarbeitungs-Domäne (tuple, Typsequenz, Typliste) mappen zu müssen. Zudem kann das gleiche Verarbeitungs-Template auch Spezial-Belegungen für Hilfs-Operationen mit anbieten, und man kann gleich die häufigsten verwandten Tools in einer einzigen Definition zur Verfügung stellen. +

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

+ da zeichnet sich ein Schema ab, das die bekannten Sequenz-Umordnungen sehr direkt ausführt, ohne erst in eine andere Repräsentation (wie Typelist) zu mappen. Trotzdem ist der Aufwand O(n), für das Umkehren der Sequenz sogar O(n²) +

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