From 710ae8fa0fa2f396ab8743b1b17aac5571138734 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 23 Apr 2011 20:19:29 +0200 Subject: [PATCH] Allow for improved performance in special cases Using a policy based switch in case the client interface collaborates and provides copy operations; in this case, a direct static downcast instead of the expensive indirect (dynamic) cast to the management interface can be employed. --- src/lib/polymorphic-value.hpp | 145 +++++++++++++++++++++++---- tests/lib/polymorphic-value-test.cpp | 2 +- 2 files changed, 125 insertions(+), 22 deletions(-) diff --git a/src/lib/polymorphic-value.hpp b/src/lib/polymorphic-value.hpp index bd343342f..70a8bd35a 100644 --- a/src/lib/polymorphic-value.hpp +++ b/src/lib/polymorphic-value.hpp @@ -23,7 +23,7 @@ /** @file polymorphic-value.hpp ** A mechanism to allow for opaque polymorphic value objects. ** This template helps to overcome a problem frequently encountered in - ** C++ programming, based on the fundamental design of C++, favouring + ** C++ programming, based on the fundamental design of C++, which favours ** explicit low-level control, copying of values and strict ctor-dtor pairs. ** Many object oriented design patterns build on polymorphism, where the ** actual type of an object isn't disclosed and collaborations rely on @@ -35,7 +35,7 @@ ** so 'objects' need just to be mentioned by reference. ** ** In C++ to employ many of the well known techniques, you're more or less - ** bound to explicitly put the objects somewhere in hash allocated memory + ** bound to explicitly put the objects somewhere in heap allocated memory ** and then pass an interface pointer or reference into the actual algorithm. ** Often, this hinders a design based on constant values and small descriptor ** objects used inline, thus forcing into unnecessarily complex and heavyweight @@ -46,19 +46,54 @@ ** as \c 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). Thus, the ability to return a polymorphic object from a - ** factory or configuration function by value would open a lot of + ** the generated code). Consequently the ability to return a polymorphic object + ** 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 ** ** 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. - ** This seemingly contradictory can be achieved, provided that + ** This seemingly contradictory goal can be achieved, provided that ** - the space occupied by the actual implementation object is limited, ** so it can be placed as binary data into an otherwise opaque holder buffer ** - and the actual implementation object assists with copying and cloning - ** itself, observing the actual implementation type and layout + ** itself, observing the actual implementation data layout + ** + ** The PolymorphicValue template implements this idea, by exposing an copyable + ** container with value semantics to the client code. On instantiation, a common + ** base interface for the actual value objects needs to be provided; the resulting + ** instance will be automatically convertible to this interface. Obviously this + ** common interface must be an ABC or at least contain some virtual functions. + ** 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 the embedded vtable, on access the + ** proper implementation functions will be invoked. + ** + ** Expanding on that pattern, the copying and cloning operations of the whole + ** container can be implemented by forwarding to appropriate virtual functions + ** on the embedded payload (implementation) object -- the concrete implementation + ** of these virtual functions can be assumed to know the real type and thus be + ** able to invoke the correct copy ctor or assignment operator. For this to + ** work, the interface needs to expose those copy and clone operations somehow + ** as virtual functions. There are two alternatives to get at this point: + ** - in the general case, the common base interface doesn't expose such operations. + ** Thus we need to mix in an additional \em management interface; this + ** can be done by \em subclassing the desired implementation type, because + ** this concrete type is irrelevant after finishing the placement constructor. + ** In order to re-access this management interface, so to be able to invoke + ** the copy or clone operations, we need to do an elaborate re-cast operation, + ** first going down to the leaf type and then back up into the mixed in + ** management interface. Basically this operation is performed by using + ** an \c dynamic_cast(bufferContents) + ** - but when the used client types provide some collaboration and implement + ** this management interface either directly on the API or as an immediate + ** sub-interface, then this copy/management interface is located within the + ** direct inheritance chain and can be reached by a simple \c static_cast. + ** Indeed, as we're just using a different meaning of the VTable, only a + ** single indirection (virtual function call) is required at runtime in + ** this case to invoke the copy ctor or assignment operator. ** ** @see polymorphic-value-test.cpp ** @see opaque-holder.hpp other similar opaque inline buffer templates @@ -71,11 +106,13 @@ #include "lib/error.hpp" +#include "lib/meta/duck-detector.hpp" //#include "lib/bool-checkable.hpp" //#include "lib/access-casted.hpp" //#include "lib/util.hpp" //#include +#include namespace lib { @@ -85,8 +122,74 @@ namespace lib { // using util::unConst; - namespace { // implementation helpers... + namespace polyvalue { // implementation helpers... + using boost::enable_if; + using lumiera::Yes_t; + using lumiera::No_t; + + struct EmptyBase{ }; + + template + class CopySupport + : public BA + { + public: + virtual ~CopySupport() { }; + virtual void cloneInto (void* targetBuffer) const =0; + virtual void copyInto (IFA& targetBase) const =0; + }; + + + + + template + class exposes_copySupportFunctions + { + + META_DETECT_FUNCTION(void, cloneInto, (void*) const); + + public: + enum{ value = HasFunSig_cloneInto::value + }; + }; + + + template + struct Trait + { + typedef CopySupport CopyAPI; + enum{ ADMIN_OVERHEAD = 2 * sizeof(void*) }; + + static CopyAPI& + accessCopyHandlingInterface (TY& bufferContents) + { + REQUIRE (INSTANCEOF (CopyAPI, &bufferContents)); + return dynamic_cast (bufferContents); + } + + typedef CopyAPI AdapterAttachment; + }; + + + template + struct Trait >::type> + { + typedef TY CopyAPI; + enum{ ADMIN_OVERHEAD = 1 * sizeof(void*) }; + + template + static CopyAPI& + accessCopyHandlingInterface (IFA& bufferContents) + { + REQUIRE (INSTANCEOF (CopyAPI, &bufferContents)); + return static_cast (bufferContents); + } + + typedef EmptyBase AdapterAttachment; + }; } @@ -112,10 +215,18 @@ namespace lib { */ template < class IFA ///< the nominal Base/Interface class for a family of types - , size_t siz ///< maximum storage required for the targets to be held inline + , size_t storage ///< maximum storage required for the target data to be held inline + , class CPY = IFA ///< special sub-interface to support copy operations > class PolymorphicValue { + + typedef polyvalue::Trait _Traits; + typedef typename _Traits::CopyAPI _CopyHandlingAdapter; + enum{ + siz = storage + _Traits::ADMIN_OVERHEAD + }; + mutable char buf_[siz]; template @@ -158,18 +269,10 @@ namespace lib { } - class HandlingAdapter - { - public: - virtual ~HandlingAdapter() { }; - virtual void cloneInto (void* targetBuffer) const =0; - virtual void copyInto (IFA& targetBase) const =0; - }; - template class Adapter : public IMP - , public HandlingAdapter + , public _Traits::AdapterAttachment { virtual void cloneInto (void* targetBuffer) const @@ -200,15 +303,15 @@ namespace lib { }; - HandlingAdapter& + _CopyHandlingAdapter& accessHandlingInterface () const { IFA& bufferContents = access(); - REQUIRE (INSTANCEOF (HandlingAdapter, &bufferContents)); - HandlingAdapter& hap = dynamic_cast (bufferContents); - return hap; + _CopyHandlingAdapter& hap = _Traits::accessCopyHandlingInterface (bufferContents); + return hap; ////TODO cleanup unnecessary temporary } + public: operator IFA& () { diff --git a/tests/lib/polymorphic-value-test.cpp b/tests/lib/polymorphic-value-test.cpp index 7674b677e..26570c8ff 100644 --- a/tests/lib/polymorphic-value-test.cpp +++ b/tests/lib/polymorphic-value-test.cpp @@ -216,7 +216,7 @@ namespace test{ CHECK (elm == myLocalVal); CHECK (!isSameObject (elm, myLocalVal)); - CHECK (sizeof(myLocalVal) <= MAX_SIZ + _ALIGN_); + CHECK (sizeof(myLocalVal) <= MAX_SIZ + polyvalue::Trait::ADMIN_OVERHEAD + _ALIGN_); }