outline and stub the API functions.
This commit is contained in:
parent
eb263d44d7
commit
413a6a5d48
2 changed files with 270 additions and 20 deletions
249
src/lib/variant.hpp
Normal file
249
src/lib/variant.hpp
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
/*
|
||||
VARIANT.hpp - lightweight typesafe union record
|
||||
|
||||
Copyright (C) Lumiera.org
|
||||
2015, Hermann Vosseler <Ichthyostega@web.de>
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License as
|
||||
published by the Free Software Foundation; either version 2 of
|
||||
the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/** @file variant.hpp
|
||||
** A typesafe union record to carry embedded values of unrelated type.
|
||||
** This file defines a simple alternative to boost::variant. It pulls in
|
||||
** fewer headers, has a shorter code path and is hopefully more readable,
|
||||
** but also doesn't deal with alignment issues and is <b>not threadsafe</b>.
|
||||
**
|
||||
** Deliberately, the design rules out re-binding of the contained type. Thus,
|
||||
** once created, a variant \em must hold a valid element and always an element
|
||||
** of the same type. Beyond that, variant elements are copyable and mutable.
|
||||
** Direct access requires knowledge of the embedded type (no switch-on type).
|
||||
** Type mismatch is checked at runtime. As a fallback, we provide a visitor
|
||||
** scheme for generic access.
|
||||
**
|
||||
** \par implementation notes
|
||||
** We use a similar "double capsule" implementation technique as for lib::OpaqueHolder.
|
||||
** In fact, Variant is almost identical to the latter, just omitting unnecessary flexibility.
|
||||
** The outer capsule exposes the public handling interface, while the inner, private capsule
|
||||
** is a polymorphic value holder. Since C++ as such does not support polymorphic values,
|
||||
** the inner capsule is placed "piggyback" into a char buffer. The actual value is carried
|
||||
** within yet another, nested char buffer. Thus, effectively the first "slot" of the storage
|
||||
** will hold the VTable pointer, thereby encoding the actual type information -- leading to
|
||||
** a storage requirement of MAX<TYPES...> plus one "slot" for the VTable. (with "slot" we
|
||||
** denote the smallest disposable storage size for the given platform after alignment,
|
||||
** typically the size of a size_t).
|
||||
**
|
||||
** @see Veriant_test
|
||||
** @see lib::diff::GenNode
|
||||
**
|
||||
*/
|
||||
|
||||
|
||||
#ifndef LIB_VARIANT_H
|
||||
#define LIB_VARIANT_H
|
||||
|
||||
|
||||
#include "lib/meta/typelist.hpp"
|
||||
#include "lib/meta/typelist-util.hpp"
|
||||
//#include "lib/util.hpp"
|
||||
|
||||
#include <type_traits>
|
||||
//#include <utility>
|
||||
//#include <string>
|
||||
//#include <array>
|
||||
|
||||
|
||||
namespace lib {
|
||||
|
||||
using std::move;
|
||||
using std::string;
|
||||
using util::unConst;
|
||||
|
||||
namespace error = lumiera::error;
|
||||
using error::LUMIERA_ERROR_WRONG_TYPE;
|
||||
|
||||
|
||||
/**
|
||||
* Typesafe union record.
|
||||
* A Variant element may carry an embedded value of any of the predefined types.
|
||||
* The type may not be rebound: It must be created holding some value and each
|
||||
* instance is fixed to the specific type used at construction time.
|
||||
* Yet within the same type, variant elements are copyable and assignable.
|
||||
* The embedded type is erased on the signature, but knowledge about the
|
||||
* actual type is retained, encoded into the embedded VTable. Thus,
|
||||
* any access to the variant's value requires knowledge of the type
|
||||
* in question, but type mismatch will provoke an exception at runtime.
|
||||
* Generic access is possible using a visitor.
|
||||
*/
|
||||
template<typename TYPES>
|
||||
class Variant
|
||||
{
|
||||
enum { SIZ = meta::maxSize<typename TYPES::List>::value };
|
||||
|
||||
/** Inner capsule managing the contained object (interface) */
|
||||
struct Buffer
|
||||
{
|
||||
char content_[SIZ];
|
||||
|
||||
void* ptr() { return &content_; }
|
||||
|
||||
|
||||
virtual ~Buffer() {} ///< this is an ABC with VTable
|
||||
|
||||
virtual void copyInto (void* targetStorage) const =0;
|
||||
};
|
||||
|
||||
|
||||
/** concrete inner capsule specialised for a given type */
|
||||
template<typename TY>
|
||||
struct Buff
|
||||
: Buffer
|
||||
{
|
||||
static_assert (SIZ >= sizeof(TY), "Variant record: insufficient embedded Buffer size");
|
||||
|
||||
TY&
|
||||
get() const ///< core operation: target is contained within the inline buffer
|
||||
{
|
||||
return *reinterpret_cast<TY*> (unConst(this)->ptr());
|
||||
}
|
||||
|
||||
~Buff()
|
||||
{
|
||||
get().~TY();
|
||||
}
|
||||
|
||||
Buff (TY const& obj)
|
||||
{
|
||||
new(Buffer::ptr()) TY(obj);
|
||||
}
|
||||
|
||||
Buff (TY && robj)
|
||||
{
|
||||
new(Buffer::ptr()) TY(move(robj));
|
||||
}
|
||||
|
||||
Buff (Buff const& oBuff)
|
||||
{
|
||||
new(Buffer::ptr()) TY(oBuff.get());
|
||||
}
|
||||
|
||||
Buff (Buff && rBuff)
|
||||
{
|
||||
new(Buffer::ptr()) TY(move (rBuff.get()));
|
||||
}
|
||||
|
||||
Buff&
|
||||
operator= (TY const& obj)
|
||||
{
|
||||
if (&obj != Buffer::ptr())
|
||||
get() = obj;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Buff&
|
||||
operator= (TY && robj)
|
||||
{
|
||||
get() = move(robj);
|
||||
return *this;
|
||||
}
|
||||
|
||||
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 == */
|
||||
|
||||
virtual void
|
||||
copyInto (void* targetStorage) const override
|
||||
{
|
||||
new(targetStorage) Buff(get());
|
||||
}
|
||||
};
|
||||
|
||||
enum{ BUFFSIZE = sizeof(Buffer) };
|
||||
|
||||
/** embedded buffer actually holding the concrete Buff object,
|
||||
* which in turn holds and manages the target object.
|
||||
* @note Invariant: always contains a valid Buffer subclass */
|
||||
char storage_[BUFFSIZE];
|
||||
|
||||
public:
|
||||
Variant()
|
||||
{
|
||||
UNIMPLEMENTED("default constructed element of first type");
|
||||
}
|
||||
|
||||
template<typename X>
|
||||
Variant(X const& x)
|
||||
{
|
||||
UNIMPLEMENTED("place buffer to hold element of type X");
|
||||
}
|
||||
|
||||
template<typename X>
|
||||
Variant(X && x)
|
||||
{
|
||||
UNIMPLEMENTED("place buffer and move element of type X");
|
||||
}
|
||||
|
||||
|
||||
#ifdef LIB_TEST_TEST_HELPER_H
|
||||
/* == diagnostic helper == */
|
||||
|
||||
operator string() const
|
||||
{
|
||||
UNIMPLEMENTED("diagnostic string conversion");
|
||||
}
|
||||
#endif
|
||||
|
||||
/* === Access === */
|
||||
|
||||
template<typename X>
|
||||
X&
|
||||
get()
|
||||
{
|
||||
UNIMPLEMENTED("value access");
|
||||
}
|
||||
|
||||
|
||||
class Visitor
|
||||
{
|
||||
public:
|
||||
virtual ~Visitor() { } ///< this is an interface
|
||||
};
|
||||
|
||||
void
|
||||
accept (Visitor& visitor)
|
||||
{
|
||||
UNIMPLEMENTED("visitor style value access");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace lib
|
||||
#endif /*LIB_VARIANT_H*/
|
||||
|
|
@ -24,6 +24,8 @@
|
|||
|
||||
#include "lib/test/run.hpp"
|
||||
#include "lib/test/test-helper.hpp"
|
||||
#include "lib/time/timevalue.hpp"
|
||||
#include "lib/variant.hpp"
|
||||
#include "lib/util.hpp"
|
||||
|
||||
|
||||
|
|
@ -44,6 +46,9 @@ namespace lib {
|
|||
namespace test{
|
||||
|
||||
using ::Test;
|
||||
using meta::Types;
|
||||
using lib::time::Time;
|
||||
using lib::time::TimeVar;
|
||||
|
||||
namespace { // test fixture...
|
||||
|
||||
|
|
@ -81,18 +86,18 @@ namespace test{
|
|||
void
|
||||
createVariant()
|
||||
{
|
||||
Time someTime;
|
||||
TestVariant v0;
|
||||
TestVariant v1(11);
|
||||
TestVariant v2("lololo");
|
||||
|
||||
auto v3 = TestVariant::empty<Time>();
|
||||
TestVariant v3(someTime);
|
||||
|
||||
VERIFY_ERROR (WRONG_TYPE, TestVariant(3.1415));
|
||||
|
||||
cout << v0 <<endl
|
||||
<< v1 <<endl
|
||||
<< v2 <<endl
|
||||
<< v3 <<endl;
|
||||
cout << string(v0) <<endl
|
||||
<< string(v1) <<endl
|
||||
<< string(v2) <<endl
|
||||
<< string(v3) <<endl;
|
||||
|
||||
CHECK (contains (string(v0), "Variant"));
|
||||
CHECK (contains (string(v0), "bool"));
|
||||
|
|
@ -115,18 +120,14 @@ namespace test{
|
|||
void
|
||||
accessVariant()
|
||||
{
|
||||
int someVal = rand() % 10000;
|
||||
int someStr = randStr(55);
|
||||
int someTime = randTime();
|
||||
int someVal = rand() % 10000;
|
||||
string someStr = randStr(55);
|
||||
Time someTime = randTime();
|
||||
|
||||
TestVariant v3(someTime);
|
||||
TestVariant v2(someStr);
|
||||
|
||||
auto v1 = TestVariant::empty<int64_t>();
|
||||
v1 = someVal;
|
||||
|
||||
TestVariant v0;
|
||||
v0 = true;
|
||||
TestVariant v1; v1 = someVal;
|
||||
TestVariant v0; v0 = true;
|
||||
|
||||
CHECK (true == v0.get<bool>() );
|
||||
CHECK (someVal == v1.get<int64_t>());
|
||||
|
|
@ -152,14 +153,14 @@ namespace test{
|
|||
// does not compile:
|
||||
v0.get<long>();
|
||||
v1.get<double>();
|
||||
v4.get<TimeVal>();
|
||||
v3.get<TimeVar>();
|
||||
|
||||
struct Accessor
|
||||
: TestVariant::Visitor
|
||||
{
|
||||
bool b_ = false;
|
||||
int i_ = 12;
|
||||
Time t_;
|
||||
TimeVar t_;
|
||||
|
||||
void handle (bool b) { b_ = b; }
|
||||
void handle (Time t) { t_ = t; }
|
||||
|
|
@ -171,7 +172,7 @@ namespace test{
|
|||
}
|
||||
};
|
||||
|
||||
Accsssor acs;
|
||||
Accessor acs;
|
||||
CHECK (!acs.b_);
|
||||
CHECK (acs.i_ == 12);
|
||||
|
||||
|
|
@ -179,12 +180,12 @@ namespace test{
|
|||
CHECK (acs.b_);
|
||||
CHECK (acs.i_ == 12);
|
||||
|
||||
v4.accept (acs);
|
||||
v3.accept (acs);
|
||||
CHECK (acs.b_);
|
||||
CHECK (acs.i_ == 12);
|
||||
CHECK (acs.t_ == someTime);
|
||||
|
||||
v3.accept (acs);
|
||||
v2.accept (acs);
|
||||
// nothing changed,
|
||||
// since we defined no accessor function
|
||||
CHECK (acs.b_);
|
||||
|
|
|
|||
Loading…
Reference in a new issue