diff --git a/src/lib/iter-tree-explorer.hpp b/src/lib/iter-tree-explorer.hpp index fe896534e..e28be857a 100644 --- a/src/lib/iter-tree-explorer.hpp +++ b/src/lib/iter-tree-explorer.hpp @@ -1530,7 +1530,7 @@ namespace lib { * which for the consumer side behave like `iterNext()` calls. If a layer needs to do something special for * `iterNext()`, it needs to perform a similar action for `expandChildren()`. * - it must be behave like a default-constructible, copyable value object - * @return augmented TreeExplorer, incorporating and adpting the injected layer + * @return augmented TreeExplorer, incorporating and adapting the injected layer */ template class LAY> auto diff --git a/src/lib/polymorphic-value.hpp b/src/lib/polymorphic-value.hpp index 6471d4e22..24d23a038 100644 --- a/src/lib/polymorphic-value.hpp +++ b/src/lib/polymorphic-value.hpp @@ -477,6 +477,12 @@ namespace lib { typedef IFA Interface; + Interface& + getPayload() + { + return accessEmbedded(); + } + operator Interface& () { return accessEmbedded(); diff --git a/src/lib/verb-token.hpp b/src/lib/verb-token.hpp index cf0594a59..695fef7f7 100644 --- a/src/lib/verb-token.hpp +++ b/src/lib/verb-token.hpp @@ -113,7 +113,7 @@ namespace lib { } Literal const& - getID() + getID() const { return token_; } diff --git a/src/lib/verb-visitor.hpp b/src/lib/verb-visitor.hpp index d880bffbb..b6a54d2c4 100644 --- a/src/lib/verb-visitor.hpp +++ b/src/lib/verb-visitor.hpp @@ -111,18 +111,28 @@ namespace lib { } + /** Building block: the Interface to cause the invocation */ template struct VerbInvoker : polyvalue::CloneValueSupport // mark and mix-in virtual copy construction support { virtual ~VerbInvoker() { } - virtual RET applyTo (REC&) =0; + virtual RET applyTo (REC&) =0; + virtual Literal getID() const =0; + + bool operator== (VerbInvoker const& o) const { return getID() == o.getID(); } + bool operator!= (VerbInvoker const& o) const { return getID() != o.getID(); } }; + template struct VerbHolder; + /** + * Building block: actual storage for a "verb" (function pointer), + * together with the pre-bound invocation arguments for this specific operation. + */ template struct VerbHolder : VerbInvoker @@ -134,13 +144,21 @@ namespace lib { /** meta-sequence to pick argument values from the storage tuple */ using SequenceIterator = typename meta::BuildIdxIter::Ascending; + /** Storage for the argument tuple */ Args args_; - VerbHolder (typename Verb::Handler handlerRef, Literal verbID, ARGS&&... args) + template + VerbHolder (typename Verb::Handler handlerRef, Literal verbID, PARS&&... args) : Verb{handlerRef, verbID} - , args_{std::forward (args)...} + , args_{std::forward (args)...} { } + Literal + getID() const override + { + return Verb::getID(); + } + RET applyTo (REC& receiver) override { @@ -148,45 +166,88 @@ namespace lib { } private: + /** @internal actual function invocation, thereby unpacking the argument tuple */ template RET invokeVerb (REC& receiver, meta::IndexSeq) - { //////////////////////////////////////////TICKET #1006 | TICKET #1184 why do we need std::forward here? the target is a "perfect forwarding" function, which should be able to receive a LValue reference to the tuple element just fine... - return (receiver.*Verb::handler_)(std::get (args_)...); + { + return (receiver.*Verb::handler_) (std::get (args_)...); } }; + /******************************************************************************************//** + * A self-contained token to embody a specific yet abstracted operation, + * together with a concrete set of suitable arguments. The concrete operation + * is suppled on invocation, when the VerbPack is combined with an actual _receiver_ + * object, implementing the interface `REC` and thus providing the function implementation. + * VerbPack represents a kind of double-dispatch, flexible both on the actual operation + * (embodied into the given VerbPack object) and also flexible in the concrete receiver. + * @tparam REC the "visitor interface" to invoke operations on + * @tparam RET expected (common) return value of the bound operations (can be `void`) + * @tparam arg_storage maximum storage size to reserve as buffer for actual function parameters. + * @remarks + * - binding an operation with arguments exceeding `arg_storage` triggers a static assertion + * - the resulting VerbPack object has value semantics and is copyable, to the extent any + * embedded function arguments are copyable by themselves. + */ template class VerbPack : public PolymorphicValue, storageOverhead(arg_storage)> { - using PolyHolder = PolymorphicValue, storageOverhead(arg_storage)>; + using Dispatcher = VerbInvoker; // the interface to talk to our payload + using PolyHolder = PolymorphicValue; + template + struct HandlerTypeDetector + { + static_assert (!sizeof(FUN), "handler must be a function member pointer for the given receiver"); + }; template - using PayloadType = VerbHolder*; + struct HandlerTypeDetector + { + using Verb = VerbToken; + using Payload = VerbHolder; + }; + + template + using PayloadType = typename HandlerTypeDetector::Payload *; - template - using Handler = typename VerbToken::Handler; public: - template - VerbPack (Handler handler, Literal verbID, ARGS&&... args) - : PolyHolder(PayloadType(), handler, verbID, std::forward(args)...) + /** setup a VerbPack for a given operation on the interface `REC` + * @param handler function member-pointer to define the operation + * @param verbID unique ID to designate the token; equality is based + * on this `verbID`, all tokens with same ID count as equal + * @param args arbitrary (yet suitable) arguments to pre-bind and use later + * when actually invoking the operation on a concrete receiver + */ + template + VerbPack (FUN handler, Literal verbID, ARGS&&... args) + : PolyHolder(PayloadType(), handler, verbID, std::forward(args)...) { } + /** + * Core operation: invoke the operation for this "verb" with the pre-bound parameters + * @param receiver a subclass of `REC`, providing the function to invoke + * @return result of performing the actual operation + */ RET applyTo (REC& receiver) { - VerbInvoker& dispatch(*this); - return dispatch.applyTo (receiver); + return PolyHolder::getPayload().applyTo (receiver); + } + + operator string() const + { + return "VerbPack(" + + string{util::unConst(this)->getPayload().getID()} + + ")"; } }; - - -} // namespace lib +} // namespace lib #endif /*LIB_VERB_VISITOR_H*/ diff --git a/tests/15library.tests b/tests/15library.tests index 88c97555e..d5a87508b 100644 --- a/tests/15library.tests +++ b/tests/15library.tests @@ -740,6 +740,17 @@ return: 0 END +TEST "verb token with embedded arguments" VerbVisitorDispatch_test < 'haw-hawhaw-hawhaw-hawhaw-haw' +out-lit: dispatching VerbPack(honk) -> 'quaack-quaack!' +out-lit: dispatching VerbPack(honk) -> 'Hoonk-Hoonk!' +out-lit: dispatching VerbPack(woof) -> 'Woof..Woof..' +out-lit: dispatching VerbPack(moo) -> 'Moo__Moo__Moo' +out-lit: dispatching VerbPack(meh) -> 'Meh?' +return: 0 +END + + TEST "VectorTransfer_test" VectorTransfer_test <; @@ -65,53 +67,54 @@ namespace test{ Verb VERB(Receiver, honk); Verb VERB(Receiver, moo); Verb VERB(Receiver, meh); - } - - - /** - * a receiver of verb-tokens, - * which renders them verbosely - */ - class VerboseRenderer - : public Receiver - { - string woof() { return "Woof-Woof!"; } - string honk() { return "Honk-Honk!"; } - string moo() { return "Moo-Moo!"; } - string meh() { return "Meh!"; } - }; - - - /** - * Statefull receiver of verb-tokens. - */ - class RecollectingReceiver - : public Receiver - { - string verb_; - _Fmt fmt_; - - string - buildResultTerm (string nextToken) - { - string resultExpression (fmt_ % verb_ % nextToken); - verb_ = nextToken; - return resultExpression; - } - - - string woof() { return buildResultTerm (VERB_woof); } - string honk() { return buildResultTerm (VERB_honk); } - string moo() { return buildResultTerm (VERB_moo); } - string meh() { return buildResultTerm (VERB_meh); } - - - public: - RecollectingReceiver() - : verb_(BEGINNING) - , fmt_("%s followed by %s") - { } - }; + + + /** + * a receiver of verb-tokens, + * which renders them verbosely + */ + class VerboseRenderer + : public Receiver + { + string woof() { return "Woof-Woof!"; } + string honk() { return "Honk-Honk!"; } + string moo() { return "Moo-Moo!"; } + string meh() { return "Meh!"; } + }; + + + /** + * Statefull receiver of verb-tokens. + */ + class RecollectingReceiver + : public Receiver + { + string verb_; + _Fmt fmt_; + + string + buildResultTerm (string nextToken) + { + string resultExpression (fmt_ % verb_ % nextToken); + verb_ = nextToken; + return resultExpression; + } + + + string woof() { return buildResultTerm (VERB_woof); } + string honk() { return buildResultTerm (VERB_honk); } + string moo() { return buildResultTerm (VERB_moo); } + string meh() { return buildResultTerm (VERB_meh); } + + + public: + RecollectingReceiver() + : verb_(BEGINNING) + , fmt_("%s followed by %s") + { } + }; + + }//(End) Test fixture diff --git a/tests/library/verb-visitor-dispatch-test.cpp b/tests/library/verb-visitor-dispatch-test.cpp index 8b5057a75..bd50835f6 100644 --- a/tests/library/verb-visitor-dispatch-test.cpp +++ b/tests/library/verb-visitor-dispatch-test.cpp @@ -31,118 +31,107 @@ #include "lib/format-string.hpp" #include "lib/format-cout.hpp" #include "lib/format-util.hpp" +#include "lib/meta/tuple-helper.hpp" +#include "lib/iter-tree-explorer.hpp" #include #include +#include using std::string; using util::_Fmt; using util::join; using std::vector; +using std::forward; +using std::make_tuple; namespace lib { namespace test{ - ///////////////////////////TODO : Debugging - struct Trackr - { - size_t num; - - Trackr (size_t val) - : num(val) - { - cout <<"Trackr("<; + namespace { // Test Fixture + + /** the "visitor" interface used by all VerbPacks in this test */ + class Receiver + { + public: + virtual ~Receiver() { } ///< this is an interface + + virtual string woof (bool huge, uint cnt) =0; + virtual string honk (string) =0; + virtual string moo (size_t num) =0; + virtual string meh () =0; + }; + + + using Token = VerbPack; // the argument list for honk(string) requires the most inline storage using TokenSeq = vector; - } - - - /** - * a receiver of verb-tokens, - * which renders them verbosely - */ - class VerboseRenderer - : public Receiver - { - string - woof (bool huge, uint cnt) override - { - string woof{huge? "Woof..":"haw-haw"}; - while (0 < cnt--) - woof += woof; - return woof; - } - string - honk (string theHonk) override - { - return theHonk+"-"+theHonk+"!"; - } - string - moo (Trackr num) override - { - return join (vector{num.num, "Moo"}, "__"); - } - string - meh() override - { - return "Meh!"; - } - }; + + + /** + * a concrete receiver of verb-tokens, + * which renders them verbosely + */ + class VerboseRenderer + : public Receiver + { + string + woof (bool huge, uint cnt) override + { + string woof; + while (0 < cnt--) + woof += isnil(woof)? string {huge? "Woof..":"haw-haw"} + : woof; + return woof; + } + string + honk (string theHonk) override + { + return theHonk+"-"+theHonk+"!"; + } + string + moo (size_t num) override + { + return join (vector{num, "Moo"}, "__"); + } + string + meh() override + { + return "Meh?"; + } + }; + + /** + * another concrete receiver to report any invocation with arguments + */ + class DiagnosticRenderer + : public Receiver + { + string woof (bool huge, uint cnt) override { return report("woof", huge, cnt); } + string honk (string theHonk) override { return report("honk", theHonk); } + string moo (size_t num) override { return report("moo", num); } + string meh() override { return report("meh"); } + + template + string + report (Literal func, ARGS&&...args) + { + return string(func) + + meta::dump (make_tuple (forward(args)...)); + } + }; + + }//(End)Test Fixture -#define SHOW_TYPE(_TY_) \ - cout << "typeof( " << STRINGIFY(_TY_) << " )= " << lib::meta::typeStr<_TY_>() <= location); diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index b3859f34c..19de35eb6 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -19650,8 +19650,8 @@ - - + + @@ -19667,15 +19667,19 @@ - - - - + + + + - + + + + - - + + + @@ -19703,8 +19707,8 @@ - - + + @@ -19730,7 +19734,7 @@ - + @@ -19945,7 +19949,9 @@ - + + + @@ -20112,16 +20118,17 @@ - + + - - + + @@ -20156,22 +20163,26 @@ - - - + + + - - + + + + + + - - + + - + @@ -20189,15 +20200,70 @@ + + + + + + + +

+ anstatt, wie es die bisherige (naive) implementierung macht, +

+

+ die typename ARGS... auch dafür zu verwenden, den Handler-Typ zu konstruieren. +

+

+ Mit diesem einfachen Kniff passiert die Konvertierung dann in dem Moment, +

+

+ wo wir die konkreten Argumente in den vorbereiteten Argument-Tupel im Holder schieben +

+ + +
+
- - + + + + + + + + + + + + +

+ ...jeodch nicht eigens im Test dokumentiert +

+
    +
  • + weil das sehr aufwendig wäre und den kompletten Test dominieren würde +
  • +
  • + weil die Implementierung (in dieser Hinsicht) letztlich banal ist: Anwenden eines Tupel auf eine Funktion +
  • +
+ + +
+ +
+ + + + +
- - + + + @@ -20210,7 +20276,7 @@ - + @@ -20224,9 +20290,38 @@

+ +
+ + + + + + +

+ weil ich es geschafft habe, +

+

+ den gesamten Auswahl-Mechanismus in einen einzigen Konstruktur-Aufruf zu packen. +

+

+ Man kann also nach Belieben VerbPacks in allen Varianten bauen, +

+

+ und es obliegt der nächst höheren Schicht, dies auch in sinnvollem Rahmen zu tun... +

+ + +
+ + +
+ + +