]> git.tdb.fi Git - libs/core.git/blob - source/core/variant.h
Rewrite the Variant class
[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 check_type() const { return funcs==get_functions<typename std::remove_cv<T>::type>(); }
65
66         bool check_same_type(const Variant &v) const { return (funcs && funcs==v.funcs); }
67
68         bool operator==(const Variant &v) const { return (has_same_type(v) && funcs->compare(storage, v.storage)); }
69         bool operator!=(const Variant &v) const { return !(operator==(v)); }
70
71         template<typename T>
72         operator T() const { return value<T>(); }
73
74 private:
75         template<typename T>
76         static constexpr bool is_small() { return (sizeof(T)<=INTERNAL_SIZE && alignof(T)<=alignof(void *)); }
77
78         template<typename T, typename U>
79         using EnableSmall = typename std::enable_if<is_small<T>(), U>::type;
80
81         template<typename T, typename U>
82         using EnableLarge = typename std::enable_if<!is_small<T>(), U>::type;
83
84         template<typename T>
85         static const Functions *get_functions();
86
87         template<typename T>
88         static const std::type_info &get_type() { return typeid(T); }
89
90         template<typename T>
91         static EnableSmall<T, void> create(char *s, const T &v)
92         { new(s) T(v); }
93
94         template<typename T>
95         static EnableLarge<T, void> create(char *s, const T &v)
96         { *reinterpret_cast<T **>(s) = new T(v); }
97
98         template<typename T>
99         static typename std::enable_if<!IsEqualityComparable<T>::value, bool>::type compare(const char *, const char *)
100         { return false; }
101
102         template<typename T>
103         static typename std::enable_if<IsEqualityComparable<T>::value, EnableSmall<T, bool>>::type compare(const char *s1, const char *s2)
104         { return *reinterpret_cast<const T *>(s1)==*reinterpret_cast<const T *>(s2); }
105
106         template<typename T>
107         static typename std::enable_if<IsEqualityComparable<T>::value, EnableLarge<T, bool>>::type compare(const char *s1, const char *s2)
108         { return **reinterpret_cast<const T *const *>(s1)==**reinterpret_cast<const T *const *>(s2); }
109
110         template<typename T>
111         static EnableSmall<T, void> clone(char *s, const char *v)
112         { new(s) T(*reinterpret_cast<const T *>(v)); }
113
114         template<typename T>
115         static EnableLarge<T, void> clone(char *s, const char *v)
116         { *reinterpret_cast<T **>(s) = new T(**reinterpret_cast<const T *const *>(v)); }
117
118         template<typename T>
119         static EnableSmall<T, void> destroy(char *s)
120         { reinterpret_cast<T *>(s)->~T(); }
121
122         template<typename T>
123         static EnableLarge<T, void> destroy(char *s)
124         { delete *reinterpret_cast<T **>(s); }
125 };
126
127
128 template<typename T>
129 inline void Variant::assign(const T &value)
130 {
131         if(funcs)
132                 funcs->destroy(storage);
133
134         funcs = get_functions<typename std::remove_cv<T>::type>();
135         create(storage, value);
136 }
137
138 inline void Variant::copy_from(const Variant &v)
139 {
140         if(funcs)
141                 funcs->destroy(storage);
142
143         funcs = v.funcs;
144         if(funcs)
145                 funcs->clone(storage, v.storage);
146 }
147
148 template<typename T>
149 inline T &Variant::get()
150 {
151         if(!has_type<T>())
152                 throw type_mismatch(typeid(T), (funcs ? funcs->get_type() : typeid(void)));
153
154         if(sizeof(T)<=INTERNAL_SIZE)
155                 return *reinterpret_cast<T *>(storage);
156         else
157                 return **reinterpret_cast<T **>(storage);
158 }
159
160 template<typename T>
161 inline const Variant::Functions *Variant::get_functions()
162 {
163         static Functions funcs =
164         {
165                 &get_type<T>,
166                 &compare<T>,
167                 &clone<T>,
168                 &destroy<T>
169         };
170         return &funcs;
171 }
172
173 } // namespace Msp
174
175 #endif