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