diff --git a/research/try.cpp b/research/try.cpp index 4d7d3183c..41a5fefbe 100644 --- a/research/try.cpp +++ b/research/try.cpp @@ -1,6 +1,7 @@ /* try.cpp - to try out and experiment with new features.... * scons will create the binary bin/try */ +// 06/25 - provide a concept to accept _tuple-like_ objects // 06/25 - investigate function type detection of std::bind Binders // 12/24 - investigate problem when perfect-forwarding into a binder // 12/24 - investigate overload resolution on a templated function similar to std::get @@ -9,130 +10,123 @@ /** @file try.cpp - * Investigate ambiguities regarding the function type of standard binders. - * The Binder objects returned from `std::bind` provide a set of overloaded - * function call `operator()` (with variants for c/v). Unfortunately this defeats - * the common techniques to detect a function signature from a callable, when - * only a concrete instance of such a binder is given. Furthermore, looking - * at the definition of `class _Bind_result<_Result, _Functor(_Bound_args...)>` - * in my implementation of the C++ Stdlib, it seems we are pretty much out - * of luck, and even `std::function` fails with the template argument detection. - * A possible workaround could be to wrap the Binder object immediately into - * a lambda, but only if the actual types for the argument list can be - * provided directly to a template to generate this λ-wrapper. - * @note there is a nasty twist regarding const-correctness, which almost made - * this workaround fail altogether. The overloaded operator() from std::bind - * serves the same purpose (to deal with const/volatile), and this is the - * very reason that defeats the detection of the function signature. - * The workaround attempts to expose precisely one function call operator, - * and this becomes problematic as soon as the resulting object is processed - * further, and maybe bound into another lambda capture. Thus we define the - * wrapper class explicitly, so that any const-ness can be cast away. - * This turns out to be necessary in tuple-closure.hpp. + * Develop a concept to detect _tuple-like_ classes, based on the requirements + * of the »tuple protocol«. Using some ideas from [Stackoverflow] as starting point. + * [Stackoverflow]: https://stackoverflow.com/q/68443804/444796 */ #include "lib/format-cout.hpp" #include "lib/test/test-helper.hpp" #include "lib/test/diagnostic-output.hpp" -#include "lib/meta/function.hpp" -#include "lib/meta/variadic-rebind.hpp" +#include "lib/meta/tuple-helper.hpp" +#include "lib/hetero-data.hpp" #include "lib/util.hpp" -#include +#include -using std::forward; -using std::placeholders::_1; -using lib::meta::_Fun; +using std::string; +namespace lib { +namespace meta { + + template + concept tuple_sized = requires + { + { std::tuple_size::value } -> std::convertible_to; + }; + -void -fun (int& a) - { - std::cout << a << std::endl; - } - -short -fup (long l, long long ll) - { - return short(l - ll); - } - - - -/** WORKAROUND: wrap a binder to yield clear function signature */ -template -struct AdaptInvokable - { - template - static auto - buildWrapper (FUN&& fun) - { - - struct Wrap - { - FUN fun_; - - Wrap(FUN&& f) : fun_{forward(f)} { } - - auto - operator() (ARGS... args) - { - return fun_(forward(args)...); - } - }; - - return Wrap{forward(fun)}; -///////////////////////////////////////////////////////////// NOTE -///////////////////////////////////////////////////////////// can not use a Lambda, since we're then trapped -///////////////////////////////////////////////////////////// in an unsurmountable mixture of const and non-const -// return [functor = forward(fun)] -// (ARGS... args) mutable -// { -// return functor (forward (args)...); -// }; - } - }; - -template -auto -buildInvokableWrapper (FUN&& fun) - { - using ArgTypes = typename TYPES::Seq; - using Builder = typename lib::meta::RebindVariadic::Type; + template + concept tuple_adl_accessible = requires(TUP tup) + { + typename std::tuple_element_t; + { get(tup) } -> std::convertible_to&>; + }; - return Builder::buildWrapper (forward (fun)); + template + concept tuple_mem_accessible = requires(TUP tup) + { + typename std::tuple_element_t; + { tup.template get() } -> std::convertible_to&>; + }; + + template + concept tuple_accessible = tuple_mem_accessible or tuple_adl_accessible; + + template + class AndAll + { + template + static constexpr bool + canAccessAll (std::index_sequence) + { + return (tuple_accessible and ...); + } + + using IdxSeq = typename ElmTypes::Idx; + + public: + static constexpr bool can_AccessElement = canAccessAll(IdxSeq{}); + }; + + + template + concept tuple_like = not is_reference_v + and tuple_sized> + and AndAll>::can_AccessElement; + +}}//namespace lib::meta + + + +template +void +show() + { + SHOW_TYPE(X) } +template +void +show() + { + cout << "Tup!! "<< lib::test::showType() < ([](auto i) + { + using Elm = std::tuple_element_t; + cout <<" "<() <; - SHOW_TYPE (Bup) - SHOW_TYPE (Fub) -// using Sub = Fub::Sig; ////////////////Problem: does not compile + using Tup = std::tuple; + using Arr = std::array; + using Hetero = lib::HeteroData::Chain::ChainExtent::ChainType; - using Fut = decltype(fun); - SHOW_TYPE (_Fun::Sig) + SHOW_EXPR((lib::meta::tuple_sized )) + SHOW_EXPR((lib::meta::tuple_sized )) + SHOW_EXPR((lib::meta::tuple_sized )) + SHOW_EXPR((lib::meta::tuple_sized )) - auto wup = buildInvokableWrapper>(bup); + SHOW_EXPR((lib::meta::tuple_accessible)) +// SHOW_EXPR((lib::meta::tuple_accessible)) + SHOW_EXPR((lib::meta::tuple_accessible)) + SHOW_EXPR((lib::meta::AndAll::can_AccessElement)) + SHOW_EXPR((lib::meta::AndAll::can_AccessElement)) - using Wup = decltype(wup); - using WupSig = _Fun::Sig; - SHOW_TYPE (WupSig); - SHOW_EXPR (wup(3)) - SHOW_EXPR (sizeof(bup)) - SHOW_EXPR (sizeof(wup)) + SHOW_EXPR((lib::meta::tuple_like )) + SHOW_EXPR((lib::meta::tuple_like )) + SHOW_EXPR((lib::meta::tuple_like )) + SHOW_EXPR((lib::meta::tuple_like )) - auto waua = buildInvokableWrapper> (std::bind (fun, 55)); - waua (); - SHOW_TYPE (_Fun::Sig) + show(); + show(); + show(); + show(); cout << "\n.gulp." < Elm_t const& get() const noexcept - { - return const_cast(this)->get(); - } + { + return const_cast(this)->get(); + } /** diff --git a/src/lib/parse.hpp b/src/lib/parse.hpp index 128b7680d..2c6e06529 100644 --- a/src/lib/parse.hpp +++ b/src/lib/parse.hpp @@ -24,7 +24,7 @@ ** So what is provided here is _not a parser library_ — yet aims at »making ** simple things simple« and let you implement the complicated ones yourselves. ** Several decisions were taken accordingly, like only supporting std::string_view - ** and automatically consuming any leading whitespace. And notably the focus was + ** and to consume any leading whitespace automatically. And notably the focus was ** _not placed_ on the challenging aspects of parsing — while still allowing a ** pathway towards definition of arbitrarily recursive grammars, if so desired. ** @@ -40,7 +40,7 @@ ** the C++ matcher object as result — and thus essentially some pointers into ** the original sequence, which has to be passed in as C++ string_view. ** - ** An essential concept of this parsing support framework is that each parser + ** An essential feature of this parsing support framework is that each parser ** can be decorated by a _model transformation functor,_ which gets the result ** of the wrapped parser and can return _any arbitrary value object._ In essence, ** this framework does not provide the notion of an _abstract syntax tree_ — yet @@ -57,7 +57,7 @@ ** - `accept_opt` builds a clause to optionally accept in accordance ** to the given definition; if no match is found, parsing backtracks. ** - `accept_repeated` builds a clause to accept a repetition of the - ** structure accepted by its argument, optionally with an explicit delimiter + ** structure accepted by its argument, optionally with an explicit separator ** and possibly with a limited number of instances. The result values are ** obviously all from the same type and will be collected into a IterModel, ** which essentially is a std::vector (note: heap storage!). @@ -68,24 +68,26 @@ ** described by the SPEC _after_ the structure already described by the syntax. ** Both parts must succeed for the parse to be successful. The result value ** is packaged into a parse::SeqModel, which essentially is a tuple; when - ** attaching several .seq() specifications, it can become a N-ary tuple. + ** attaching several .seq() specifications, it is extended to a N-ary tuple. ** - `.alt(SPEC)` adds an _alternative branch_ to the existing syntax. ** Either part alone is sufficient for a successful parse. First the existing ** branch(es) are tried, and only if those do not succeed, backtracking is ** performed and then the alternative branch is tried. Once some match is ** found, further branches will _not be attempted._ (short-circuit). - ** Thus there is _always one_ result model, is placed into an AltModel, + ** Thus there is _always one_ result model, placed into an AltModel, ** which is a _variant data type_ with a common inline result buffer. ** The _selector field_ must be checked to find out which branch of the ** syntax succeeded, and then the result must be handled with its appropriate ** type, because the various branches can possibly yield entirely different ** result value types. - ** - `.repeat()` _sequentially adds_ a repeated clause be accepted - ** _after_ what the existing syntax accepts. The result is thus a SeqModel. + ** - `.repeat()` _sequentially adds_ a repeated clause to be accepted + ** _after_ what the existing syntax accepts. The result is thus a SeqModel, + ** with the result-model from the repetition in the last tuple element; + ** the repetition itself yields an IterModel. ** - `.bracket()` _sequentially adds_ a bracketing clause to be ** accepted _after_ parsing with the existing syntax. Again, the result - ** is a SeqModel, with the result-model from the repetition in the last - ** tuple element. The repetition itself yields an IterModel. + ** is a SeqModel, with the result-model from the bracket contents + ** packaged into the last tuple element. ** - `.bind(FUN)` is a postfix-operator and decorates the existing ** syntax with a result-binding functor `FUN`: The syntax's result value ** is passed into this functor and whatever this functor returns will @@ -99,8 +101,8 @@ ** A _recursive syntax definition_ is what unleashes the parsing technique's ** full strength; but recursive grammars can be challenging to master at times ** and may in fact lead to deadlock due to unlimited recursion. Since this - ** framework is focused on ease of use in simple situations, recursion is - ** considered an advanced usage and thus supported in a way that requires + ** framework is focused on ease of use for the simple situations, recursion + ** is considered an advanced usage and thus supported in a way that requires ** some preparation and help by the user. In essence... ** - a syntax clause to be referred recursively _must be pre-declared_ ** - this pre-declaration gives it a known, fixed result type and will diff --git a/tests/library/hetero-data-test.cpp b/tests/library/hetero-data-test.cpp index 8988878c7..7fe7a4b99 100644 --- a/tests/library/hetero-data-test.cpp +++ b/tests/library/hetero-data-test.cpp @@ -187,19 +187,19 @@ namespace test{ using Front = lib::HeteroData; using Cons2 = Front::Chain; using Data2 = Cons2::NewFrame; - using List2 = Cons2::ChainType; + using HeDa2 = Cons2::ChainType; using Acc4 = Cons2::AccessorFor; using Acc3 = Cons2::AccessorFor; using Acc2 = Front::Accessor<1>; using Acc1 = Front::Accessor<0>; using Cons3 = Cons2::ChainExtent; using Data3 = Cons3::NewFrame; - using List3 = Cons3::ChainType; + using HeDa3 = Cons3::ChainType; using Acc5 = Cons3::AccessorFor; using Acc6 = Cons3::AccessorFor; CHECK (2 == Front::size()); - CHECK (4 == List2::size()); - CHECK (6 == List3::size()); + CHECK (4 == HeDa2::size()); + CHECK (6 == HeDa3::size()); // // Note: up to now, not a single actual data element has been created // Moreover, individual blocks can be created in any order... @@ -278,7 +278,7 @@ namespace test{ CHECK (not isSameAdr (d2, v3)); CHECK (not isSameAdr (d3, v5)); // we can directly re-cast into another typed front-end - List3& fullChain = Cons3::recast(front); + HeDa3& fullChain = Cons3::recast(front); CHECK (isSameAdr (fullChain.get<2>(), std::get<0>(d2))); CHECK (isSameAdr (fullChain.get<3>(), std::get<1>(d2))); CHECK (isSameAdr (fullChain.get<4>(), std::get<0>(d3))); @@ -290,7 +290,7 @@ namespace test{ CHECK (isSameAdr (fullChain.get<4>(), v5)); CHECK (isSameAdr (fullChain.get<5>(), v6)); // we can even use partially specified chains - List2& partChain = Cons2::recast(fullChain); + HeDa2& partChain = Cons2::recast(fullChain); CHECK (isSameAdr (partChain.get<0>(), v1)); CHECK (isSameAdr (partChain.get<1>(), v2)); CHECK (isSameAdr (partChain.get<2>(), v3)); diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 57a2d460c..a89710b62 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -91601,7 +91601,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -91643,7 +91643,7 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
- + @@ -91783,6 +91783,9 @@ Date:   Thu Apr 20 18:53:17 2023 +0200
+ + +
@@ -156835,6 +156838,7 @@ std::cout << tmpl.render({"what", "World"}) << s + @@ -157075,6 +157079,24 @@ std::cout << tmpl.render({"what", "World"}) << s + + + + + + + + + + +

