From 413a6a5d48bc51e3389e2552acfcfb0a67b244db Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Thu, 16 Apr 2015 23:04:36 +0200 Subject: [PATCH] outline and stub the API functions. --- src/lib/variant.hpp | 249 +++++++++++++++++++++++++++++++++ tests/library/variant-test.cpp | 41 +++--- 2 files changed, 270 insertions(+), 20 deletions(-) create mode 100644 src/lib/variant.hpp diff --git a/src/lib/variant.hpp b/src/lib/variant.hpp new file mode 100644 index 000000000..f9cd02091 --- /dev/null +++ b/src/lib/variant.hpp @@ -0,0 +1,249 @@ +/* + VARIANT.hpp - lightweight typesafe union record + + Copyright (C) Lumiera.org + 2015, Hermann Vosseler + + 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 not threadsafe. + ** + ** 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 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 +//#include +//#include +//#include + + +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 + class Variant + { + enum { SIZ = meta::maxSize::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 + 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 (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 + Variant(X const& x) + { + UNIMPLEMENTED("place buffer to hold element of type X"); + } + + template + 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 + 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*/ diff --git a/tests/library/variant-test.cpp b/tests/library/variant-test.cpp index 2d6421c21..7685f4533 100644 --- a/tests/library/variant-test.cpp +++ b/tests/library/variant-test.cpp @@ -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