From c32685ada821de17b45e9ca4edf1ddc4fdfbb127 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Fri, 17 Apr 2015 19:33:25 +0200 Subject: [PATCH] WIP: first round of implementation finally got all those copy / assgnment flavours straight. Still unsolved: unable to instantiate the Variant template for a type with private assignment operator (like e.g. Time ) The problem is our virtual assignement operator, which forces instantiation of the implementation (for the VTable), even if the actual assignment is never invoked. --- src/lib/variant.hpp | 194 +++++++++++++++++++++++++++------ tests/library/variant-test.cpp | 47 ++++---- 2 files changed, 185 insertions(+), 56 deletions(-) diff --git a/src/lib/variant.hpp b/src/lib/variant.hpp index f9cd02091..72e732e08 100644 --- a/src/lib/variant.hpp +++ b/src/lib/variant.hpp @@ -61,6 +61,7 @@ //#include "lib/util.hpp" #include +#include //#include //#include //#include @@ -68,12 +69,49 @@ namespace lib { - using std::move; using std::string; + + using std::move; + using std::forward; using util::unConst; namespace error = lumiera::error; - using error::LUMIERA_ERROR_WRONG_TYPE; + + namespace { // implementation helpers + + using std::is_constructible; + using std::remove_reference; + using meta::NullType; + using meta::Node; + + + template + struct CanBuildFrom + : CanBuildFrom::type + ,typename TYPES::List + > + { }; + + template + struct CanBuildFrom> + { + using Type = X; + }; + + template + struct CanBuildFrom> + { + using Type = typename CanBuildFrom::Type; + }; + + template + struct CanBuildFrom + { + static_assert (0 > sizeof(X), "No type in Typelist can be built from the given argument"); + }; + + + }//(End) implementation helpers /** @@ -104,6 +142,9 @@ 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; + }; @@ -115,14 +156,14 @@ namespace lib { static_assert (SIZ >= sizeof(TY), "Variant record: insufficient embedded Buffer size"); TY& - get() const ///< core operation: target is contained within the inline buffer + access() const ///< core operation: target is contained within the inline buffer { return *reinterpret_cast (unConst(this)->ptr()); } ~Buff() { - get().~TY(); + access().~TY(); } Buff (TY const& obj) @@ -137,43 +178,28 @@ namespace lib { Buff (Buff const& oBuff) { - new(Buffer::ptr()) TY(oBuff.get()); + new(Buffer::ptr()) TY(oBuff.access()); } Buff (Buff && rBuff) { - new(Buffer::ptr()) TY(move (rBuff.get())); + new(Buffer::ptr()) TY(move (rBuff.access())); } - Buff& - operator= (TY const& obj) + void + assignValue (TY const& ob) { - if (&obj != Buffer::ptr()) - get() = obj; - return *this; + if (&ob != Buffer::ptr()) + this->access() = ob; } - Buff& - operator= (TY && robj) + void + assignValue (TY && rob) { - get() = move(robj); - return *this; + this->access() = move(rob); } - Buff& - operator= (Buff const& ref) - { - if (&ref != this) - get() = ref.get(); - return *this; - } - Buff& - operator= (Buff && rref) - { - get() = move(rref.get()); - return *this; - } /* == virtual access functions == */ @@ -181,7 +207,33 @@ namespace lib { virtual void copyInto (void* targetStorage) const override { - new(targetStorage) Buff(get()); + 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())); + } + + static Buff& + downcast (Buffer& b) + { + Buff* buff = dynamic_cast (&b); + + if (!buff) + throw error::Logic("Variant type mismatch: " + "the given variant record does not hold " + "a value of the type requested here" + ,error::LUMIERA_ERROR_WRONG_TYPE); + else + return *buff; } }; @@ -192,22 +244,91 @@ namespace lib { * @note Invariant: always contains a valid Buffer subclass */ char storage_[BUFFSIZE]; + + + protected: /* === internal interface for managing the storage === */ + + Buffer& + buffer() + { + return *reinterpret_cast (&storage_); + } + Buffer const& + buffer() const + { + return *reinterpret_cast (&storage_); + } + + template + Buff& + buff() + { + return Buff::downcast(this->buffer()); + } + + public: Variant() { - UNIMPLEMENTED("default constructed element of first type"); + using DefaultType = typename TYPES::List::Head; + + new(storage_) Buff (DefaultType()); + } + + Variant (Variant& ref) + { + ref.buffer().copyInto (&storage_); + } + + Variant (Variant const& ref) + { + ref.buffer().copyInto (&storage_); + } + + Variant (Variant&& rref) + { + rref.buffer().copyInto (&storage_); } template - Variant(X const& x) + Variant(X&& x) { - UNIMPLEMENTED("place buffer to hold element of type X"); + using StorageType = typename CanBuildFrom::Type; + + new(storage_) Buff (forward(x)); } template - Variant(X && x) + Variant& + operator= (X&& x) { - UNIMPLEMENTED("place buffer and move element of type X"); + 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)); + return *this; + } + + Variant& + operator= (Variant& ovar) + { + buffer() = ovar.buffer(); + return *this; + } + + Variant& + operator= (Variant const& ovar) + { + buffer() = ovar.buffer(); + return *this; + } + + Variant& + operator= (Variant&& rvar) + { + buffer().operator= (move(rvar.buffer())); + return *this; } @@ -226,7 +347,10 @@ namespace lib { X& get() { - UNIMPLEMENTED("value access"); + static_assert (meta::isInList(), + "Type error: the given variant could never hold the required type"); + + return buff().access(); } diff --git a/tests/library/variant-test.cpp b/tests/library/variant-test.cpp index 7685f4533..9ff282ae7 100644 --- a/tests/library/variant-test.cpp +++ b/tests/library/variant-test.cpp @@ -30,16 +30,12 @@ //#include -#include #include +#include //#include //using boost::lexical_cast; -#include +//#include -using util::contains; -using std::string; -using std::cout; -using std::endl; namespace lib { @@ -50,6 +46,14 @@ namespace test{ using lib::time::Time; using lib::time::TimeVar; + using util::contains; + using std::string; + using std::cout; + using std::endl; + + using error::LUMIERA_ERROR_WRONG_TYPE; + + namespace { // test fixture... }//(End) test fixture @@ -88,11 +92,12 @@ namespace test{ { Time someTime; TestVariant v0; - TestVariant v1(11); - TestVariant v2("lololo"); + TestVariant v1(11L); + TestVariant v2(string("lololo")); TestVariant v3(someTime); - VERIFY_ERROR (WRONG_TYPE, TestVariant(3.1415)); + //// does not compile.... + // TestVariant evil(3.1415); cout << string(v0) <() ); @@ -150,10 +155,10 @@ namespace test{ VERIFY_ERROR (WRONG_TYPE, v3.get()); VERIFY_ERROR (WRONG_TYPE, v3.get() ); - // does not compile: - v0.get(); - v1.get(); - v3.get(); + //// does not compile... + // v0.get(); + // v1.get(); + // v3.get(); struct Accessor : TestVariant::Visitor @@ -208,20 +213,20 @@ namespace test{ void verifyAssignment() { - TestVariant v1("boo"); - TestVariant v2(23); - TestVariant v3(42); + TestVariant v1(string("boo")); + TestVariant v2(23L); + TestVariant v3(42L); - v1 = "booo"; + v1 = string("booo"); v2 = v3; - v3 = 24; + v3 = 24L; CHECK ("booo" == v1.get()); CHECK (42 == v2.get()); CHECK (24 == v3.get()); VERIFY_ERROR (WRONG_TYPE, v1 = v2 ); - VERIFY_ERROR (WRONG_TYPE, v1 = 22 ); - VERIFY_ERROR (WRONG_TYPE, v2 = "2"); + VERIFY_ERROR (WRONG_TYPE, v1 = 22L ); + VERIFY_ERROR (WRONG_TYPE, v2 = string("2")); } };