Line data Source code
1 : // A c++11 variant class
2 : // source: https://gist.github.com/S6066/f726a37b2b703efea7ee27103e5bec89
3 :
4 : #ifndef variant_h
5 : #define variant_h
6 :
7 : #include <cassert>
8 : #include <type_traits>
9 : #include <utility>
10 :
11 : template <typename...> struct IsOneOf { static constexpr bool value = false; };
12 :
13 : template <typename T, typename S, typename... Ts> struct IsOneOf<T, S, Ts...> {
14 : static constexpr bool value = std::is_same<T, S>::value || IsOneOf<T, Ts...>::value;
15 : };
16 :
17 : #include <type_traits>
18 :
19 : template <typename...> struct IndexOf;
20 :
21 : // Found
22 : template <class T, class... Rest>
23 : struct IndexOf<T, T, Rest...> : std::integral_constant<std::size_t, 0u> {};
24 :
25 : // Still searching
26 : template <class T, class Other, class... Rest>
27 : struct IndexOf<T, Other, Rest...>
28 : : std::integral_constant<std::size_t, 1 + IndexOf<T, Rest...>::value> {};
29 :
30 : namespace Detail {
31 :
32 : template <class... Ts> struct VariantHelper;
33 :
34 : template <class Union, class T, class... Ts> struct VariantHelper<Union, T, Ts...> {
35 : inline static void destroy(std::size_t index, Union* data);
36 : inline static void move(std::size_t index, Union* oldValue, Union* newValue);
37 : inline static void copy(std::size_t index, const Union* oldValue, Union* new_v);
38 : };
39 :
40 : template <class Union> struct VariantHelper<Union> {
41 0 : inline static void destroy(std::size_t index, Union* data) {}
42 : inline static void move(std::size_t index, Union* oldValue, Union* newValue) {}
43 0 : inline static void copy(std::size_t index, const Union* oldValue, Union* newValue) {}
44 : };
45 :
46 : } // namespace Detail
47 :
48 : template <class... Ts> class Variant {
49 : public:
50 : static_assert(sizeof...(Ts) > 1, "Variant must have at least 2 different types");
51 :
52 : Variant() = default;
53 :
54 : template <class T, class... Args,
55 : class = typename std::enable_if<IsOneOf<T, Ts...>::value>::type>
56 1 : Variant(const T& t) {
57 1 : new (&m_data) T(t);
58 1 : m_index = IndexOf<T, void, Ts...>::value;
59 1 : }
60 :
61 : inline ~Variant();
62 :
63 : inline Variant(const Variant<Ts...>& other);
64 : inline Variant(Variant<Ts...>&& other);
65 :
66 : inline Variant<Ts...>& operator=(const Variant<Ts...>& other);
67 : inline Variant<Ts...>& operator=(Variant<Ts...>&& other);
68 :
69 : template <class T> inline bool is() const;
70 :
71 : inline bool valid() const;
72 :
73 : template <class T, class... Args,
74 : class = typename std::enable_if<IsOneOf<T, Ts...>::value>::type>
75 : inline void set(Args&&... args);
76 :
77 : template <class T, class = typename std::enable_if<IsOneOf<T, Ts...>::value>::type>
78 : inline const T& get() const;
79 :
80 : template <class T, class = typename std::enable_if<IsOneOf<T, Ts...>::value>::type>
81 : inline T& get();
82 :
83 : inline void reset();
84 :
85 : private:
86 : using Data = typename std::aligned_union<0, Ts...>::type;
87 : using Helper = Detail::VariantHelper<Data, Ts...>;
88 :
89 : std::size_t m_index{};
90 : Data m_data;
91 : };
92 :
93 : namespace Detail {
94 :
95 : template <class Union, class T, class... Ts>
96 6 : void VariantHelper<Union, T, Ts...>::destroy(std::size_t index, Union* data) {
97 6 : if (index == 0u)
98 : reinterpret_cast<T*>(data)->~T();
99 :
100 : else {
101 4 : --index;
102 4 : VariantHelper<Union, Ts...>::destroy(index, data);
103 : }
104 6 : }
105 :
106 : template <class Union, class T, class... Ts>
107 : void VariantHelper<Union, T, Ts...>::move(std::size_t index, Union* oldValue, Union* newValue) {
108 : if (index == 0u)
109 : new (newValue) T(std::move(*reinterpret_cast<T*>(oldValue)));
110 :
111 : else {
112 : --index;
113 : VariantHelper<Union, Ts...>::move(index, oldValue, newValue);
114 : }
115 : }
116 :
117 : template <class Union, class T, class... Ts>
118 3 : void VariantHelper<Union, T, Ts...>::copy(std::size_t index, const Union* oldValue,
119 : Union* newValue) {
120 3 : if (index == 0u)
121 1 : new (newValue) T(*reinterpret_cast<const T*>(oldValue));
122 :
123 : else {
124 2 : --index;
125 2 : VariantHelper<Union, Ts...>::copy(index, oldValue, newValue);
126 : }
127 3 : }
128 :
129 : } // namespace Detail
130 :
131 2 : template <class... Ts> Variant<Ts...>::~Variant() {
132 2 : if (valid())
133 2 : Helper::destroy(m_index - 1u, &m_data);
134 2 : }
135 :
136 : template <class... Ts>
137 1 : Variant<Ts...>::Variant(const Variant<Ts...>& other) : m_index{other.m_index} {
138 1 : if (valid())
139 1 : Helper::copy(m_index - 1u, &other.m_data, &m_data);
140 1 : }
141 :
142 : template <class... Ts> Variant<Ts...>::Variant(Variant<Ts...>&& other) : m_index{other.m_index} {
143 : if (valid())
144 : Helper::move(m_index - 1u, &other.m_data, &m_data);
145 : }
146 :
147 : template <class... Ts> Variant<Ts...>& Variant<Ts...>::operator=(const Variant<Ts...>& other) {
148 : if (valid())
149 : Helper::destroy(m_index - 1u, &m_data);
150 :
151 : m_index = other.m_index;
152 :
153 : if (valid())
154 : Helper::copy(m_index - 1u, &other.m_data, &m_data);
155 :
156 : return *this;
157 : }
158 :
159 : template <class... Ts> Variant<Ts...>& Variant<Ts...>::operator=(Variant<Ts...>&& other) {
160 : if (valid())
161 : Helper::destroy(m_index - 1u, &m_data);
162 :
163 : m_index = other.m_index;
164 :
165 : if (valid())
166 : Helper::move(m_index - 1u, &other.m_data, &m_data);
167 :
168 : return *this;
169 : }
170 :
171 0 : template <class... Ts> template <class T> bool Variant<Ts...>::is() const {
172 0 : return m_index == IndexOf<T, void, Ts...>::value;
173 : }
174 :
175 3 : template <class... Ts> bool Variant<Ts...>::valid() const {
176 3 : return m_index != 0u; // void
177 : }
178 :
179 : template <class... Ts>
180 : template <class T, class... Args, class>
181 : void Variant<Ts...>::set(Args&&... args) {
182 : if (valid())
183 : Helper::destroy(m_index - 1u, &m_data);
184 :
185 : new (&m_data) T(std::forward<Args>(args)...);
186 : m_index = IndexOf<T, void, Ts...>::value;
187 : }
188 :
189 0 : template <class... Ts> template <class T, class> const T& Variant<Ts...>::get() const {
190 0 : assert(valid() && "Uninitialized variant !");
191 :
192 0 : if (m_index == IndexOf<T, void, Ts...>::value) {
193 0 : const T* ptr = reinterpret_cast<const T*>(&m_data);
194 0 : return *ptr;
195 : }
196 :
197 : else
198 0 : throw std::bad_cast{};
199 : }
200 :
201 : template <class... Ts> template <class T, class> T& Variant<Ts...>::get() {
202 : assert(valid() && "Uninitialized variant !");
203 :
204 : if (m_index == IndexOf<T, void, Ts...>::value) {
205 : T* ptr = reinterpret_cast<T*>(&m_data);
206 : return *ptr;
207 : }
208 :
209 : else
210 : throw std::bad_cast{};
211 : }
212 :
213 : template <class... Ts> void Variant<Ts...>::reset() {
214 : if (valid())
215 : Helper::destroy(m_index - 1u, &m_data);
216 :
217 : m_index = 0u;
218 : }
219 :
220 : template <class... Ts> using variant = Variant<Ts...>;
221 :
222 : #endif /* variant_hpp */
|