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.
This commit is contained in:
parent
8794aec35a
commit
c32685ada8
2 changed files with 185 additions and 56 deletions
|
|
@ -61,6 +61,7 @@
|
|||
//#include "lib/util.hpp"
|
||||
|
||||
#include <type_traits>
|
||||
#include <typeindex>
|
||||
//#include <utility>
|
||||
//#include <string>
|
||||
//#include <array>
|
||||
|
|
@ -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<typename X, typename TYPES>
|
||||
struct CanBuildFrom
|
||||
: CanBuildFrom<typename remove_reference<X>::type
|
||||
,typename TYPES::List
|
||||
>
|
||||
{ };
|
||||
|
||||
template<typename X, typename TYPES>
|
||||
struct CanBuildFrom<X, Node<X, TYPES>>
|
||||
{
|
||||
using Type = X;
|
||||
};
|
||||
|
||||
template<typename X, typename T,typename TYPES>
|
||||
struct CanBuildFrom<X, Node<T, TYPES>>
|
||||
{
|
||||
using Type = typename CanBuildFrom<X,TYPES>::Type;
|
||||
};
|
||||
|
||||
template<typename X>
|
||||
struct CanBuildFrom<X, NullType>
|
||||
{
|
||||
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<TY*> (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<Buff*> (&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<Buffer*> (&storage_);
|
||||
}
|
||||
Buffer const&
|
||||
buffer() const
|
||||
{
|
||||
return *reinterpret_cast<const Buffer*> (&storage_);
|
||||
}
|
||||
|
||||
template<typename X>
|
||||
Buff<X>&
|
||||
buff()
|
||||
{
|
||||
return Buff<X>::downcast(this->buffer());
|
||||
}
|
||||
|
||||
|
||||
public:
|
||||
Variant()
|
||||
{
|
||||
UNIMPLEMENTED("default constructed element of first type");
|
||||
using DefaultType = typename TYPES::List::Head;
|
||||
|
||||
new(storage_) Buff<DefaultType> (DefaultType());
|
||||
}
|
||||
|
||||
Variant (Variant& ref)
|
||||
{
|
||||
ref.buffer().copyInto (&storage_);
|
||||
}
|
||||
|
||||
Variant (Variant const& ref)
|
||||
{
|
||||
ref.buffer().copyInto (&storage_);
|
||||
}
|
||||
|
||||
Variant (Variant&& rref)
|
||||
{
|
||||
rref.buffer().copyInto (&storage_);
|
||||
}
|
||||
|
||||
template<typename X>
|
||||
Variant(X const& x)
|
||||
Variant(X&& x)
|
||||
{
|
||||
UNIMPLEMENTED("place buffer to hold element of type X");
|
||||
using StorageType = typename CanBuildFrom<X, TYPES>::Type;
|
||||
|
||||
new(storage_) Buff<StorageType> (forward<X>(x));
|
||||
}
|
||||
|
||||
template<typename X>
|
||||
Variant(X && x)
|
||||
Variant&
|
||||
operator= (X&& x)
|
||||
{
|
||||
UNIMPLEMENTED("place buffer and move element of type X");
|
||||
using RawType = typename remove_reference<X>::type;
|
||||
static_assert (meta::isInList<RawType, typename TYPES::List>(),
|
||||
"Type error: the given variant could never hold the required type");
|
||||
|
||||
buff<RawType>().assignValue (forward<X>(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<X, typename TYPES::List>(),
|
||||
"Type error: the given variant could never hold the required type");
|
||||
|
||||
return buff<X>().access();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -30,16 +30,12 @@
|
|||
|
||||
|
||||
//#include <boost/lexical_cast.hpp>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
//#include <map>
|
||||
//using boost::lexical_cast;
|
||||
#include <vector>
|
||||
//#include <vector>
|
||||
|
||||
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) <<endl
|
||||
<< string(v1) <<endl
|
||||
|
|
@ -126,7 +131,7 @@ namespace test{
|
|||
|
||||
TestVariant v3(someTime);
|
||||
TestVariant v2(someStr);
|
||||
TestVariant v1; v1 = someVal;
|
||||
TestVariant v1 = int64_t(someVal);
|
||||
TestVariant v0; v0 = true;
|
||||
|
||||
CHECK (true == v0.get<bool>() );
|
||||
|
|
@ -150,10 +155,10 @@ namespace test{
|
|||
VERIFY_ERROR (WRONG_TYPE, v3.get<int64_t>());
|
||||
VERIFY_ERROR (WRONG_TYPE, v3.get<string>() );
|
||||
|
||||
// does not compile:
|
||||
v0.get<long>();
|
||||
v1.get<double>();
|
||||
v3.get<TimeVar>();
|
||||
//// does not compile...
|
||||
// v0.get<int>();
|
||||
// v1.get<double>();
|
||||
// v3.get<TimeVar>();
|
||||
|
||||
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<string>());
|
||||
CHECK (42 == v2.get<int64_t>());
|
||||
CHECK (24 == v3.get<int64_t>());
|
||||
|
||||
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"));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue