]> git.tdb.fi Git - libs/core.git/blob - source/core/variant.h
Check errors from CreateSemaphore
[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, bool small = (sizeof(T)<=INTERNAL_SIZE && alignof(T)<=alignof(void *))>
35         struct FunctionsImpl;
36
37         template<typename T>
38         using EnableNotVariant = typename std::enable_if<!std::is_same<typename std::remove_cv<typename std::remove_reference<T>::type>::type, Variant>::value>::type;
39
40         const Functions *funcs = nullptr;
41         alignas(void *) char storage[INTERNAL_SIZE];
42
43 public:
44         Variant() = default;
45         template<typename T, typename = EnableNotVariant<T>>
46         Variant(T &&v) { assign(std::forward<T>(v)); }
47         Variant(const Variant &v) { copy_from(v); }
48         Variant(Variant &&v) { move_from(std::move(v)); }
49         ~Variant() { clear(); }
50
51         template<typename T, typename = EnableNotVariant<T>>
52         Variant &operator=(T &&v) { assign(std::forward<T>(v)); return *this; }
53
54         Variant &operator=(const Variant &v) { if(&v!=this) copy_from(v); return *this; }
55         Variant &operator=(Variant &&v) { if(&v!=this) move_from(std::move(v)); return *this; }
56
57         void clear();
58         bool has_value() const { return funcs; }
59
60 private:
61         template<typename T>
62         void assign(T &&);
63
64         void copy_from(const Variant &);
65         void move_from(Variant &&);
66
67         template<typename T>
68         T &get();
69
70 public:
71         template<typename T>
72         T &value() { return get<T>(); }
73
74         template<typename T>
75         const T &value() const { return const_cast<Variant *>(this)->get<T>(); }
76
77         template<typename T>
78         bool has_type() const { return type_equals(funcs, get_functions<typename std::remove_cv<T>::type>()); }
79
80         bool has_same_type(const Variant &v) const { return type_equals(funcs, v.funcs); }
81
82         template<typename T>
83         DEPRECATED bool check_type() const { return has_type<T>(); }
84
85         DEPRECATED bool check_same_type(const Variant &v) const { return has_same_type(v); }
86
87         bool operator==(const Variant &v) const;
88         bool operator!=(const Variant &v) const { return !(operator==(v)); }
89
90         template<typename T>
91         operator T() const { return value<T>(); }
92
93 private:
94         static bool type_equals(const Functions *, const Functions *);
95
96         template<typename T>
97         static const Functions *get_functions();
98
99         template<typename T>
100         static const std::type_info &get_type() { return typeid(T); }
101
102         /* The extra function parameter is needed to avoid duplicate definition error
103         from MSVC 19.29 (VS 2019) */
104         template<typename T, typename = typename std::enable_if<!IsEqualityComparable<T>::value>::type>
105         static bool compare(const T &, const T &, int = 0)
106         { return false; }
107
108         template<typename T, typename = typename std::enable_if<IsEqualityComparable<T>::value>::type>
109         static bool compare(const T &v1, const T &v2)
110         { return v1==v2; }
111 };
112
113
114 template<typename T>
115 struct Variant::FunctionsImpl<T, true>
116 {
117         static void create(char *s, T &&v)
118         { new(s) typename std::remove_reference<T>::type(std::forward<T>(v)); }
119
120         static bool compare(const char *s1, const char *s2)
121         { return Variant::compare<T>(*reinterpret_cast<const T *>(s1), *reinterpret_cast<const T *>(s2)); }
122
123         static void clone(char *s, const char *v)
124         { new(s) T(*reinterpret_cast<const T *>(v)); }
125
126         static void move(char *s, char *v)
127         { new(s) T(std::move(*reinterpret_cast<T *>(v))); }
128
129         static void destroy(char *s)
130         { reinterpret_cast<T *>(s)->~T(); }
131 };
132
133 template<typename T>
134 struct Variant::FunctionsImpl<T, false>
135 {
136         static void create(char *s, T &&v)
137         { using V = typename std::remove_reference<T>::type; *reinterpret_cast<V **>(s) = new V(std::forward<T>(v)); }
138
139         static bool compare(const char *s1, const char *s2)
140         { return Variant::compare<T>(**reinterpret_cast<const T *const *>(s1), **reinterpret_cast<const T *const *>(s2)); }
141
142         static void clone(char *s, const char *v)
143         { *reinterpret_cast<T **>(s) = new T(**reinterpret_cast<const T *const *>(v)); }
144
145         static void move(char *s, char *v)
146         { T *&p = *reinterpret_cast<T **>(v); *reinterpret_cast<T **>(s) = p; p = nullptr; }
147
148         static 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         FunctionsImpl<T>::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::operator==(const Variant &other) const
196 {
197         if(!funcs && !other.funcs)
198                 return true;
199         else if(has_same_type(other))
200                 return funcs->compare(storage, other.storage);
201         else
202                 return false;
203 }
204
205 inline bool Variant::type_equals(const Functions *funcs1, const Functions *funcs2)
206 {
207         if(!funcs1 || !funcs2)
208                 return false;
209         else if(funcs1==funcs2)
210                 return true;
211         else
212                 return funcs1->get_type()==funcs2->get_type();
213 }
214
215 template<typename T>
216 inline const Variant::Functions *Variant::get_functions()
217 {
218         using Impl = FunctionsImpl<T>;
219         static Functions funcs =
220         {
221                 &get_type<T>,
222                 &Impl::compare,
223                 &Impl::clone,
224                 &Impl::move,
225                 &Impl::destroy
226         };
227         return &funcs;
228 }
229
230 } // namespace Msp
231
232 #endif