From 6fa11ffcf6b0a7fcb95c1d052e5ee215ea81b350 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 25 Apr 2011 04:49:05 +0200 Subject: [PATCH] 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 --- src/lib/meta/duck-detector.hpp | 20 ++++++- src/lib/polymorphic-value.hpp | 83 ++++++++++++++++++++++++++-- tests/lib/polymorphic-value-test.cpp | 25 +++++++++ 3 files changed, 121 insertions(+), 7 deletions(-) diff --git a/src/lib/meta/duck-detector.hpp b/src/lib/meta/duck-detector.hpp index 0091a894f..84363616e 100644 --- a/src/lib/meta/duck-detector.hpp +++ b/src/lib/meta/duck-detector.hpp @@ -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 diff --git a/src/lib/polymorphic-value.hpp b/src/lib/polymorphic-value.hpp index 94a4b4b09..0f13a9b98 100644 --- a/src/lib/polymorphic-value.hpp +++ b/src/lib/polymorphic-value.hpp @@ -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 ///< 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 - 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 + class allow_Clone_but_no_Copy + { + META_DETECT_MEMBER(copyInto); + + public: + enum{ value = exposes_CloneFunction::value + && ! HasMember_copyInto::value + }; + }; - /** + /** + * Policy class for invoking the assignment operator. + * By default the embedded payload objects are assumed + * to be freely assignable + */ + template + struct AssignmentPolicy + { + template + 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 + struct AssignmentPolicy >::type> + { + template + 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 Assignment; }; @@ -237,7 +304,7 @@ namespace lib { * simple \c static_cast without runtime overhead. */ template - struct Trait >::type> + struct Trait >::type> { typedef TY CopyAPI; enum{ ADMIN_OVERHEAD = 1 * sizeof(void*) }; @@ -251,6 +318,7 @@ namespace lib { } typedef EmptyBase AdapterAttachment; + typedef AssignmentPolicy 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 _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 (targetBase); - target = (*this); // forward to assignment operator - } + _AssignmentPolicy::assignEmbedded(target,*this); + } // forward to assignment operator public: /* == forwarding ctor to implementation type == */ diff --git a/tests/lib/polymorphic-value-test.cpp b/tests/lib/polymorphic-value-test.cpp index bdc366f9c..8ee1ef356 100644 --- a/tests/lib/polymorphic-value-test.cpp +++ b/tests/lib/polymorphic-value-test.cpp @@ -184,6 +184,7 @@ namespace test{ CHECK (0 == _checkSum); // all dead verifyOverrunProtection(); + verifyCopySupportDetectionMetafunctions(); } @@ -277,6 +278,30 @@ namespace test{ VERIFY_ERROR (ASSERTION, PolyVal::build() ); #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 CopySupportAPI; + typedef polyvalue::CloneValueSupport CloneOnlyAPI; + + CHECK ( !polyvalue::exposes_CloneFunction::value ); + CHECK ( polyvalue::exposes_CloneFunction::value ); + CHECK ( polyvalue::exposes_CloneFunction::value ); + + CHECK ( polyvalue::allow_Clone_but_no_Copy::value ); + CHECK ( !polyvalue::allow_Clone_but_no_Copy::value ); + } };