From 40a9df666ff00a09778fd0ebe20cdffb78784513 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sat, 18 Apr 2015 02:49:09 +0200 Subject: [PATCH] WIP: draft towards a solution of the copy policy problem turns out to be quite a tough challenge.... since obviously we want to support usage of types with partially disabled copy/assignment operations within Variant. As long as the corresponding operations on the container aren't invoked, we expect those types to be usable just fine. The problem arises at the interaction with type erasure; to support corret copy / assignement in such a situation, we need virtual copy / assignment operators. And, since these are to be installed into a VTable, the templated functions will be instantiated allways, which might cause invocation of inhibited copy / assignement functions and thus compilation failure, in spite of never actually invoking such an illegal operation. The drafted solution is to mix in a specifically configured copy support policy, which at least raises a runtime error, instead of invoking the incriminating operation(s) --- src/lib/variant.hpp | 250 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 229 insertions(+), 21 deletions(-) diff --git a/src/lib/variant.hpp b/src/lib/variant.hpp index 72e732e08..949cb508f 100644 --- a/src/lib/variant.hpp +++ b/src/lib/variant.hpp @@ -77,10 +77,16 @@ namespace lib { namespace error = lumiera::error; + template + class Variant; + namespace { // implementation helpers using std::is_constructible; using std::remove_reference; + using std::remove_const; + using std::is_same; + using std::enable_if; using meta::NullType; using meta::Node; @@ -111,6 +117,168 @@ namespace lib { }; + template + struct Bare + { + using Type = typename remove_const< + typename remove_reference::type>::type; + }; + + template + struct is_Variant + { + static constexpr bool value = false; + }; + + template + struct is_Variant> + { + static constexpr bool value = true; + }; + + template + struct use_if_is_Variant + : enable_if::Type>::value, V> + { }; + + + template + class FullCopySupport + : public B + { + virtual void + copyInto (void* targetStorage) const override + { + D const& src = static_cast (*this); + new(targetStorage) D(src); + } + + virtual void + moveInto (void* targetStorage) override + { + D& src = static_cast (*this); + new(targetStorage) D(move(src)); + } + + virtual void + copyInto (B& target) const override + { + D& t = D::downcast(target); + D const& s = static_cast (*this); + t = s; + } + + virtual void + moveInto (B& target) override + { + D& t = D::downcast(target); + D& s = static_cast (*this); + t = move(s); + } + }; + + + template + class CloneSupport + : public B + { + virtual void + copyInto (void* targetStorage) const override + { + D const& src = static_cast (*this); + new(targetStorage) D(src); + } + + virtual void + moveInto (void* targetStorage) override + { + D& src = static_cast (*this); + new(targetStorage) D(move(src)); + } + + virtual void + copyInto (B& target) const override + { + throw error::Logic("Assignment invoked but target is not assignable"); + } + + virtual void + moveInto (B& target) override + { + throw error::Logic("Assignment invoked but target is not assignable"); + } + }; + + + template + class MoveSupport + : public B + { + virtual void + copyInto (void* targetStorage) const override + { + throw error::Logic("Copy construction invoked but target allows only move construction"); + } + + virtual void + moveInto (void* targetStorage) override + { + D& src = static_cast (*this); + new(targetStorage) D(move(src)); + } + + virtual void + copyInto (B& target) const override + { + throw error::Logic("Assignment invoked but target is not assignable"); + } + + virtual void + moveInto (B& target) override + { + throw error::Logic("Assignment invoked but target is not assignable"); + } + }; + + + template + class NoCopyMoveSupport + : public B + { + virtual void + copyInto (void* targetStorage) const override + { + throw error::Logic("Copy construction invoked but target is noncopyable"); + } + + virtual void + moveInto (void* targetStorage) override + { + throw error::Logic("Move construction invoked but target is noncopyable"); + } + + virtual void + copyInto (B& target) const override + { + throw error::Logic("Assignment invoked but target is not assignable"); + } + + virtual void + moveInto (B& target) override + { + throw error::Logic("Assignment invoked but target is not assignable"); + } + }; + + + template + struct CopySupport + { + template + using Policy = NoCopyMoveSupport; + }; + + }//(End) implementation helpers @@ -142,8 +310,11 @@ namespace lib { virtual ~Buffer() {} ///< this is an ABC with VTable virtual void copyInto (void* targetStorage) const =0; - virtual void operator= (Buffer const&) =0; - virtual void operator= (Buffer&&) =0; + virtual void moveInto (void* targetStorage) =0; + virtual void copyInto (Buffer& target) const =0; + virtual void moveInto (Buffer& target) =0; +// virtual void operator= (Buffer const&) =0; +// virtual void operator= (Buffer&&) =0; }; @@ -151,7 +322,7 @@ namespace lib { /** concrete inner capsule specialised for a given type */ template struct Buff - : Buffer + : CopySupport::template Policy> { static_assert (SIZ >= sizeof(TY), "Variant record: insufficient embedded Buffer size"); @@ -210,17 +381,17 @@ namespace lib { new(targetStorage) Buff(this->access()); } - virtual void - operator= (Buffer const& ob) override - { - assignValue (downcast(unConst(ob)).access()); - } - - virtual void - operator= (Buffer&& rref) override - { - assignValue (move (downcast(rref).access())); - } +// virtual void +// operator= (Buffer const& ob) override +// { +// assignValue (downcast(unConst(ob)).access()); +// } +// +// virtual void +// operator= (Buffer&& rref) override +// { +// assignValue (move (downcast(rref).access())); +// } static Buff& downcast (Buffer& b) @@ -246,6 +417,36 @@ namespace lib { + template + struct Assign + { + using RawType = typename remove_reference::type; + static_assert (meta::isInList(), + "Type error: the given variant could never hold the required type"); + + + static void + assign (Buffer& buffer, X&& x) + { + buff().assignValue (forward(x)); + } + + + }; + + template + struct Assign::type> + { + + static void + assign (Buffer& buffer, V&& oVar) + { +// oVar.buffer() +// buff().assignValue (forward(oVar)); + } + }; + + protected: /* === internal interface for managing the storage === */ Buffer& @@ -268,6 +469,11 @@ namespace lib { public: + ~Variant() + { + buffer().~Buffer(); + } + Variant() { using DefaultType = typename TYPES::List::Head; @@ -300,16 +506,18 @@ namespace lib { template Variant& - operator= (X&& x) + operator= (X x) { - using RawType = typename remove_reference::type; - static_assert (meta::isInList(), - "Type error: the given variant could never hold the required type"); +// using RawType = typename remove_reference::type; +// static_assert (meta::isInList(), +// "Type error: the given variant could never hold the required type"); +// +// buff().assignValue (forward(x)); - buff().assignValue (forward(x)); + Assign::assign (this->buffer(), forward (x)); return *this; } - +/* Variant& operator= (Variant& ovar) { @@ -330,7 +538,7 @@ namespace lib { buffer().operator= (move(rvar.buffer())); return *this; } - + */ #ifdef LIB_TEST_TEST_HELPER_H /* == diagnostic helper == */