From e28635a11a3129efb768febaf4246a0d9d43d1bf Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 20 Apr 2019 22:23:55 +0200 Subject: [PATCH] Library: investigate copy behaviour in forwarding calls --- src/lib/polymorphic-value.hpp | 19 +- src/lib/verb-visitor.hpp | 4 +- tests/library/verb-visitor-dispatch-test.cpp | 41 ++- wiki/thinkPad.ichthyo.mm | 279 +++++++++++++++++++ 4 files changed, 333 insertions(+), 10 deletions(-) diff --git a/src/lib/polymorphic-value.hpp b/src/lib/polymorphic-value.hpp index dd4254c40..1d96edac6 100644 --- a/src/lib/polymorphic-value.hpp +++ b/src/lib/polymorphic-value.hpp @@ -50,7 +50,7 @@ ** from a factory or configuration function by value would open a lot of ** straight forward design possibilities and concise formulations. ** - ** \par how to build a copyable value without knowing it's layout in detail + ** # how to build a copyable value without knowing it's layout in detail ** ** So the goal is to build a copyable and assignable type with value semantics, ** without disclosing the actual implementation and object layout at the usage site. @@ -107,6 +107,16 @@ ** the "implementation type" specified by the client. Thus, within the ** context of the copy operation, we know all the concrete types. ** + ** @todo the actual implementation for copy support basically achieves this goal, + ** but it is somewhat confusing and muddled, and not entirely correct in some + ** corner cases (esp. when the target type does _not collaborate_ but also + ** does _only support copy construction_, but no assignment. + ** In fact, part of the solution implemented here is known as "virtual copy + ** support"; meanwhile we use a generic version of that pattern in our + ** [Variant container](\ref variant.hpp). Thus, at some point, we should + ** rework this aspect of the solution to make it more orthogonal, clearer + ** to understand and more correct. /////////////////////////////////////////////TICKET #1197 + ** ** ** # using polymorphic value objects ** @@ -209,7 +219,7 @@ namespace lib { virtual ~CloneValueSupport() { }; virtual void cloneInto (void* targetBuffer) const =0; }; - + ///////////////////////////////////////////////TICKET #1197 : this should be a full "virtual copy support" to cover all possible cases /** @@ -285,11 +295,12 @@ namespace lib { * 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 + * @todo this whole decision logic works but is confusingly written ///////////////////////TICKET #1197 : improve design of copy support */ template struct Trait { - typedef CopySupport CopyAPI; + 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*) }; static CopyAPI& @@ -365,7 +376,7 @@ namespace lib { typedef polyvalue::Trait _Traits; typedef typename _Traits::CopyAPI _CopyHandlingAdapter; - typedef typename _Traits::Assignment _AssignmentPolicy; + typedef typename _Traits::Assignment _AssignmentPolicy; /////////////////TICKET #1197 : confusingly indirect decision logic enum{ siz = storage + _Traits::ADMIN_OVERHEAD }; diff --git a/src/lib/verb-visitor.hpp b/src/lib/verb-visitor.hpp index dee711f6f..eeeb35a74 100644 --- a/src/lib/verb-visitor.hpp +++ b/src/lib/verb-visitor.hpp @@ -74,6 +74,7 @@ namespace lib { template struct VerbInvoker + : polyvalue::CloneValueSupport // mark and mix-in virtual copy construction support { virtual ~VerbInvoker() { } @@ -85,8 +86,7 @@ namespace lib { template struct Holder - : polyvalue::CopySupport< // mix-in virtual copy/move support - VerbInvoker> // ...the common interface to use + : VerbInvoker { using Verb = VerbToken; using Args = std::tuple; diff --git a/tests/library/verb-visitor-dispatch-test.cpp b/tests/library/verb-visitor-dispatch-test.cpp index 2c8dfca1d..c1fe99028 100644 --- a/tests/library/verb-visitor-dispatch-test.cpp +++ b/tests/library/verb-visitor-dispatch-test.cpp @@ -43,6 +43,39 @@ using std::vector; namespace lib { namespace test{ + ///////////////////////////TODO : Debugging + struct Trackr + { + size_t num; + + Trackr (size_t val) + : num(val) + { + cout <<"Trackr("<{num, "Moo"}, "__"); + return join (vector{num.num, "Moo"}, "__"); } string meh() override @@ -145,7 +178,7 @@ namespace test{ Token littleWoof(&Receiver::woof, "woof", false, 3u); Token quack(&Receiver::honk, "honk", string{"quaack"}); Token honk(&Receiver::honk, "honk", string{"Hoonk"}); - Token moo(&Receiver::moo, "moo", size_t(3)); + Token moo(&Receiver::moo, "moo", Trackr(3)); Token meh(&Receiver::meh, "meh"); diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 22e297752..ef157a971 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -19681,6 +19681,285 @@ + + + + + + + + +

+ nämlich mit minimalem Admin-Overhead, +

+

+ indem er sich auf die VTable des einzubettenden Typs abstützt. +

+

+ +

+

+ Die andere Alternative, der OpaqueHolder, verwendet selber nochmal einen zustäzlichen virtuellen Holder, +

+

+ und Variant paßt auch nicht, da ich ja mit Template-generierten Subklassen arbeite, +

+

+ und daher nicht a priori die Liste aller einzubettenden Varianten kenne +

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

+ nämlich hier der Visitor, der den Aufruf letztlich emfpängt +

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

+ ...denn es handelt sich hierbei um einen Konstruktionstrick. +

+

+ Scott Meyers spricht deshalb auch immer von "unverersal references" und unterscheidet +

+

+ diese von einer expliziten RValue-Referenz. +

+

+ +

+

+ Demnach wäre das Standard-Baumuster, daß alle Glieder in  der perfect-forwarding-Kette +

+

+ per universal-Reference miteinander verbunden sind. Und im Konkreten fall muß man +

+

+ das so hintricksen, indem man die std::get-Funktion passend bestückt... +

+

+ +

+

+ Au weia +

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

+ Beispiel: Wenn der Typ selber keinen Support anbietet, +

+

+ dann nehmen wir immer das volle CopySupport-API, und differenzieren nicht +

+

+ mehr nach Typen mit reinem clone-support. Was dann tatsächlich dazu führt, +

+

+ daß der Compiler verucht, den Zuweisungsoperator zu verwernden! +

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

+ nämlich diejenige für Typen ohne Support auf dem API. +

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

+ ...etwas analog zu meinem TreeMutator. +

+

+ D.h. man muß alle verwendeten Signaturen auf dem Receiver +

+

+ erst mal in einem Builder-Konstrukt gewissermaßen "registrieren", um dann den passenden +

+

+ VerbPack-Typ konstruiert zu bekommen. +

+ + +
+
+ + + + + + +

+ d.h. man erzeugt in einem einzigen Aufruf den VerbPack für eine Zielfunktion +

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

+ zwei verschachtelte, delegierende Konstruktoren, +

+

+ von denen der zweite, innere die eigentliche Typinferenz macht +

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

+ sollten z.B. die Konstruktor-Funktionen nicht unmittelbar mit definiert werden? +

+ + +
+
+ + + + + + +

+ ...weil man stets noch einen Layer darübersetzt? +

+

+ Wie auch im konkreten Fall das TrackProfile, was dann ein vector<VerbPack> werden würde? +

+ + +
+
+