]> git.tdb.fi Git - libs/core.git/blob - source/core/variant.h
Add move semantics to Variant
[libs/core.git] / source / core / variant.h
1 #ifndef MSP_CORE_VARIANT_H_
2 #define MSP_CORE_VARIANT_H_
3
4 #include <stdexcept>
5 #include <type_traits>
6 #include <typeinfo>
7 #include "meta.h"
8 #include "mspcore_api.h"
9
10 namespace Msp {
11
12 class MSPCORE_API type_mismatch: public std::runtime_error
13 {
14 public:
15         type_mismatch(const std::type_info &, const std::type_info &);
16 };
17
18
19 class MSPCORE_API Variant
20 {
21 public:
22         static constexpr unsigned INTERNAL_SIZE = 2*sizeof(void *);
23
24         struct Functions
25         {
26                 const std::type_info &(*get_type)();
27                 bool (*compare)(const char *, const char *);
28                 void (*clone)(char *, const char *);
29                 void (*move)(char *, char *);
30                 void (*destroy)(char *);
31         };
32
33 private:
34         template<typename T>
35         using EnableNotVariant = typename std::enable_if<!std::is_same<typename std::remove_cv<typename std::remove_reference<T>::type>::type, Variant>::value>::type;
36
37         const Functions *funcs = nullptr;
38         alignas(void *) char storage[INTERNAL_SIZE];
39
40 public:
41         Variant() = default;
42         template<typename T, typename = EnableNotVariant<T>>
43         Variant(T &&v) { assign(std::forward<T>(v)); }
44         Variant(const Variant &v) { copy_from(v); }
45         Variant(Variant &&v) { move_from(std::move(v)); }
46         ~Variant() { clear(); }
47
48         template<typename T, typename = EnableNotVariant<T>>
49         Variant &operator=(T &&v) { assign(std::forward<T>(v)); return *this; }
50
51         Variant &operator=(const Variant &v) { if(&v!=this) copy_from(v); return *this; }
52         Variant &operator=(Variant &&v) { if(&v!=this) move_from(std::move(v)); return *this; }
53
54         void clear();
55
56 private:
57         template<typename T>
58         void assign(T &&);
59
60         void copy_from(const Variant &);
61         void move_from(Variant &&);
62
63         template<typename T>
64         T &get();
65
66 public:
67         template<typename T>
68         T &value() { return get<T>(); }
69
70         template<typename T>
71         const T &value() const { return const_cast<Variant *>(this)->get<T>(); }
72
73         template<typename T>
74         bool has_type() const { return type_equals(funcs, get_functions<typename std::remove_cv<T>::type>()); }
75
76         bool has_same_type(const Variant &v) const { return type_equals(funcs, v.funcs); }
77
78         template<typename T>
79         DEPRECATED bool check_type() const { return has_type<T>(); }
80
81         DEPRECATED bool check_same_type(const Variant &v) const { return has_same_type(v); }
82
83         bool operator==(const Variant &v) const { return (has_same_type(v) && funcs->compare(storage, v.storage)); }
84         bool operator!=(const Variant &v) const { return !(operator==(v)); }
85
86         template<typename T>
87         operator T() const { return value<T>(); }
88
89 private:
90         static bool type_equals(const Functions *, const Functions *);
91
92         template<typename T>
93         static constexpr bool is_small() { return (sizeof(T)<=INTERNAL_SIZE && alignof(T)<=alignof(void *)); }
94
95         template<typename T, typename U>
96         using EnableSmall = typename std::enable_if<is_small<T>(), U>::type;
97
98         template<typename T, typename U>
99         using EnableLarge = typename std::enable_if<!is_small<T>(), U>::type;
100
101         template<typename T>
102         static const Functions *get_functions();
103
104         template<typename T>
105         static const std::type_info &get_type() { return typeid(T); }
106
107         template<typename T>
108         static EnableSmall<T, void> create(char *s, T &&v)
109         { new(s) typename std::remove_reference<T>::type(std::forward<T>(v)); }
110
111         template<typename T>
112         static EnableLarge<T, void> create(char *s, T &&v)
113         { using V = typename std::remove_reference<T>::type; *reinterpret_cast<V **>(s) = new V(std::forward<T>(v)); }
114
115         template<typename T>
116         static typename std::enable_if<!IsEqualityComparable<T>::value, bool>::type compare(const char *, const char *)
117         { return false; }
118
119         template<typename T>
120         static typename std::enable_if<IsEqualityComparable<T>::value, EnableSmall<T, bool>>::type compare(const char *s1, const char *s2)
121         { return *reinterpret_cast<const T *>(s1)==*reinterpret_cast<const T *>(s2); }
122
123         template<typename T>
124         static typename std::enable_if<IsEqualityComparable<T>::value, EnableLarge<T, bool>>::type compare(const char *s1, const char *s2)
125         { return **reinterpret_cast<const T *const *>(s1)==**reinterpret_cast<const T *const *>(s2); }
126
127         template<typename T>
128         static EnableSmall<T, void> clone(char *s, const char *v)
129         { new(s) T(*reinterpret_cast<const T *>(v)); }
130
131         template<typename T>
132         static EnableLarge<T, void> clone(char *s, const char *v)
133         { *reinterpret_cast<T **>(s) = new T(**reinterpret_cast<const T *const *>(v)); }
134
135         template<typename T>
136         static EnableSmall<T, void> move(char *s, char *v)
137         { new(s) T(std::move(*reinterpret_cast<T *>(v))); }
138
139         template<typename T>
140         static EnableLarge<T, void> move(char *s, char *v)
141         { T *&p = *reinterpret_cast<T **>(v); *reinterpret_cast<T **>(s) = p; p = nullptr; }
142
143         template<typename T>
144         static EnableSmall<T, void> destroy(char *s)
145         { reinterpret_cast<T *>(s)->~T(); }
146
147         template<typename T>
148         static EnableLarge<T, void> destroy(char *s)
149         { delete *reinterpret_cast<T **>(s); }
150 };
151
152
153 inline void Variant::clear()
154 {
155         if(funcs)
156                 funcs->destroy(storage);
157         funcs = nullptr;
158 }
159
160 template<typename T>
161 inline void Variant::assign(T &&v)
162 {
163         clear();
164         funcs = get_functions<typename std::remove_cv<typename std::remove_reference<T>::type>::type>();
165         create(storage, std::forward<T>(v));
166 }
167
168 inline void Variant::copy_from(const Variant &v)
169 {
170         clear();
171         if((funcs = v.funcs))
172                 funcs->clone(storage, v.storage);
173 }
174
175 inline void Variant::move_from(Variant &&v)
176 {
177         clear();
178         if((funcs = v.funcs))
179                 funcs->move(storage, v.storage);
180         v.clear();
181 }
182
183 template<typename T>
184 inline T &Variant::get()
185 {
186         if(!has_type<T>())
187                 throw type_mismatch(typeid(T), (funcs ? funcs->get_type() : typeid(void)));
188
189         if(sizeof(T)<=INTERNAL_SIZE)
190                 return *reinterpret_cast<T *>(storage);
191         else
192                 return **reinterpret_cast<T **>(storage);
193 }
194
195 inline bool Variant::type_equals(const Functions *funcs1, const Functions *funcs2)
196 {
197         if(!funcs1 || !funcs2)
198                 return false;
199         else if(funcs1==funcs2)
200                 return true;
201         else
202                 return funcs1->get_type()==funcs2->get_type();
203 }
204
205 template<typename T>
206 inline const Variant::Functions *Variant::get_functions()
207 {
208         static Functions funcs =
209         {
210                 &get_type<T>,
211                 &compare<T>,
212                 &clone<T>,
213                 &move<T>,
214                 &destroy<T>
215         };
216         return &funcs;
217 }
218
219 } // namespace Msp
220
221 #endif