diff --git a/doc/technical/howto/crackNuts.txt b/doc/technical/howto/crackNuts.txt index 4015c2091..d5741c03e 100644 --- a/doc/technical/howto/crackNuts.txt +++ b/doc/technical/howto/crackNuts.txt @@ -184,8 +184,44 @@ pick and manipulate individually:: can be programmed recursively, similar to LISP. The »bridge« is to unpack the variadic argument pack into the `lib::meta::Types` + +meta-manipulations:: + When you need to rebind and manipulate a variadic sequence, it helps to transform the sequence + into one of our meta sequence representations (`lib::meta::Types` or the Loki typelists). + In `variadic-helper.hpp`, we define a convenient rebinding template `lib::meta::Elms<>`, + which can work transparently on any type sequence or any tuple-like entity ++ + - to get at the variadics in a sequence representation + - to get a matching index sequence + - to rebind into another variadic template, using the same variadic sequence + - to apply a meta-function on each of the variadic types + - to compute a conjunction or disjunction of meta-predicates over the sequence ++ +tuple-like:: + This is a concept to match on any type in compliance with the »tuple protocol« ++ + - such a type must inject a specialisation of `std::tuple_size` + - and it must likewise support `std::tuple_element_t` + - and, based on that, expose a constexpr _getter function_ + - together this also implies, that such a type can be used in _structured bindings_ + (but note, structured bindings also work on plain arrays and on simple structs, + which are _not_ considered _tuple-like_ by themselves) ++ +apply to tuple-likes:: + Unfortunately, the standard has a glaring deficiency here, insofar it defines an _exposition only_ + concept, which is then hard mapped to support only some fixed types from the STL (tuples, pairs, + std::array and some range stuff). This was one of the main reasons to define our own `concept tuple_like` ++ + - but unfortunately, `std::apply` was fixed with C++20 to allow only the above mentioned fixed set of + types from the STL, while in theory there is no reason why not to allow any _tuple-like_ entity + - this forces us to define our own `lib::meta::apply` as a drop-in replacement for `std::apply` + - in addition to that, we also define a `lib::meta::getElm`, which is an universal getter + to work on any _tuple-like_, either with a `get` member function or a free-ADL function `get` + - note that starting with C++20 it is forbidden to inject function overloads into namespace std, + and thus a free `get` function must be injected as friend via ADL and used appropriately, i.e. + unqualified. ++ apply functor to each tuple element:: - A common trick is to use `std::apply` in combination with a _fold-expression_ + A common trick is to use `apply` in combination with a _fold-expression_ + - 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' diff --git a/src/lib/iter-zip.hpp b/src/lib/iter-zip.hpp index 7d6c6cf2f..1ee811f59 100644 --- a/src/lib/iter-zip.hpp +++ b/src/lib/iter-zip.hpp @@ -51,7 +51,7 @@ namespace lib { * Building block for a tupeled-iterator. * exposes the iterator API lifted to the product type (tuple). */ - template + template class ProductCore { ITUP iters_; @@ -76,8 +76,8 @@ namespace lib { bool checkPoint() const { //note: short-circuit - return std::apply ([](auto&... its) { return (bool(its) and ...); } - , iters_); + return meta::apply ([](auto&... its) { return (bool(its) and ...); } + , iters_); } ITUP& @@ -137,7 +137,7 @@ namespace lib { inline auto zip (ITS&& ...iters) { - auto access_result = [](auto& it)->decltype(auto){ return *it; }; // Note: pass-through result type (maybe reference) + auto access_result = [ ](auto& it)->decltype(auto){ return *it; }; // Note: pass-through result type (maybe reference) auto tuple_results = [&](auto& it){ return meta::mapEach (*it, access_result); }; // auto core = iter::ProductCore{iter::buildIterTuple (std::forward (iters)...)}; diff --git a/src/lib/meta/function-closure.hpp b/src/lib/meta/function-closure.hpp index a1cdb4b5b..356ed8010 100644 --- a/src/lib/meta/function-closure.hpp +++ b/src/lib/meta/function-closure.hpp @@ -230,17 +230,17 @@ namespace func{ * argument positions marked with `std::_Placeholder` instances * will remain _open_ to accept arguments on the resulting function. */ - template> + template requires(tuple_like>) auto bindArgTuple (FUN&& fun, TUP&& tuple) { - return std::apply ([functor = forward(fun)] - (auto&&... args) - { - return std::bind (move(functor) - ,forward (args) ...); - } - ,std::forward (tuple)); + return lib::meta::apply ([functor = forward(fun)] + (auto&&... args) + { + return std::bind (move(functor) + ,forward (args) ...); + } + ,std::forward (tuple)); } /** diff --git a/src/lib/meta/tuple-closure.hpp b/src/lib/meta/tuple-closure.hpp index 585c90b60..9400a1e9d 100644 --- a/src/lib/meta/tuple-closure.hpp +++ b/src/lib/meta/tuple-closure.hpp @@ -141,7 +141,7 @@ namespace meta{ auto operator() (RemainingParams remPar) const { - return apply (unConst(this)->partialClosure_, remPar); + return lib::meta::apply (unConst(this)->partialClosure_, remPar); }; CLO partialClosure_; diff --git a/src/lib/meta/tuple-helper.hpp b/src/lib/meta/tuple-helper.hpp index 0bbdb4c25..6e9af9711 100644 --- a/src/lib/meta/tuple-helper.hpp +++ b/src/lib/meta/tuple-helper.hpp @@ -18,6 +18,12 @@ ** to the tuple type provided by the standard library, including traits and ** helpers to build tuple types from metaprogramming and to pretty-print tuples. ** + ** Notably, a `concept tuple_like` is provided, which is satisfied for any type in compliance + ** with the »tuple protocol«. Together with a [generic accessor][\ref lib::meta::getElm), + ** this allows to handle all _tuple-like_ types uniformly. + ** @note Due to an unfortunate limitation of the standard, we're forced to provide our own alternative + ** implementation to replace `std::apply`, so that a function can be applied to any _tuple-like_ + ** ** Furthermore, a generic iteration construct is provided, to instantiate ** a generic Lambda for each element of a given tuple, which allows to write ** generic code »for each tuple element«. @@ -71,17 +77,13 @@ namespace meta { struct is_Tuple> : std::true_type { }; - - template - using enable_if_Tuple = lib::meta::enable_if>>; - - template - using disable_if_Tuple = lib::meta::disable_if>>; using std::remove_cv_t; using std::is_reference_v; + using std::remove_reference_t; + /** @internal building-block: a type supporting the `tuple_size` metafunction */ template concept tuple_sized = requires { @@ -89,6 +91,7 @@ namespace meta { }; + /** @internal building-block: a type where elements can be accessed through a `get` friend function */ template concept tuple_adl_accessible = requires(TUP tup) { @@ -96,6 +99,7 @@ namespace meta { { get(tup) } -> std::convertible_to&>; }; + /** @internal building-block: a type where elements can be accessed through a `get` member function */ template concept tuple_mem_accessible = requires(TUP tup) { @@ -115,12 +119,20 @@ namespace meta { }); + /** + * Concept to mark any type compliant to the »tuple protocol« + */ template concept tuple_like = not is_reference_v and tuple_sized> and tuple_accessible>; + /** + * Helper for abstracted / unified access to member elements of any _tuple-like_ + * @remark preferably uses a `get` member function, falling back to a + * free function `get`, which is found by ADL. + */ template requires(tuple_like>) decltype(auto) @@ -157,12 +169,12 @@ namespace meta { __unpack_and_apply (FUN&& f, TUP&& tup, std::index_sequence) { return std::invoke (std::forward (f) - ,get (std::forward(tup))... + ,getElm (std::forward(tup))... ); } - /** @internal invoke a metafunction with \a FUN and all element types from \a TUP */ + /** @internal invoke a metafunction with \a FUN and all element types from the _tuple-like_ \a TUP */ template class META, class FUN, class TUP> struct _InvokeMetafunTup { @@ -171,6 +183,14 @@ namespace meta { using Args = typename Prepend::Seq; using Type = typename RebindVariadic::Type; }; + template class META, class FUN, class TUP> + struct _InvokeMetafunTup + { + using Tupl = std::decay_t; + using Elms = typename ElmTypes::Apply; + using Args = typename Prepend::Seq; + using Type = typename RebindVariadic::Type; + }; template inline constexpr bool can_nothrow_invoke_tup = _InvokeMetafunTup::Type::value; @@ -184,7 +204,7 @@ namespace meta { * @todo 6/2025 as a first step, this replicates the implementation from C++17; * the second step would be to constrain this to a concept `tuple_like` */ - template + template requires(tuple_like>) constexpr decltype(auto) apply (FUN&& f, TUP&& tup) noexcept (can_nothrow_invoke_tup ) { @@ -205,15 +225,15 @@ namespace meta { * std::apply to unpack the tuple's contents into an argument pack and * then employ a fold expression with the comma operator. */ - template> + template requires(tuple_like>) constexpr void forEach (TUP&& tuple, FUN fun) { - std::apply ([&fun](auto&&... elms) - { - (fun (std::forward (elms)), ...); - } - ,std::forward (tuple)); + lib::meta::apply ([&fun](ELMS&&... elms) + { + (fun (std::forward(elms)), ...); + } + ,std::forward (tuple)); } /** @@ -230,20 +250,56 @@ namespace meta { * Notably this differs from #forEach, where a fold-expression with comma-operator * is used, which is guaranteed to evaluate from left to right. */ - template> + template requires(tuple_like>) constexpr 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)); + return lib::meta::apply ([&fun](ELMS&&... 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)); } + /** + * Specialisation of variadic access for any tuple-like + * @see variadic-helper.hpp + */ + template + struct ElmTypes + { + template + struct Extract; + template + struct Extract> + { + using ElmTypes = Types ...>; + }; + + static constexpr size_t SIZ = std::tuple_size_v; + + 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; + }; + + + namespace { // rebinding helper to create std::tuple from a type sequence diff --git a/src/lib/meta/util.hpp b/src/lib/meta/util.hpp index 863d5341e..f3e06a138 100644 --- a/src/lib/meta/util.hpp +++ b/src/lib/meta/util.hpp @@ -193,33 +193,6 @@ namespace meta { - namespace { - /** - * check for the necessary precondition, not sufficient. - * @remark Detecting the possibility of structured binding reliably will be possible with C++23. - * Even a partial implementation, covering only `std::tuple_element` is surprisingly - * complicated, due to the built-in limit checks. What do do with a `tuple<>`? - */ - template::value> - struct _Probe_TupleProtocol - { }; - } - template - using enable_if_TupleProtocol = std::void_t<_Probe_TupleProtocol>>; - - /** Trait template to detect a »tuple-like« type, - * which can be used in structured bindings. - * @note we check only one precondition: the support for `std::tuple_size` - */ - template - struct is_Structured - : std::false_type - { }; - - template - struct is_Structured> - : std::true_type - { }; /** Trait template for detecting a typelist type. diff --git a/src/lib/meta/variadic-helper.hpp b/src/lib/meta/variadic-helper.hpp index d3f81b445..501352dd6 100644 --- a/src/lib/meta/variadic-helper.hpp +++ b/src/lib/meta/variadic-helper.hpp @@ -202,38 +202,7 @@ namespace meta { 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 = Types::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; - }; + // Note: a further specialisation for any »tuple-like« is defined in tuple-helper.hpp diff --git a/src/steam/engine/feed-manifold.hpp b/src/steam/engine/feed-manifold.hpp index c3ca459cc..97cf3f0ad 100644 --- a/src/steam/engine/feed-manifold.hpp +++ b/src/steam/engine/feed-manifold.hpp @@ -81,11 +81,10 @@ #include "lib/meta/trait.hpp" #include "lib/meta/typeseq-util.hpp" #include "lib/meta/variadic-helper.hpp" +#include "lib/meta/tuple-helper.hpp" #include "lib/meta/generator.hpp" #include "lib/test/test-helper.hpp" -#include - namespace steam { namespace engine { @@ -97,7 +96,7 @@ namespace engine { using lib::meta::is_UnaryFun; using lib::meta::is_BinaryFun; using lib::meta::is_TernaryFun; - using lib::meta::is_Structured; + using lib::meta::tuple_like; using lib::meta::forEachIDX; using lib::meta::ElmTypes; using lib::meta::Tagged; @@ -475,8 +474,8 @@ namespace engine { auto& accessArg (ARG& arg) { - if constexpr (is_Structured()) - return std::get (arg); + if constexpr (tuple_like) + return lib::meta::getElm (arg); else return arg; } diff --git a/tests/library/meta/meta-utils-test.cpp b/tests/library/meta/meta-utils-test.cpp index 001d945c2..1afe4f3e9 100644 --- a/tests/library/meta/meta-utils-test.cpp +++ b/tests/library/meta/meta-utils-test.cpp @@ -55,7 +55,6 @@ namespace test { verify_genericTypeDisplay(); detect_stringConversion(); - detect_tupleProtocol(); detect_typeList(); } @@ -165,42 +164,6 @@ namespace test { } - void - detect_tupleProtocol() - { - // verify arbitrary non-structured types - CHECK ((not is_Structured())); - CHECK ((not is_Structured())); - CHECK ((not is_Structured())); - CHECK ((not is_Structured())); - CHECK ((not is_Structured())); - CHECK ((not is_Structured())); - CHECK ((not is_Structured())); - CHECK ((not is_Structured())); - CHECK ((not is_Structured())); - CHECK ((not is_Structured())); - CHECK ((not is_Structured())); - CHECK ((not is_Structured())); - CHECK ((not is_Structured())); - CHECK ((not is_Structured())); - CHECK ((not is_Structured())); - CHECK ((not is_Structured())); - CHECK ((not is_Structured())); - CHECK ((not is_Structured >())); - - // the following indeed support C++ tuple protocol - CHECK (( is_Structured >())); - CHECK (( is_Structured >())); - CHECK (( is_Structured >())); - CHECK (( is_Structured >())); - CHECK (( is_Structured >())); - CHECK (( is_Structured >())); - CHECK (( is_Structured >())); - CHECK (( is_Structured >())); - CHECK (( is_Structured >())); - } - - //-------------------------------------------------TEST-types-- using TheList = Types; using Arr = std::array; - using Het = lib::HeteroData::Chain::ChainExtent::ChainType; + using Het = lib::HeteroData::Chain::ChainExtent::ChainType; CHECK ( tuple_sized ); CHECK ( tuple_sized ); @@ -282,10 +282,42 @@ namespace test { CHECK ( tuple_accessible ); CHECK (not tuple_accessible ); + // verify the concept detects various tuple-like types CHECK ( tuple_like ); CHECK ( tuple_like ); CHECK ( tuple_like ); - CHECK (not tuple_like ); + CHECK (( tuple_like::List>>)); + CHECK (( tuple_like >)); + CHECK (( tuple_like >)); + CHECK (( tuple_like >)); + CHECK (( tuple_like >)); + CHECK (( tuple_like >)); + CHECK (( tuple_like >)); + CHECK (( tuple_like >)); + CHECK (( tuple_like >)); + CHECK (( tuple_like >)); + + // verify arbitrary non-structured types + CHECK (not tuple_like); + CHECK (not tuple_like); + CHECK (not tuple_like); + CHECK (not tuple_like); + CHECK (not tuple_like); + CHECK (not tuple_like); + CHECK (not tuple_like); + CHECK (not tuple_like); + CHECK (not tuple_like); + CHECK (not tuple_like); + CHECK (not tuple_like); + CHECK (not tuple_like); + CHECK (not tuple_like); + CHECK (not tuple_like); + CHECK (not tuple_like); + CHECK (not tuple_like); + CHECK (not tuple_like); + CHECK (not tuple_like); + CHECK((not tuple_like)); + CHECK((not tuple_like>)); // the tuple, the array and the HeteroData are tuple-like, // and will be handled by a special overload, exploiting the additional features diff --git a/tests/library/meta/variadic-helper-test.cpp b/tests/library/meta/variadic-helper-test.cpp index 27e870c38..ee385da9a 100644 --- a/tests/library/meta/variadic-helper-test.cpp +++ b/tests/library/meta/variadic-helper-test.cpp @@ -25,10 +25,10 @@ #include "lib/test/run.hpp" #include "lib/test/test-helper.hpp" #include "lib/meta/variadic-helper.hpp" +#include "lib/meta/tuple-helper.hpp" #include "lib/hetero-data.hpp" #include "lib/test/diagnostic-output.hpp"////////////TODO -#include #include using lib::test::showType; diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index d22e3d17d..c39b13958 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -58587,6 +58587,13 @@ + + + + + + + @@ -165792,7 +165799,8 @@ Since then others have made contributions, see the log for the history. - + + @@ -166017,8 +166025,8 @@ Since then others have made contributions, see the log for the history. - - + + @@ -166099,7 +166107,7 @@ Since then others have made contributions, see the log for the history. - + @@ -166126,7 +166134,9 @@ Since then others have made contributions, see the log for the history. - + + + @@ -166234,7 +166244,7 @@ Since then others have made contributions, see the log for the history. - + @@ -166257,43 +166267,34 @@ Since then others have made contributions, see the log for the history. - - - +

in der FeedManifold (spielt dort aber eine essentielle Rolle, weil nur auf diesem Weg die völlige Flexibilität in allen Argumenten erreicht wird)

- -
+
- - - +

ElmTypes<TUP>::Tup macht ein variadic rebind von einer Typ-Sequenz in ein std::tuple

- -
+
- - - +

...das bedeutet, der Fall einer reinen Loki-Typliste fällt erst mal weg (wird ja derzeit nicht verwendet), und der sonderbare »fallback« ändert nun sein Veralten und würde zu einem 1-Tupel. Dafür kann die gesamte constexpr-if-Logik wegfallen, weil ElmTypes<Tup> bereits eine Index-Sequenz fertig bereitstellt.

- -
+
@@ -166305,16 +166306,13 @@ Since then others have made contributions, see the log for the history. - - - +

siehe Entdeckung, die ich im Test dokumentiert habe: eine reine Compile-Time evaluation kann man zwar so machen, das wäre aber von hinten durch die Brust ins Auge...

- -
+
@@ -166325,9 +166323,7 @@ Since then others have made contributions, see the log for the history. - - - +

das ist nämlich eine Spezialisierung, die dann bereits auf dieses Concept aufsetzen würde... @@ -166336,8 +166332,7 @@ Since then others have made contributions, see the log for the history. - - + @@ -166354,20 +166349,102 @@ Since then others have made contributions, see the log for the history. - - - - + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + +

+ nützlich für Tests +

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

+ ...und jetzt weiß ich, warum das Pendant für std::apply so kompliziert implementiert ist!!! +

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

+ und zwar bildet das das Verhalten nach, das die Sprache automatisch bei Funktions-Anwendung vollzieht: wenn konkrete Parameter eine im Scope sichtbare Variable bezeichnen, dann bekommt die Funktion automatisch eine Referenz +

+ +
+ + + + + + + - + + + +
+ + + + + + + +
+ @@ -166478,7 +166555,7 @@ Since then others have made contributions, see the log for the history.
- + @@ -166493,7 +166570,7 @@ Since then others have made contributions, see the log for the history.
- +