special case: support cloning, but not copy

needed to add yet another policy template,
so PolymorphicValue doesn't invoke the
assignment operator in such cases
This commit is contained in:
Fischlurch 2011-04-25 04:49:05 +02:00
parent 6daf14211b
commit 6fa11ffcf6
3 changed files with 121 additions and 7 deletions

View file

@ -60,9 +60,25 @@
** specific function signature and then try to assign the named member. This allows even
** to determine if a member function of a type in question has the desired signature.
**
** All these detection building blocks are written such as to provide a bool member \v ::value,
** All these detection building blocks are written such as to provide a bool member \c ::value,
** which is in accordance to the conventions of boost metaprogramming. I.e. you can immediately
** use them within boost::enable_if
** use them within \c boost::enable_if
**
** \par some pitfalls to consider
**
** @warning The generated metafunctions all yield the \c false value by default.
** Effectively this means that an error in the test expression might go unnoticed;
** you'd be better off explicitly checking the detection result by an unit test.
**
** There are several typical problems to care about
** - a member can be both a variable or a function of that name
** - function signatures need to match precisely, including const modifiers
** - the generated metafunction (template) uses a type parameter 'TY', which could
** shadow or conflict with an type parameter in the enclosing scope
** - the member and function checks rely on member pointers, which generally rely on
** the explicit static type. These checks don't see any inherited members / functions.
** - obviously, all those checks are never able to detect anything depending on runtime
** types or RTTI
**
** @see util-foreach.hpp usage example
** @see duck-detector-test.cpp

View file

@ -153,6 +153,7 @@ namespace lib {
namespace polyvalue { // implementation details...
namespace error = lumiera::error;
using boost::enable_if;
using lumiera::Yes_t;
using lumiera::No_t;
@ -186,6 +187,21 @@ namespace lib {
virtual void copyInto (IFA& targetBase) const =0;
};
/**
* A variation for limited copy support.
* Sometimes, only cloning (copy construction) of value objects is allowed,
* but not assignment of new values to existing objects. In this case, the
* concrete values can inherit from this variant of the support API.
*/
template<class BA> ///< direct baseclass to use for this clone support API
class CloneValueSupport
: public BA
{
public:
virtual ~CloneValueSupport() { };
virtual void cloneInto (void* targetBuffer) const =0;
};
/**
@ -193,7 +209,7 @@ namespace lib {
* to support clone operations
*/
template<typename T>
class exposes_copySupportFunctions
class exposes_CloneFunction
{
META_DETECT_FUNCTION(void, cloneInto, (void*) const);
@ -203,8 +219,58 @@ namespace lib {
};
};
/**
* helper to detect if the API supports only
* copy construction, but no assignment
*/
template<typename T>
class allow_Clone_but_no_Copy
{
META_DETECT_MEMBER(copyInto);
public:
enum{ value = exposes_CloneFunction<T>::value
&& ! HasMember_copyInto<T>::value
};
};
/**
/**
* Policy class for invoking the assignment operator.
* By default the embedded payload objects are assumed
* to be freely assignable
*/
template<class API, class YES =void>
struct AssignmentPolicy
{
template<class IMP>
static void
assignEmbedded(IMP& dest,IMP const& src)
{
dest = src;
}
};
/**
* special case when the embedded payload objects permit
* copy construction, but no assignment to existing instances.
* Instead of invoking the assignment operator (which typically
* isn't defined at all in such cases), a misguided assignment
* to the container will raise an exception
*/
template<class API>
struct AssignmentPolicy<API, typename enable_if< allow_Clone_but_no_Copy<API> >::type>
{
template<class IMP>
static void
assignEmbedded(IMP&,IMP const&)
{
throw error::Logic("attempt to overwrite unmodifiable value");
}
};
/**
* traits template to deal with
* different ways to support copy operations.
* Default is no support by the API and implementation types.
@ -226,6 +292,7 @@ namespace lib {
}
typedef CopyAPI AdapterAttachment;
typedef AssignmentPolicy<CopyAPI> Assignment;
};
@ -237,7 +304,7 @@ namespace lib {
* simple \c static_cast without runtime overhead.
*/
template <class TY>
struct Trait<TY, typename enable_if< exposes_copySupportFunctions<TY> >::type>
struct Trait<TY, typename enable_if< exposes_CloneFunction<TY> >::type>
{
typedef TY CopyAPI;
enum{ ADMIN_OVERHEAD = 1 * sizeof(void*) };
@ -251,6 +318,7 @@ namespace lib {
}
typedef EmptyBase AdapterAttachment;
typedef AssignmentPolicy<CopyAPI> Assignment;
};
}//(End)implementation details
@ -259,6 +327,10 @@ namespace lib {
/**
* Template to build polymorphic value objects.
* Inline buffer with value semantics, yet holding and owning an object
@ -285,6 +357,7 @@ namespace lib {
typedef polyvalue::Trait<CPY> _Traits;
typedef typename _Traits::CopyAPI _CopyHandlingAdapter;
typedef typename _Traits::Assignment _AssignmentPolicy;
enum{
siz = storage + _Traits::ADMIN_OVERHEAD
};
@ -363,8 +436,8 @@ namespace lib {
{
REQUIRE (INSTANCEOF (Adapter, &targetBase));
Adapter& target = static_cast<Adapter&> (targetBase);
target = (*this); // forward to assignment operator
}
_AssignmentPolicy::assignEmbedded(target,*this);
} // forward to assignment operator
public: /* == forwarding ctor to implementation type == */

View file

@ -184,6 +184,7 @@ namespace test{
CHECK (0 == _checkSum); // all dead
verifyOverrunProtection();
verifyCopySupportDetectionMetafunctions();
}
@ -277,6 +278,30 @@ namespace test{
VERIFY_ERROR (ASSERTION, PolyVal::build<OversizedImp>() );
#endif ///////////////////////////////////////////////////////////////////////////////////////////////TICKET #537 : restore throwing ASSERT
}
/** @Test internally, PolymorphicValue uses some metafunctions
* to pick a suitable code path, based on the presence of helper functions
* on the API of the embedded objects. Default is no support by these objects,
* which then requires to use a more expensive implementation. Sometimes it's
* desirable to support \em cloning only (copy ctor), but no assignment after
* the fact. In this special case, a support API with only a \cloneInto member
* can be implemented, causing the PolymorphicValue container to raise an
* exception in case the copy operator is invoked.
*/
void
verifyCopySupportDetectionMetafunctions()
{
typedef polyvalue::CopySupport<Interface> CopySupportAPI;
typedef polyvalue::CloneValueSupport<Interface> CloneOnlyAPI;
CHECK ( !polyvalue::exposes_CloneFunction<Interface>::value );
CHECK ( polyvalue::exposes_CloneFunction<CopySupportAPI>::value );
CHECK ( polyvalue::exposes_CloneFunction<CloneOnlyAPI>::value );
CHECK ( polyvalue::allow_Clone_but_no_Copy<CloneOnlyAPI>::value );
CHECK ( !polyvalue::allow_Clone_but_no_Copy<CopySupportAPI>::value );
}
};