From d052edf91de19df35201072b57d94071ea87d778 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 20 Jan 2025 23:55:42 +0100 Subject: [PATCH] Library: try out building a variant-model on top * the implementation of this ''Sum Type'' got quite technical and complicated; thus better to be extracted as separate library component * use this as base for the `AltModel` * make a usage sketch, invoking only the model interactions required --- src/lib/branch-case.hpp | 566 +++++++++++++++++++++++++++++ src/lib/parse.hpp | 171 +-------- tests/15library.tests | 9 +- tests/library/branch-case-test.cpp | 251 +++++++++++++ tests/library/parse-test.cpp | 53 ++- wiki/thinkPad.ichthyo.mm | 103 ++++-- 6 files changed, 949 insertions(+), 204 deletions(-) create mode 100644 src/lib/branch-case.hpp create mode 100644 tests/library/branch-case-test.cpp diff --git a/src/lib/branch-case.hpp b/src/lib/branch-case.hpp new file mode 100644 index 000000000..1581e013d --- /dev/null +++ b/src/lib/branch-case.hpp @@ -0,0 +1,566 @@ +/* + BRANCH-CASE.hpp - helpers for parsing textual specifications + + Copyright (C) + 2024, 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 branch-case.hpp + ** Convenience wrappers and definitions for parsing structured definitions. + ** Whenever a specification syntax entails nested structures, extracting contents + ** with regular expressions alone becomes tricky. Without much sophistication, a + ** directly implemented simple recursive descent parser is often less brittle and + ** easier to understand and maintain. With some helper abbreviations, notably + ** a combinator scheme to work from building blocks, a hand-written solution + ** can benefit from taking short-cuts, especially related to result bindings. + */ + + +#ifndef LIB_BRANCH_CASE_H +#define LIB_BRANCH_CASE_H + + +#include "lib/iter-adapter.hpp" +#include "lib/meta/function.hpp" +#include "lib/meta/trait.hpp" +#include "lib/regex.hpp" + +#include +#include +#include +#include +#include + +namespace util { + namespace parse { + + using std::move; + using std::forward; + using std::optional; + using lib::meta::_Fun; + using lib::meta::has_Sig; + using lib::meta::NullType; + using std::decay_t; + using std::tuple; + using std::array; + + using StrView = std::string_view; + + template + struct _MaxBufSiz; + template<> + struct _MaxBufSiz<> + { + static constexpr size_t siz = 0; + }; + template + struct _MaxBufSiz + { + static constexpr size_t siz = std::max (sizeof(T) + ,_MaxBufSiz::siz); + }; + + template + class BranchCase + { + public: + static constexpr auto TOP = sizeof...(TYPES) -1; + static constexpr auto SIZ = _MaxBufSiz::siz; + + template + using SlotType = std::tuple_element_t>; + + protected: + /** @internal default-created state is **invalid** */ + BranchCase() = default; + + + size_t branch_{0}; + + alignas(int64_t) + std::byte buffer_[SIZ]; + + template + TX& + emplace (INITS&&...inits) + { + return * new(&buffer_) TX(forward (inits)...); + } + + template + TX& + access () + { + return * std::launder (reinterpret_cast (&buffer_[0])); + } + + /** apply generic functor to the currently selected branch */ + template + auto + selectBranch (FUN&& fun) + { + if constexpr (0 < idx) + if (branch_ < idx) + return selectBranch (forward(fun)); + return fun (get()); + } + + public: + template + auto + apply (FUN&& fun) + { + return selectBranch (forward (fun)); + } + + ~BranchCase() + { + apply ([this](auto& it) + { using Elm = decay_t; + access().~Elm(); + }); + } + + template + BranchCase (size_t idx, INITS&& ...inits) + : branch_{idx} + { + apply ([&,this](auto& it) + { using Elm = decay_t; + if constexpr (std::is_constructible_v) + this->emplace (forward (inits)...); + }); + } + + BranchCase (BranchCase const& o) + { + branch_ = o.branch_; + BranchCase& unConst = const_cast (o); + unConst.apply ([this](auto& it) + { using Elm = decay_t; + this->emplace (it); + }); + } + + BranchCase (BranchCase && ro) + { + branch_ = ro.branch_; + ro.apply ([this](auto& it) + { using Elm = decay_t; + this->emplace (move (it)); + }); + } + + friend void + swap (BranchCase& o1, BranchCase o2) + { + using std::swap; + BranchCase tmp; + o1.apply ([&](auto& it) + { using Elm = decay_t; + tmp.emplace (move (o1.access())); + }); + swap (o1.branch_,o2.branch_); + o1.apply ([&](auto& it) + { using Elm = decay_t; + o1.emplace (move (o2.access())); + }); + o2.apply ([&](auto& it) + { using Elm = decay_t; + o2.emplace (move (tmp.access())); + }); + } + + BranchCase& + operator= (BranchCase ref) + { + swap (*this, ref); + return *this; + } + + template + auto + moveExtended() + { + using Extended = BranchCase; + Extended& upFaked = reinterpret_cast (*this); + return Extended (move (upFaked)); + } + + + size_t + selected() const + { + return branch_; + } + + template + SlotType& + get() + { + return access>(); + } + }; + +#if false ///////////////////////////////////////////////////////////////////////////////TODO accommodate + /** + */ + template + struct Eval + { + using Result = RES; + optional result; + size_t consumed{0}; + }; + + template + struct Connex + : util::NonAssign + { + using PFun = FUN; + PFun parse; + + using Result = typename _Fun::Ret::Result; + + Connex (FUN&& pFun) + : parse{move(pFun)} + { } + }; + + auto + buildConnex(NullType) + { + return Connex{[](StrView) -> Eval + { + return {NullType{}}; + }}; + } + using NulP = decltype(buildConnex (NullType())); + + auto + buildConnex (regex rex) + { + return Connex{[regEx = move(rex)] + (StrView toParse) -> Eval + { // skip leading whitespace... + size_t pre = leadingWhitespace (toParse); + toParse = toParse.substr(pre); + auto result{matchAtStart (toParse,regEx)}; + size_t consumed = result? pre+result->length() : 0; + return {move(result), consumed}; + }}; + } + using Term = decltype(buildConnex (std::declval())); + + Term + buildConnex (string const& rexDef) + { + return buildConnex (regex{rexDef}); + } + + template + auto + buildConnex (Connex const& anchor) + { + return Connex{anchor}; + } + template + auto + buildConnex (Connex && anchor) + { + return Connex{move(anchor)}; + } + + + template + auto + adaptConnex (CON&& connex, BIND&& modelBinding) + { + using RX = typename CON::Result; + using Arg = lib::meta::_FunArg; + static_assert (std::is_constructible_v, + "Model binding must accept preceding model result."); + using AdaptedRes = typename _Fun::Ret; + return Connex{[origConnex = forward(connex) + ,binding = forward(modelBinding) + ] + (StrView toParse) -> Eval + { + auto eval = origConnex.parse (toParse); + if (eval.result) + return {binding (move (*eval.result))}; + else + return {std::nullopt}; + }}; + } + + + /* ===== building structured models ===== */ + + /** + * Product Model : results from a conjunction of parsing clauses, + * which are to be accepted in sequence, one after the other. + */ + template + struct SeqModel + : tuple + { + static constexpr size_t SIZ = sizeof...(RESULTS); + using Seq = lib::meta::TySeq; + using Tup = std::tuple; + + SeqModel() = default; + + template + SeqModel (SeqModel&& seq, XX&& extraElm) + : Tup{std::tuple_cat (seq.extractTuple() + ,make_tuple (forward (extraElm)) )} + { } + + template + SeqModel (X1&& res1, X2&& res2) + : Tup{move(res1), move(res2)} + { } + + 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. + */ + template + struct AltModel + { + + }; + + + /** Special case Product Model to represent iterative sequence */ + template + struct IterModel + { + + }; + + /** Marker-Tag for the result from a sub-expression, not to be joined */ + template + struct SubModel + { + + }; + + /** Standard case : combinator of two model branches */ + template class TAG, class R1, class R2 =void> + struct _Join + { + using Result = TAG; + }; + + /** Generic case : extend a structured model by further branch */ + template class TAG, class...RS, class R2> + struct _Join,R2> + { + using Result = TAG; + }; + + /** Special Case : absorb sub-expression without flattening */ + template class TAG, class R1, class R2> + struct _Join,R2> + { + using Result = TAG; + }; + template class TAG, class R1, class R2> + struct _Join> + { + using Result = TAG; + }; + template class TAG, class R1, class R2> + struct _Join,SubModel> + { + using Result = TAG; + }; + + /** Special Case : absorb further similar elements into IterModel */ + template + struct _Join, RES> + { + using Result = IterModel; + }; + + + /** accept sequence of two parse functions */ + template + auto + sequenceConnex (C1&& connex1, C2&& connex2) + { + using R1 = typename decay_t::Result; + using R2 = typename decay_t::Result; + using ProductResult = typename _Join::Result; + using ProductEval = Eval; + return Connex{[conL = forward(connex1) + ,conR = forward(connex2) + ] + (StrView toParse) -> ProductEval + { + auto eval1 = conL.parse (toParse); + if (eval1.result) + { + size_t posAfter1 = eval1.consumed; + StrView restInput = toParse.substr(posAfter1); + auto eval2 = conR.parse (restInput); + if (eval2.result) + { + uint consumedOverall = posAfter1 + eval2.consumed; + return ProductEval{ProductResult{move(*eval1.result) + ,move(*eval2.result)} + ,consumedOverall + }; + } + } + return ProductEval{std::nullopt}; + }}; + } + + + template + class Syntax; + + + template + class Parser + : public CON + { + using PFun = typename CON::PFun; + static_assert (_Fun(), "Connex must define a parse-function"); + + public: + using Connex = CON; + using Result = typename CON::Result; + +using Sigi = typename _Fun::Sig; +//lib::test::TypeDebugger buggi; +//lib::test::TypeDebugger guggi; + + static_assert (has_Sig(StrView)>() + ,"Signature of the parse-function not suitable"); + + Eval + operator() (StrView toParse) + { + return CON::parse (toParse); + } + + template + Parser (SPEC&& spec) + : CON{buildConnex (forward (spec))} + { } + +// template +// Parser (Syntax const& anchor) +// : CON{anchor} +// { } +// template +// Parser (CON const& anchor) +// : CON{anchor} +// { } + }; + + Parser(NullType) -> Parser; + Parser(regex &&) -> Parser; + Parser(regex const&) -> Parser; + Parser(string const&) -> Parser; + + template + Parser(Connex const&) -> Parser>; +// +// template +// Parser(Syntax const&) -> Parser; + + + template + class Syntax + : public Eval + { + PAR parse_; + + public: + using Connex = typename PAR::Connex; + using Result = typename PAR::Result; + + bool success() const { return bool(Syntax::result); } + bool hasResult() const { return bool(Syntax::result); } + Result& getResult() { return * Syntax::result; } + Result&& extractResult(){ return move(getResult()); } + + Syntax() + : parse_{NullType()} + { } + + explicit + Syntax (PAR&& parser) + : parse_{move (parser)} + { } + + explicit + operator bool() const + { + return success(); + } + + Syntax&& + parse (StrView toParse) + { + eval() = parse_(toParse); + return move(*this); + } + + Connex const& + getConny() const + { + return parse_; + } + + template + auto + seq (SPEC&& clauseDef) + { + return accept( + sequenceConnex (move(parse_) + ,Parser{forward (clauseDef)})); + } + + private: + Eval& + eval() + { + return *this; + } + }; + + template + auto + accept (SPEC&& clauseDef) + { + return Syntax{Parser{forward (clauseDef)}}; + } + + // template + // Parser(Syntax const&) -> Parser; + +#endif /////////////////////////////////////////////////////////////////////////////////////TODO accommodate + }// namespace parse + +//using parse::accept; +}// namespace util + +namespace lib { +}// namespace lib +#endif/*LIB_BRANCH_CASE_H*/ diff --git a/src/lib/parse.hpp b/src/lib/parse.hpp index 2e188537b..f84a8b2b0 100644 --- a/src/lib/parse.hpp +++ b/src/lib/parse.hpp @@ -27,7 +27,7 @@ #define LIB_PARSE_H -#include "lib/iter-adapter.hpp" +#include "lib/branch-case.hpp" #include "lib/meta/function.hpp" #include "lib/meta/trait.hpp" #include "lib/regex.hpp" @@ -53,158 +53,6 @@ namespace util { using StrView = std::string_view; - template - struct _MaxBufSiz; - template<> - struct _MaxBufSiz<> - { - static constexpr size_t siz = 0; - }; - template - struct _MaxBufSiz - { - static constexpr size_t siz = std::max (sizeof(T) - ,_MaxBufSiz::siz); - }; - - template - class BranchCase - { - public: - static constexpr auto TOP = sizeof...(TYPES) -1; - static constexpr auto SIZ = _MaxBufSiz::siz; - - template - using SlotType = std::tuple_element_t>; - - protected: - size_t branch_{0}; - - alignas(int64_t) - std::byte buffer_[SIZ]; - - template - TX& - emplace (INITS&&...inits) - { - return * new(&buffer_) TX(forward (inits)...); - } - - template - TX& - access () - { - return * std::launder (reinterpret_cast (&buffer_[0])); - } - - /** apply generic functor to the currently selected branch */ - template - auto - selectBranch (FUN&& fun) - { - if constexpr (0 < idx) - if (branch_ < idx) - return selectBranch (forward(fun)); - return fun (get()); - } - - BranchCase() = default; - public: - template - auto - apply (FUN&& fun) - { - return selectBranch (forward (fun)); - } - - ~BranchCase() - { - apply ([this](auto& it) - { using Elm = decay_t; - access().~Elm(); - }); - } - - template - BranchCase (size_t idx, INITS&& ...inits) - { - branch_ = idx; - apply ([&,this](auto& it) - { using Elm = decay_t; - emplace (forward (inits)...); - }); - } - - BranchCase (BranchCase const& o) - { - branch_ = o.branch_; - BranchCase& unConst = const_cast (o); - unConst.apply ([this](auto& it) - { using Elm = decay_t; - this->emplace (it); - }); - } - - BranchCase (BranchCase && ro) - { - branch_ = ro.branch_; - ro.apply ([this](auto& it) - { using Elm = decay_t; - this->emplace (move (it)); - }); - } - - friend void - swap (BranchCase& o1, BranchCase o2) - { - using std::swap; - BranchCase tmp; - o1.apply ([&](auto& it) - { using Elm = decay_t; - tmp.emplace (move (o1.access())); - }); - swap (o1.branch_,o2.branch_); - o1.apply ([&](auto& it) - { using Elm = decay_t; - o1.emplace (move (o2.access())); - }); - o2.apply ([&](auto& it) - { using Elm = decay_t; - o2.emplace (move (tmp.access())); - }); - } - - BranchCase& - operator= (BranchCase ref) - { - swap (*this, ref); - return *this; - } - - template - auto - moveExtended() - { - using Extended = BranchCase; - Extended& upFaked = reinterpret_cast (*this); - return Extended (move (upFaked)); - } - - - size_t - selected() const - { - return branch_; - } - - template - SlotType& - get() - { - return access>(); - } - }; - /** */ @@ -334,8 +182,25 @@ namespace util { */ template struct AltModel + : BranchCase { + using _Model = BranchCase; + template + using Additionally = AltModel; + + template> + AltModel (INIT&& init) + : _Model{_Model::TOP, forward (init)} + { } + + template + Additionally + addBranch() + { + Additionally& upFaked = reinterpret_cast&> (*this); + return {move (upFaked)}; + } }; diff --git a/tests/15library.tests b/tests/15library.tests index e1fc49b99..ae3bc8571 100644 --- a/tests/15library.tests +++ b/tests/15library.tests @@ -7,7 +7,12 @@ return: 0 END -TEST "CmdlineWrapper_test" CmdlineWrapper_test < out-lit: wrapping cmdline: @@ -43,7 +48,7 @@ return: 0 END -TEST "CustomSharedPtr_test" CustomSharedPtr_test < + +  **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 branch-case-test.cpp + ** unit test \ref BranchCase_test + */ + + + +#include "lib/test/run.hpp" +#include "lib/test/test-helper.hpp" +#include "lib/branch-case.hpp" +//#include "lib/iter-explorer.hpp" +//#include "lib/format-util.hpp" +#include "lib/meta/tuple-helper.hpp" +#include "lib/test/diagnostic-output.hpp"//////////////////TODO +//#include "lib/util.hpp" + +//#include +//#include + + + +namespace util { +namespace parse{ +namespace test { + + using lib::meta::is_Tuple; + using std::get; +// using util::join; +// using util::isnil; +// using std::vector; +// using std::shared_ptr; +// using std::make_shared; + +// using LERR_(ITER_EXHAUST); +// using LERR_(INDEX_BOUNDS); + + + namespace { // test fixture + +// const uint NUM_ELMS = 10; + +// using Numz = vector; + + } // (END)fixture + + + + + + + + /************************************************************************//** + * @test verify helpers and shortcuts for simple recursive descent parsing + * of structured data and specifications. + * + * @see parse.hpp + * @see proc-node.cpp "usage example" + */ + class BranchCase_test : public Test + { + + virtual void + run (Arg) + { + simpleBlah(); + acceptAlternatives(); + } + + + /** @test TODO just blah. */ + void + simpleBlah () + { + } + +#if false ////////////////////////////////////////////////////////////////////////////TODO accommodate + /** @test define a terminal symbol to match by parse. */ + void + acceptTerminal() + { + // set up a parser function to accept some token as terminal + auto parse = Parser{"hello (\\w+) world"}; + string toParse{"hello vile world of power"}; + auto eval = parse (toParse); + CHECK (eval.result); + auto res = *eval.result; // ◁——————————— the »result model« of a terminal parse is the RegExp-Matcher + CHECK (res.ready() and not res.empty()); + CHECK (res.size() == "2"_expect ); + CHECK (res.position() == "0"_expect ); + CHECK (res.str() == "hello vile world"_expect ); + CHECK (res[1] == "vile"_expect ); + CHECK (res.suffix() == " of power"_expect ); + + auto syntax = Syntax{move (parse)}; // Build a syntax clause from the simple terminal symbol parser + CHECK (not syntax.hasResult()); + syntax.parse (toParse); + CHECK (syntax.success()); // Syntax clause holds an implicit state from the last parse + CHECK (syntax.getResult()[1] == "vile"_expect); + + // shorthand notation to start building a syntax + auto syntax2 = accept ("(\\w+) world"); + CHECK (not syntax2.hasResult()); + syntax2.parse (toParse); + CHECK (not syntax2.success()); + string bye{"cruel world"}; + syntax2.parse (bye); + CHECK (syntax2.success()); + CHECK (syntax2.getResult()[1] == "cruel"_expect); + + // going full circle: extract parser def from syntax +// using Conn = decltype(syntax2)::Connex; +// Conn conny{syntax2}; +// auto parse2 = Parser{conny}; + auto parse2 = Parser{syntax2.getConny()}; + CHECK (eval.result->str(1) == "vile"); + eval = parse2 (toParse); + CHECK (not eval.result); + eval = parse2 (bye); + CHECK (eval.result->str(1) == "cruel"); + } + + + /** @test define a sequence of syntax structures to match by parse. */ + void + acceptSequential() + { + // Demonstration: how sequence combinator works.... + auto term1 = buildConnex ("hello"); + auto term2 = buildConnex ("world"); + auto parseSeq = [&](StrView toParse) + { + using R1 = decltype(term1)::Result; + using R2 = decltype(term2)::Result; + using ProductResult = std::tuple; + using ProductEval = Eval; + auto eval1 = term1.parse (toParse); + if (eval1.result) + { + uint end1 = eval1.consumed; + StrView restInput = toParse.substr(end1); + auto eval2 = term2.parse (restInput); + if (eval2.result) + { + uint consumedOverall = end1 + eval2.consumed; + return ProductEval{ProductResult{move(*eval1.result) + ,move(*eval2.result)} + ,consumedOverall + }; + } + } + return ProductEval{std::nullopt}; + }; + string s1{"hello millions"}; + string s2{"hello world"}; + string s3{" hello world trade "}; + + auto e1 = parseSeq(s1); + CHECK (not e1.result); // Syntax 'hello'>>'world' does not accept "hello millions" + auto e2 = parseSeq(s2); + CHECK ( e2.result); + + using SeqRes = std::decay_t; // Note: the result type depends on the actual syntax construction + CHECK (is_Tuple()); // Result model from sequence is the tuple of terminal results + auto& [r1,r2] = *e2.result; + CHECK (r1.str() == "hello"_expect); + CHECK (r2.str() == "world"_expect); + + CHECK (term2.parse(" world").result); // Note: leading whitespace skipped by the basic terminal parsers + CHECK (term2.parse("\n \t world ").result); + CHECK (not term2.parse(" old ").result); + + + // DSL parse clause builder: a sequence of terminals... + auto syntax = accept("hello").seq("world"); + + // Perform the same parse as demonstrated above.... + CHECK (not syntax.hasResult()); + syntax.parse(s1); + CHECK (not syntax.success()); + syntax.parse(s2); + CHECK (syntax); + SeqRes seqModel = syntax.getResult(); + CHECK (get<0>(seqModel).str() == "hello"_expect); + CHECK (get<1>(seqModel).str() == "world"_expect); + + + // can build extended clause from existing one + auto syntax2 = syntax.seq("trade"); + CHECK (not syntax2.hasResult()); + syntax2.parse(s2); + CHECK (not syntax2.success()); + syntax2.parse(s3); + CHECK (syntax2.success()); + auto seqModel2 = syntax2.getResult(); // Note: model of consecutive sequence is flattened into a single tuple + CHECK (get<0>(seqModel2).str() == "hello"_expect); + CHECK (get<1>(seqModel2).str() == "world"_expect); + CHECK (get<2>(seqModel2).str() == "trade"_expect); + } +#endif /////////////////////////////////////////////////////////////////////////////////////TODO accommodate + + /** @test TODO define alternative syntax structures to match by parse. */ + void + acceptAlternatives() + { + using Branch = BranchCase; +SHOW_EXPR(sizeof(Branch)); + Branch b1{1, 42}; +SHOW_EXPR(b1.selected()); +SHOW_EXPR(b1.SIZ); +SHOW_EXPR(b1.TOP); +SHOW_EXPR(b1.get<1>()); +SHOW_EXPR(b1.get<0>()); + Branch b2{0,'x'}; +SHOW_EXPR(b2.selected()); +SHOW_EXPR(b2.get<1>()); +SHOW_EXPR(b2.get<0>()); + Branch b3{b1}; +SHOW_EXPR(b3.selected()); +SHOW_EXPR(b3.get<1>()); +SHOW_EXPR(b3.get<0>()); + b3 = b2; +SHOW_EXPR(b3.selected()); +SHOW_EXPR(b3.get<1>()); +SHOW_EXPR(b3.get<0>()); + auto bx = b1.moveExtended(); +SHOW_EXPR(sizeof(bx)) +SHOW_EXPR(bx.SIZ); +SHOW_EXPR(bx.TOP); +SHOW_EXPR(bx.selected()); +SHOW_EXPR(bx.get<1>()); +SHOW_EXPR(bx.get<0>()); + } + }; + + LAUNCHER (BranchCase_test, "unit common"); + + +}}} // namespace util::parse::test + diff --git a/tests/library/parse-test.cpp b/tests/library/parse-test.cpp index 8f27bf1b4..8483f4991 100644 --- a/tests/library/parse-test.cpp +++ b/tests/library/parse-test.cpp @@ -216,33 +216,32 @@ namespace test { void acceptAlternatives() { - using Branch = BranchCase; -SHOW_EXPR(sizeof(Branch)); - Branch b1{1, 42}; -SHOW_EXPR(b1.selected()); -SHOW_EXPR(b1.SIZ); -SHOW_EXPR(b1.TOP); -SHOW_EXPR(b1.get<1>()); -SHOW_EXPR(b1.get<0>()); - Branch b2{0,'x'}; -SHOW_EXPR(b2.selected()); -SHOW_EXPR(b2.get<1>()); -SHOW_EXPR(b2.get<0>()); - Branch b3{b1}; -SHOW_EXPR(b3.selected()); -SHOW_EXPR(b3.get<1>()); -SHOW_EXPR(b3.get<0>()); - b3 = b2; -SHOW_EXPR(b3.selected()); -SHOW_EXPR(b3.get<1>()); -SHOW_EXPR(b3.get<0>()); - auto bx = b1.moveExtended(); -SHOW_EXPR(sizeof(bx)) -SHOW_EXPR(bx.SIZ); -SHOW_EXPR(bx.TOP); -SHOW_EXPR(bx.selected()); -SHOW_EXPR(bx.get<1>()); -SHOW_EXPR(bx.get<0>()); + using R1 = char; + using R2 = string; + using R3 = double; + + using A1 = AltModel; + string s{"second"}; + using A2 = A1::Additionally; + A2 model2{s}; +SHOW_EXPR(sizeof(A2)); +SHOW_EXPR(model2.SIZ); +SHOW_EXPR(model2.TOP); +SHOW_EXPR(model2.selected()) +SHOW_EXPR(model2.get<1>()) + using A3 = A2::Additionally; + A3 model3{model2.addBranch()}; +SHOW_TYPE(A3) +SHOW_EXPR(sizeof(A3)); +SHOW_EXPR(model3.SIZ); +SHOW_EXPR(model3.TOP); +SHOW_EXPR(model3.selected()) +SHOW_EXPR(model3.get<1>()) + auto res = move(model3); +SHOW_TYPE(decltype(res)) +SHOW_EXPR(sizeof(res)) +SHOW_EXPR(res.selected()) +SHOW_EXPR(res.get<1>()) } }; diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index d579fe3cc..b19695e4d 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -55960,19 +55960,21 @@ - + + - + - + + @@ -55990,7 +55992,7 @@ - + @@ -56024,14 +56026,15 @@ - + + - + @@ -56040,21 +56043,29 @@

+
+ + + +
- - - + + + + + - + + @@ -56065,7 +56076,7 @@ - + @@ -56086,8 +56097,8 @@ - - + + @@ -56137,19 +56148,20 @@ - + - + - + + @@ -56190,8 +56202,7 @@ ...weil praktisch alle Kern-Methoden nun in zweifacher Ausfertigung gecodet werden müssen: einmal rekursiv, und einmal für den Abschluß

- -
+
@@ -56207,8 +56218,7 @@
-
- + @@ -56341,9 +56351,28 @@ + + + + +

+ Fazit: implementierbar mit generischem λ-Visitor +

+ + +
+ + + + - - + + + + +
+ + @@ -56362,10 +56391,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +