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:
Fischlurch 2011-04-23 20:19:29 +02:00
parent a10e381cfa
commit 710ae8fa0f
2 changed files with 125 additions and 22 deletions

View file

@ -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& ()
{

View file

@ -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_);
}