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.
This commit is contained in:
parent
a10e381cfa
commit
710ae8fa0f
2 changed files with 125 additions and 22 deletions
|
|
@ -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 <i>by value</i> would open a lot of
|
||||
** the generated code). Consequently the ability to return a polymorphic object
|
||||
** from a factory or configuration function <i>by value</i> 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 <i>mix in</i> 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<CopyAPI&>(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 <boost/noncopyable.hpp>
|
||||
#include <boost/utility/enable_if.hpp>
|
||||
|
||||
|
||||
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 IFA
|
||||
,class BA = EmptyBase
|
||||
>
|
||||
class CopySupport
|
||||
: public BA
|
||||
{
|
||||
public:
|
||||
virtual ~CopySupport() { };
|
||||
virtual void cloneInto (void* targetBuffer) const =0;
|
||||
virtual void copyInto (IFA& targetBase) const =0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
template<typename T>
|
||||
class exposes_copySupportFunctions
|
||||
{
|
||||
|
||||
META_DETECT_FUNCTION(void, cloneInto, (void*) const);
|
||||
|
||||
public:
|
||||
enum{ value = HasFunSig_cloneInto<T>::value
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
template <class TY, class YES = void>
|
||||
struct Trait
|
||||
{
|
||||
typedef CopySupport<TY> CopyAPI;
|
||||
enum{ ADMIN_OVERHEAD = 2 * sizeof(void*) };
|
||||
|
||||
static CopyAPI&
|
||||
accessCopyHandlingInterface (TY& bufferContents)
|
||||
{
|
||||
REQUIRE (INSTANCEOF (CopyAPI, &bufferContents));
|
||||
return dynamic_cast<CopyAPI&> (bufferContents);
|
||||
}
|
||||
|
||||
typedef CopyAPI AdapterAttachment;
|
||||
};
|
||||
|
||||
|
||||
template <class TY>
|
||||
struct Trait<TY, typename enable_if< exposes_copySupportFunctions<TY> >::type>
|
||||
{
|
||||
typedef TY CopyAPI;
|
||||
enum{ ADMIN_OVERHEAD = 1 * sizeof(void*) };
|
||||
|
||||
template<class IFA>
|
||||
static CopyAPI&
|
||||
accessCopyHandlingInterface (IFA& bufferContents)
|
||||
{
|
||||
REQUIRE (INSTANCEOF (CopyAPI, &bufferContents));
|
||||
return static_cast<CopyAPI&> (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<CPY> _Traits;
|
||||
typedef typename _Traits::CopyAPI _CopyHandlingAdapter;
|
||||
enum{
|
||||
siz = storage + _Traits::ADMIN_OVERHEAD
|
||||
};
|
||||
|
||||
mutable char buf_[siz];
|
||||
|
||||
template<class IMP>
|
||||
|
|
@ -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 IMP>
|
||||
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<IFA>();
|
||||
REQUIRE (INSTANCEOF (HandlingAdapter, &bufferContents));
|
||||
HandlingAdapter& hap = dynamic_cast<HandlingAdapter&> (bufferContents);
|
||||
return hap;
|
||||
_CopyHandlingAdapter& hap = _Traits::accessCopyHandlingInterface (bufferContents);
|
||||
return hap; ////TODO cleanup unnecessary temporary
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
operator IFA& ()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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<Interface>::ADMIN_OVERHEAD + _ALIGN_);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue