Library: finish and clean-up the solution for VerbPack dispatch

This commit is contained in:
Fischlurch 2019-05-10 16:32:56 +02:00
parent 8f43c2591e
commit 3d5a67ed14
4 changed files with 2394 additions and 2376 deletions

View file

@ -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<CopyAPI&>`
* @todo this whole decision logic works but is confusingly written ///////////////////////TICKET #1197 : improve design of copy support
*/
template <class TY, class YES = void>
struct Trait
{
typedef CopySupport<TY,EmptyBase> 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<TY,EmptyBase>; ///////////////////////////TICKET #1197 : this is naive, we do not know if the target really has full copy support...
using Assignment = AssignmentPolicy<CopyAPI>;
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<CopyAPI&> (bufferContents);
}
typedef CopyAPI AdapterAttachment;
typedef AssignmentPolicy<CopyAPI> Assignment;
};
@ -325,8 +323,10 @@ namespace lib {
template <class TY>
struct Trait<TY, enable_if< exposes_CloneFunction<TY> >>
{
typedef TY CopyAPI;
enum{ ADMIN_OVERHEAD = 1 * sizeof(void*) };
using CopyAPI = TY;
using Assignment = AssignmentPolicy<CopyAPI>;
using AdapterAttachment = struct{ /* irrelevant */ };
enum{ ADMIN_OVERHEAD = 1 * sizeof(void*) }; // just the VTable of the payload
template<class IFA>
static CopyAPI&
@ -335,9 +335,6 @@ namespace lib {
REQUIRE (INSTANCEOF (CopyAPI, &bufferContents));
return static_cast<CopyAPI&> (bufferContents);
}
typedef EmptyBase AdapterAttachment;
typedef AssignmentPolicy<CopyAPI> Assignment;
};
}//(End)implementation details
@ -373,7 +370,7 @@ namespace lib {
>
class PolymorphicValue
{
public:
private:
typedef polyvalue::Trait<CPY> _Traits;
typedef typename _Traits::CopyAPI _CopyHandlingAdapter;
typedef typename _Traits::Assignment _AssignmentPolicy; /////////////////TICKET #1197 : confusingly indirect decision logic

View file

@ -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<class REC, class RET>
struct VerbInvoker
: polyvalue::CloneValueSupport<EmptyBasE> // mark and mix-in virtual copy construction support
: polyvalue::CloneValueSupport<polyvalue::EmptyBase> // mark and mix-in virtual copy construction support
{
virtual ~VerbInvoker() { }
@ -84,10 +121,10 @@ namespace lib {
};
template<class REC, class SIG>
struct Holder;
struct VerbHolder;
template<class REC, class RET, typename... ARGS>
struct Holder<REC, RET(ARGS...)>
struct VerbHolder<REC, RET(ARGS...)>
: VerbInvoker<REC,RET>
, VerbToken<REC,RET(ARGS...)>
{
@ -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> (args)...}
{ }
@ -128,7 +165,7 @@ namespace lib {
using PolyHolder = PolymorphicValue<VerbInvoker<REC,RET>, storageOverhead(arg_storage)>;
template<typename...ARGS>
using PayloadType = Holder<REC, RET(ARGS...)>*;
using PayloadType = VerbHolder<REC, RET(ARGS...)>*;
template<typename...ARGS>
using Handler = typename VerbToken<REC, RET(ARGS...)>::Handler;

View file

@ -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<IrrelvantType, void(void)>);
using HoldL = Holder<Receiver, string(string)>;
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<string>);
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<HoldL>;
SHOW_TYPE (AdapT);
SHOW_EXPR (sizeof(AdapT));
}

File diff suppressed because it is too large Load diff