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