]> git.tdb.fi Git - libs/core.git/blobdiff - source/core/variant.h
Add move semantics to Variant
[libs/core.git] / source / core / variant.h
index 04c372b54ef305205e6f38e661a408eab75d18d6..5cb1b931ca403eab55a0ef38e9be86b3afa3d0c5 100644 (file)
@@ -5,17 +5,18 @@
 #include <type_traits>
 #include <typeinfo>
 #include "meta.h"
+#include "mspcore_api.h"
 
 namespace Msp {
 
-class type_mismatch: public std::runtime_error
+class MSPCORE_API type_mismatch: public std::runtime_error
 {
 public:
        type_mismatch(const std::type_info &, const std::type_info &);
 };
 
 
-class Variant
+class MSPCORE_API Variant
 {
 public:
        static constexpr unsigned INTERNAL_SIZE = 2*sizeof(void *);
@@ -25,30 +26,39 @@ public:
                const std::type_info &(*get_type)();
                bool (*compare)(const char *, const char *);
                void (*clone)(char *, const char *);
+               void (*move)(char *, char *);
                void (*destroy)(char *);
        };
 
 private:
+       template<typename T>
+       using EnableNotVariant = typename std::enable_if<!std::is_same<typename std::remove_cv<typename std::remove_reference<T>::type>::type, Variant>::value>::type;
+
        const Functions *funcs = nullptr;
        alignas(void *) char storage[INTERNAL_SIZE];
 
 public:
        Variant() = default;
-       template<typename T>
-       Variant(const T &v) { assign(v); }
+       template<typename T, typename = EnableNotVariant<T>>
+       Variant(T &&v) { assign(std::forward<T>(v)); }
        Variant(const Variant &v) { copy_from(v); }
-       ~Variant() { if(funcs) funcs->destroy(storage); }
+       Variant(Variant &&v) { move_from(std::move(v)); }
+       ~Variant() { clear(); }
 
-       template<typename T>
-       Variant &operator=(const T &v) { assign(v); return *this; }
+       template<typename T, typename = EnableNotVariant<T>>
+       Variant &operator=(T &&v) { assign(std::forward<T>(v)); return *this; }
 
        Variant &operator=(const Variant &v) { if(&v!=this) copy_from(v); return *this; }
+       Variant &operator=(Variant &&v) { if(&v!=this) move_from(std::move(v)); return *this; }
+
+       void clear();
 
 private:
        template<typename T>
-       void assign(const T &);
+       void assign(T &&);
 
        void copy_from(const Variant &);
+       void move_from(Variant &&);
 
        template<typename T>
        T &get();
@@ -61,9 +71,14 @@ public:
        const T &value() const { return const_cast<Variant *>(this)->get<T>(); }
 
        template<typename T>
-       bool check_type() const { return funcs==get_functions<typename std::remove_cv<T>::type>(); }
+       bool has_type() const { return type_equals(funcs, get_functions<typename std::remove_cv<T>::type>()); }
+
+       bool has_same_type(const Variant &v) const { return type_equals(funcs, v.funcs); }
+
+       template<typename T>
+       DEPRECATED bool check_type() const { return has_type<T>(); }
 
-       bool check_same_type(const Variant &v) const { return (funcs && funcs==v.funcs); }
+       DEPRECATED bool check_same_type(const Variant &v) const { return has_same_type(v); }
 
        bool operator==(const Variant &v) const { return (has_same_type(v) && funcs->compare(storage, v.storage)); }
        bool operator!=(const Variant &v) const { return !(operator==(v)); }
@@ -72,6 +87,8 @@ public:
        operator T() const { return value<T>(); }
 
 private:
+       static bool type_equals(const Functions *, const Functions *);
+
        template<typename T>
        static constexpr bool is_small() { return (sizeof(T)<=INTERNAL_SIZE && alignof(T)<=alignof(void *)); }
 
@@ -88,12 +105,12 @@ private:
        static const std::type_info &get_type() { return typeid(T); }
 
        template<typename T>
-       static EnableSmall<T, void> create(char *s, const T &v)
-       { new(s) T(v); }
+       static EnableSmall<T, void> create(char *s, T &&v)
+       { new(s) typename std::remove_reference<T>::type(std::forward<T>(v)); }
 
        template<typename T>
-       static EnableLarge<T, void> create(char *s, const T &v)
-       { *reinterpret_cast<T **>(s) = new T(v); }
+       static EnableLarge<T, void> create(char *s, T &&v)
+       { using V = typename std::remove_reference<T>::type; *reinterpret_cast<V **>(s) = new V(std::forward<T>(v)); }
 
        template<typename T>
        static typename std::enable_if<!IsEqualityComparable<T>::value, bool>::type compare(const char *, const char *)
@@ -115,6 +132,14 @@ private:
        static EnableLarge<T, void> clone(char *s, const char *v)
        { *reinterpret_cast<T **>(s) = new T(**reinterpret_cast<const T *const *>(v)); }
 
+       template<typename T>
+       static EnableSmall<T, void> move(char *s, char *v)
+       { new(s) T(std::move(*reinterpret_cast<T *>(v))); }
+
+       template<typename T>
+       static EnableLarge<T, void> move(char *s, char *v)
+       { T *&p = *reinterpret_cast<T **>(v); *reinterpret_cast<T **>(s) = p; p = nullptr; }
+
        template<typename T>
        static EnableSmall<T, void> destroy(char *s)
        { reinterpret_cast<T *>(s)->~T(); }
@@ -125,26 +150,36 @@ private:
 };
 
 
-template<typename T>
-inline void Variant::assign(const T &value)
+inline void Variant::clear()
 {
        if(funcs)
                funcs->destroy(storage);
+       funcs = nullptr;
+}
 
-       funcs = get_functions<typename std::remove_cv<T>::type>();
-       create(storage, value);
+template<typename T>
+inline void Variant::assign(T &&v)
+{
+       clear();
+       funcs = get_functions<typename std::remove_cv<typename std::remove_reference<T>::type>::type>();
+       create(storage, std::forward<T>(v));
 }
 
 inline void Variant::copy_from(const Variant &v)
 {
-       if(funcs)
-               funcs->destroy(storage);
-
-       funcs = v.funcs;
-       if(funcs)
+       clear();
+       if((funcs = v.funcs))
                funcs->clone(storage, v.storage);
 }
 
+inline void Variant::move_from(Variant &&v)
+{
+       clear();
+       if((funcs = v.funcs))
+               funcs->move(storage, v.storage);
+       v.clear();
+}
+
 template<typename T>
 inline T &Variant::get()
 {
@@ -157,6 +192,16 @@ inline T &Variant::get()
                return **reinterpret_cast<T **>(storage);
 }
 
+inline bool Variant::type_equals(const Functions *funcs1, const Functions *funcs2)
+{
+       if(!funcs1 || !funcs2)
+               return false;
+       else if(funcs1==funcs2)
+               return true;
+       else
+               return funcs1->get_type()==funcs2->get_type();
+}
+
 template<typename T>
 inline const Variant::Functions *Variant::get_functions()
 {
@@ -165,6 +210,7 @@ inline const Variant::Functions *Variant::get_functions()
                &get_type<T>,
                &compare<T>,
                &clone<T>,
+               &move<T>,
                &destroy<T>
        };
        return &funcs;