]> git.tdb.fi Git - libs/core.git/commitdiff
Add move semantics to Variant
authorMikko Rasa <tdb@tdb.fi>
Thu, 1 Jun 2023 07:17:56 +0000 (10:17 +0300)
committerMikko Rasa <tdb@tdb.fi>
Thu, 1 Jun 2023 07:17:56 +0000 (10:17 +0300)
source/core/variant.h

index fccf891850f8045a528ad723b7fd781c7df72c09..5cb1b931ca403eab55a0ef38e9be86b3afa3d0c5 100644 (file)
@@ -26,32 +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(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();
@@ -98,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 *)
@@ -125,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(); }
@@ -143,11 +158,11 @@ inline void Variant::clear()
 }
 
 template<typename T>
-inline void Variant::assign(const T &v)
+inline void Variant::assign(T &&v)
 {
        clear();
-       funcs = get_functions<typename std::remove_cv<T>::type>();
-       create(storage, v);
+       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)
@@ -157,6 +172,14 @@ inline void Variant::copy_from(const Variant &v)
                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()
 {
@@ -187,6 +210,7 @@ inline const Variant::Functions *Variant::get_functions()
                &get_type<T>,
                &compare<T>,
                &clone<T>,
+               &move<T>,
                &destroy<T>
        };
        return &funcs;