From 3d5a67ed14ebd8926ca65e401071e6f5f7327410 Mon Sep 17 00:00:00 2001
From: Ichthyostega
Date: Fri, 10 May 2019 16:32:56 +0200
Subject: [PATCH] Library: finish and clean-up the solution for VerbPack
dispatch
---
src/lib/polymorphic-value.hpp | 33 +-
src/lib/verb-visitor.hpp | 61 +-
tests/library/verb-visitor-dispatch-test.cpp | 16 -
wiki/thinkPad.ichthyo.mm | 4660 +++++++++---------
4 files changed, 2394 insertions(+), 2376 deletions(-)
diff --git a/src/lib/polymorphic-value.hpp b/src/lib/polymorphic-value.hpp
index 2bbfc609a..6471d4e22 100644
--- a/src/lib/polymorphic-value.hpp
+++ b/src/lib/polymorphic-value.hpp
@@ -43,7 +43,7 @@
** of the programming language, we may try to pull some (template based) trickery
** to make polymorphic objects fit better with the handling of small copyable
** value objects. Especially, C++ gives a special meaning to passing parameters
- ** as \c const& -- typically constructing an anonymous temporary object conveniently
+ ** as `const&` -- typically constructing an anonymous temporary object conveniently
** just for passing an abstraction barrier (while the optimiser can be expected to
** remove this barrier and the accompanying nominal copy operations altogether in
** the generated code). Consequently the ability to return a polymorphic object
@@ -68,7 +68,7 @@
** Moreover, the PolymorphicValue container provides static builder functions,
** allowing to place a concrete instance of a subclass into the content buffer.
** After construction, the actual type of this instance will be forgotten
- ** (``type erasure''), but because of the embedded vtable, on access, the
+ ** ("type erasure"), but because of the embedded vtable, on access, the
** proper implementation functions will be invoked.
**
** Expanding on that pattern, the copying and cloning operations of the whole
@@ -289,19 +289,20 @@ namespace lib {
/**
- * traits template to deal with
- * different ways to support copy operations.
+ * trait template to deal with different ways to support copy operations.
* Default is no support by the API and implementation types.
- * In this case, the CopySupport interface is mixed in at the
- * level of the concrete implementation class and later on
- * accessed through a \c dynamic_cast
+ * In this case, the CopySupport interface is mixed-in at the
+ * level of the concrete implementation class and will be
+ * accessed later on through a `dynamic_cast`
* @todo this whole decision logic works but is confusingly written ///////////////////////TICKET #1197 : improve design of copy support
*/
template
struct Trait
{
- typedef CopySupport CopyAPI; ///////////////////////////TICKET #1197 : this is naive, we do not know if the target really has full copy support...
- enum{ ADMIN_OVERHEAD = 2 * sizeof(void*) };
+ using CopyAPI = CopySupport; ///////////////////////////TICKET #1197 : this is naive, we do not know if the target really has full copy support...
+ using Assignment = AssignmentPolicy;
+ using AdapterAttachment = CopyAPI;
+ enum{ ADMIN_OVERHEAD = 2 * sizeof(void*) }; // need second VTable for CopyAPI mix-in
static CopyAPI&
accessCopyHandlingInterface (TY& bufferContents)
@@ -309,9 +310,6 @@ namespace lib {
REQUIRE (INSTANCEOF (CopyAPI, &bufferContents));
return dynamic_cast (bufferContents);
}
-
- typedef CopyAPI AdapterAttachment;
- typedef AssignmentPolicy Assignment;
};
@@ -325,8 +323,10 @@ namespace lib {
template
struct Trait >>
{
- typedef TY CopyAPI;
- enum{ ADMIN_OVERHEAD = 1 * sizeof(void*) };
+ using CopyAPI = TY;
+ using Assignment = AssignmentPolicy;
+ using AdapterAttachment = struct{ /* irrelevant */ };
+ enum{ ADMIN_OVERHEAD = 1 * sizeof(void*) }; // just the VTable of the payload
template
static CopyAPI&
@@ -335,9 +335,6 @@ namespace lib {
REQUIRE (INSTANCEOF (CopyAPI, &bufferContents));
return static_cast (bufferContents);
}
-
- typedef EmptyBase AdapterAttachment;
- typedef AssignmentPolicy Assignment;
};
}//(End)implementation details
@@ -373,7 +370,7 @@ namespace lib {
>
class PolymorphicValue
{
- public:
+ private:
typedef polyvalue::Trait _Traits;
typedef typename _Traits::CopyAPI _CopyHandlingAdapter;
typedef typename _Traits::Assignment _AssignmentPolicy; /////////////////TICKET #1197 : confusingly indirect decision logic
diff --git a/src/lib/verb-visitor.hpp b/src/lib/verb-visitor.hpp
index 181811f5c..d880bffbb 100644
--- a/src/lib/verb-visitor.hpp
+++ b/src/lib/verb-visitor.hpp
@@ -24,12 +24,13 @@
/** @file verb-visitor.hpp
** A specific double dispatch variation for function invocation.
** While the classic visitor invokes a common `handle` function with varying arguments,
- ** here we allow for pre-binding of arbitrary functions on an interface with individual
- ** suitable arguments. Yet similar to the classic visitor, the actual receiver can be a
- ** subclass of the target interface, which causes the _second_ indirection in the dispatch
- ** chain. Since the actually distinguishing factor is not so much a type, but a specific
- ** operation, we refer to the delayed invocation handles created by this binding as
- ** _verb token_ on a _receiver_ object (which is the concrete visitor).
+ ** here we allow for pre-binding of arbitrary _handler functions_ on an interface with
+ ** together with individual, suitable arguments. Yet similar to the classic visitor, the
+ ** _actual receiver_ can be a subclass of the visitor target interface, which causes the
+ ** _second_ indirection in the dispatch chain, thus completing a full double-dispatch.
+ ** Since the actually distinguishing factor is not so much a type, but a specific operation,
+ ** we refer to the delayed invocation handles created by this binding as _verb token_
+ ** on a _receiver_ object (which is the concrete visitor).
**
** This setup is an extension or derivative of the [generic verb token](\ref verb-token-hpp)
** used for the diff system and similar applications; likewise the intended usage is to establish
@@ -38,6 +39,43 @@
** use case is for the drawing of track contents in the user interface, where this pattern allows
** the separation of actual drawing code from the nested track controller structure.
**
+ **
+ ** ## implementation technique
+ **
+ ** The actual foundation is quite simple: we store a [member pointer]. Typically, this embedded
+ ** pointer-to-member shall be bound to an abstract virtual function on the _visitor interface._
+ ** So basically the "verb" boils down to storing an offset into the VTable on the interface.
+ ** Later, on invocation, a reference to the actual _receiver_ is passed in, typically a concrete
+ ** subclass of the visitor interface. The invocation then combines this receiver reference with
+ ** the offset (the member pointer) to invoke the desired virtual function.
+ **
+ ** However, the complications and technicalities arise from the ability to bind arbitrary
+ ** function signatures, even together with the actual arguments to use at invocation. Those
+ ** function arguments are supplied when creating the "packaged verb", and thus need to be stored
+ ** within this package, together with the member-pointer. The result is a _materialised_ and
+ ** _delayed_ invocation of an abstract (interface) function, while the actual concrete function
+ ** implementation shall be supplied later. Obviously, such a ["verb pack"](\ref VerbPack) has
+ ** _value semantics_ -- we want to store it, copy it and pass it along, often even within a
+ ** sequence of "verbs". And even more: we do not want to pass "hidden references" and we
+ ** do not want to rely on some management service and heap allocations. Rather, each
+ ** VerbPack shall be a self-contained value object. Within certain limitations,
+ ** this is possible in C++ by using an opaque buffer embedded within the
+ ** outer value object; basically the pre-defined buffer size must be
+ ** sufficient to hold all possible argument tuples to bind.
+ **
+ ** The actual implementation here relies on two other components from the Lumiera library:
+ ** - the lib::VerbToken provides us with the dispatch through a stored member pointer
+ ** - the lib::PolymorphicValue allows to embed a subclass within an opaque inline buffer,
+ ** just exposing the common interface.
+ ** Yet another challenge is the necessity to unpack the argument values from the storage
+ ** tuple and pass them to an (at this point abstracted) function with arbitrary signature.
+ ** Here we rely on the common implementation technique from [std::apply], here with the
+ ** special twist that we don't use a pre-bound function, but rather need to combine the
+ ** actual invocation target at the moment of the invocation.
+ **
+ ** [member pointer]: https://en.cppreference.com/w/cpp/language/pointer
+ ** [std::apply]: https://en.cppreference.com/w/cpp/utility/apply
+ **
** @see [drawing on the track canvas](\ref body-canvas-widget.cpp)
** @see VerbVisitorDispatch_test
**
@@ -72,11 +110,10 @@ namespace lib {
}
}
- using EmptyBasE = struct { };
template
struct VerbInvoker
- : polyvalue::CloneValueSupport // mark and mix-in virtual copy construction support
+ : polyvalue::CloneValueSupport // mark and mix-in virtual copy construction support
{
virtual ~VerbInvoker() { }
@@ -84,10 +121,10 @@ namespace lib {
};
template
- struct Holder;
+ struct VerbHolder;
template
- struct Holder
+ struct VerbHolder
: VerbInvoker
, VerbToken
{
@@ -99,7 +136,7 @@ namespace lib {
Args args_;
- Holder (typename Verb::Handler handlerRef, Literal verbID, ARGS&&... args)
+ VerbHolder (typename Verb::Handler handlerRef, Literal verbID, ARGS&&... args)
: Verb{handlerRef, verbID}
, args_{std::forward (args)...}
{ }
@@ -128,7 +165,7 @@ namespace lib {
using PolyHolder = PolymorphicValue, storageOverhead(arg_storage)>;
template
- using PayloadType = Holder*;
+ using PayloadType = VerbHolder*;
template
using Handler = typename VerbToken::Handler;
diff --git a/tests/library/verb-visitor-dispatch-test.cpp b/tests/library/verb-visitor-dispatch-test.cpp
index 29deb3001..8b5057a75 100644
--- a/tests/library/verb-visitor-dispatch-test.cpp
+++ b/tests/library/verb-visitor-dispatch-test.cpp
@@ -155,22 +155,6 @@ namespace test{
TokenSeq tokens = build_and_copy_tokens();
render_verbose (tokens);
// profile.append_woof(1, 2);
-
- using IrrelvantType = struct{};
- const size_t VERB_TOK_SIZE = sizeof(lib::VerbToken);
- using HoldL = Holder;
- const size_t VERB_TOK_SIZ2 = sizeof(HoldL::Verb);
- const size_t HOLDER_SIZE = sizeof(HoldL);
- const size_t ARG_TUP_SIZE = sizeof(std::tuple);
-
- SHOW_EXPR (VERB_TOK_SIZE);
- SHOW_EXPR (VERB_TOK_SIZ2);
- SHOW_EXPR (HOLDER_SIZE);
- SHOW_EXPR (ARG_TUP_SIZE);
- SHOW_EXPR (sizeof(string));
- using AdapT = Token::Adapter;
- SHOW_TYPE (AdapT);
- SHOW_EXPR (sizeof(AdapT));
}
diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm
index 66d3740be..b3859f34c 100644
--- a/wiki/thinkPad.ichthyo.mm
+++ b/wiki/thinkPad.ichthyo.mm
@@ -1,69 +1,69 @@
-
+
@@ -5572,9 +5572,9 @@
-
-
-
+
+
+
@@ -5643,82 +5643,82 @@
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
+
+
+
+
-
+
-
+
-
+
-
-
+
+
-
+
-
+
-
-
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -5736,21 +5736,21 @@
-
+
-
+
-
+
-
+
-
+
-
+
@@ -5764,16 +5764,16 @@
-
+
-
-
+
+
-
+
-
-
+
+
@@ -5812,7 +5812,7 @@
-
+
@@ -5820,69 +5820,69 @@
-
+
-
+
-
+
-
+
-
+
-
-
-
+
+
+
-
+
-
+
-
+
-
+
-
-
-
-
+
+
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -5898,7 +5898,7 @@
-
+
@@ -5918,15 +5918,15 @@
-
+
-
+
-
+
@@ -5941,32 +5941,32 @@
-
-
-
-
-
+
+
+
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
-
-
+
+
+
@@ -6003,49 +6003,49 @@
-
+
-
+
-
+
-
-
-
-
+
+
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
-
-
+
+
+
@@ -6076,41 +6076,41 @@
-
+
-
-
+
+
-
-
-
-
-
+
+
+
+
+
-
-
+
+
-
-
-
+
+
+
-
+
-
+
-
+
@@ -6627,7 +6627,7 @@
-
+
@@ -6718,7 +6718,7 @@
-
+
@@ -16532,161 +16532,161 @@
-
-
+
+
-
+
-
-
+
+
-
-
+
+
-
+
-
+
-
-
-
-
+
+
+
+
-
+
-
-
+
+
-
-
+
+
-
+
-
-
-
+
+
+
-
-
+
+
-
-
+
+
-
+
-
-
+
+
-
+
-
+
-
-
-
-
+
+
+
+
-
-
-
-
+
+
+
+
-
+
-
+
-
+
-
+
-
+
-
-
-
-
+
+
+
+
-
+
-
-
-
+
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
-
+
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -16705,10 +16705,10 @@
-
+
-
+
@@ -16778,13 +16778,13 @@
-
-
-
-
+
+
+
+
-
+
@@ -17003,50 +17003,50 @@
-
-
-
-
-
+
+
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
+
+
+
-
+
-
-
+
+
-
-
-
+
+
+
-
-
+
+
-
-
-
+
+
+
@@ -17058,41 +17058,41 @@
-
-
+
+
-
+
-
+
-
-
-
-
+
+
+
+
-
+
-
-
+
+
-
+
-
-
+
+
@@ -17105,8 +17105,8 @@
-
-
+
+
@@ -17119,42 +17119,42 @@
-
+
-
-
+
+
-
+
-
+
-
-
-
+
+
+
-
+
-
+
-
+
-
-
+
+
-
+
-
+
@@ -17424,61 +17424,61 @@
-
+
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
+
-
-
-
+
+
+
-
-
+
+
-
-
-
-
+
+
+
+
-
+
-
+
-
-
+
+
-
+
-
+
-
+
-
-
-
+
+
+
@@ -17490,7 +17490,7 @@
-
+
@@ -17505,7 +17505,7 @@
-
+
@@ -17520,36 +17520,36 @@
-
+
-
+
-
-
+
+
-
+
-
+
-
+
-
+
-
-
-
+
+
+
-
-
-
+
+
+
@@ -17572,51 +17572,51 @@
-
+
-
+
-
-
-
+
+
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
@@ -17628,55 +17628,55 @@
-
-
-
+
+
+
-
-
-
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
+
-
+
-
-
-
-
-
-
+
+
+
+
+
+
-
+
-
-
-
+
+
+
-
-
-
-
+
+
+
+
-
-
+
+
@@ -17688,7 +17688,7 @@
-
+
@@ -17703,7 +17703,7 @@
-
+
@@ -17741,61 +17741,61 @@
-
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
+
+
-
-
-
+
+
+
-
-
+
+
-
-
+
+
-
+
-
+
-
-
+
+
-
+
-
-
-
-
+
+
+
+
-
-
+
+
-
+
-
-
-
-
-
+
+
+
+
+
@@ -17809,14 +17809,14 @@
-
-
+
+