+ dazu im OS: clangd installieren +

+ +
+
+
+
@@ -166104,8 +166126,141 @@ Since then others have made contributions, see the log for the history. - + + + + + + + + + + + + + + + + + +

+ das limitiert die Nutzbarkeit ohnehin sehr stark, da man mit unlimitiert generischen Typen wenig Gemeinsames anfangen kann +

+ +
+
+ + + + + + +

+ ...auch der Standard gibt keine klaren Ziele an — vermutlich deshalb auch diese Diskrepanz, daß »tuple-like« nun doch nur für bestimmte STL-Klassen gelten soll; für diese engere Auswahl synthetisiert der Compiler nun (C++20) auch Vergleichsoperatoren. Aber darüber hinaus? es bleibt eine Art Parameter-Satz oder generischer Datensatz, mit dem man letztlich wenig anfangen kann ohne zusätzliche Anhaltspunkte (Referenzen, Kopierbarkeit, semantisches...) +

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

+ aber nur auf den einen Fall, der allgemeine Typen betrifft... +

+

+ d.h. wir unterstützen auch eine Memberfunktion get<i>() +

+ +
+
+ + + + +

+ mit duck-typing +

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

+ ...wenn man selber Typen für das »tuple-protocol« einrichtet, hat man i.d.R nicht die Zeit, sich um alle CV-Varianten + RValues zu kümmern (es gibt ja einen konkreten Use-case); allerdings spielen die CV-Varianten nur für eine get<i>()-Funktion tatsächlich eine Rolle, denn dort muß sich diese Variante auf den Ergebnistyp auswirken; man könnte diese Varianten komplett genersich per Library-Funktion aus einer Basis-Impl ableiten... +

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

+ und doch nur einen fest verdrahteten Spezialfall definiere, bloß weil's jetzt grade convenient ernscheint +

+ +
+
+ + + + + + + + +
@@ -166122,9 +166277,7 @@ Since then others have made contributions, see the log for the history.
- - - +
@@ -166209,9 +166362,7 @@ Since then others have made contributions, see the log for the history. - - - +

vermutlich ein sehr spezieller Compiler-Bug