fennec
Loading...
Searching...
No Matches
variant.h
Go to the documentation of this file.
1// =====================================================================================================================
2// fennec, a free and open source game engine
3// Copyright © 2025 Medusa Slockbower
4//
5// This program is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with this program. If not, see <https://www.gnu.org/licenses/>.
17// =====================================================================================================================
18
30
31#ifndef FENNEC_CONTAINERS_VARIANT_H
32#define FENNEC_CONTAINERS_VARIANT_H
33
36#include <fennec/math/ext/common.h>
37#include <fennec/rtti/type.h>
38
39namespace fennec
40{
41
45template<typename...TypesT>
46struct variant {
47// Assertions ==========================================================================================================
48
49 static_assert(
50 is_unique_v<TypesT...> and // No two types in TypesT... may be equivalent
51 not (is_reference_v<TypesT> or ...) and // No type in TypesT... may be a reference
52 not (is_array_v<TypesT> or ...) and // No type in TypesT... may be an array
53 not (is_void_v<TypesT> or ...) // No type in TypesT... may be void
54 );
55
56
57// Typedefs & Constants ================================================================================================
58
59 static constexpr size_t size = max_element_size_v<TypesT...>;
60 static constexpr size_t nulltype = sizeof...(TypesT);
61
62
63
64
65// Constructors ========================================================================================================
66
70 : _bytes {}
71 , _type(nulltype) {
72 using construct_t = search_element_t<is_default_constructible, TypesT...>;
73 fennec::construct<construct_t>(_handle);
74 }
75
81 template<typename T>
82 variant(T&& t)
83 : _bytes {}
84 , _type() {
85 using same_t = search_element_args<is_same, type_sequence<T>, TypesT...>;
86 using convert_t = search_element_args<is_constructible, type_sequence<T>, TypesT...>;
87 using construct_t = conditional_t<is_void_v<same_t>, convert_t, convert_t>;
88 fennec::construct<construct_t>(_handle, fennec::forward<T>(t));
89 }
90
95 template<typename T, typename...ArgsT>
96 variant(type_identity<T>, ArgsT&&...args)
97 : _bytes{}
98 , _type(nulltype) {
99 static_assert(contains_element_v<T, TypesT...>, "T must be in TypesT...");
100 fennec::construct<T>(_handle, fennec::forward<ArgsT>(args)...);
101 _type = find_element_v<T>;
102 }
103
107 variant(const variant& v)
108 : _bytes {}
109 , _type(nulltype) {
110
111 if (v._type == nulltype) {
112 return;
113 }
114
115 ((v._type == find_element_v<TypesT, TypesT...> ?
116 fennec::construct<TypesT>(_handle, v.get<TypesT>()) :
117 (0)
118 ), ...);
119 _type = v._type;
120 }
121
125 variant(variant&& v) noexcept
126 : _bytes {}
127 , _type() {
128
129 if (v._type == nulltype) {
130 return;
131 }
132
133 ((v._type == find_element_v<TypesT, TypesT...> ?
134 fennec::construct<TypesT>(_handle, fennec::move(v.get<TypesT>())) :
135 (0)
136 ), ...);
137 _type = v._type;
138 }
139
143 _clear();
144 }
145
146
147// Assignment ==========================================================================================================
148
149 template<typename T>
150 variant& operator=(T&& t) {
151
152 // First, check if `T` is in `TypesT...`
153 if constexpr((contains_element_v<T, TypesT> or ...)) {
154 using type_t = remove_reference_t<T>;
155 if (_type == find_element_v<type_t, TypesT...>) {
156 *static_cast<type_t*>(_handle) = fennec::forward<T>(t);
157 } else {
158 _clear();
159 fennec::construct<type_t>(_handle, fennec::forward<T>(t));
160 _type = find_element_v<type_t, TypesT...>;
161 }
162 return *this;
163 }
164
165 // Next, try to assign using the currently held type
166 bool assigned = false;
167 if (_type != nulltype) {
168 ((_type == find_element_v<TypesT, TypesT...> ?
169 (*static_cast<TypesT*>(_handle) = fennec::forward<T>(t), assigned = true) :
170 (0)
171 ), ...);
172 }
173
174 if (assigned) {
175 return *this;
176 }
177
178 // Otherwise, destruct, then construct
179 _clear();
180 using construct_t = search_element_args<is_constructible, type_sequence<T>, TypesT...>;
181 fennec::construct<construct_t>(_handle, fennec::forward<T>(t));
182 return *this;
183 }
184
185 template<typename T, typename...ArgsT> requires(contains_element_v<T, TypesT...>)
186 void emplace(ArgsT&&...args) {
187 _clear();
188 fennec::construct<T>(_handle, fennec::forward<ArgsT>(args)...);
189 }
190
191 template<size_t I, typename...ArgsT>
192 void emplace(ArgsT&&...args) {
193 using type_t = nth_element_t<I, TypesT...>;
194 _clear();
195 fennec::construct<type_t>(fennec::forward<ArgsT>(args)...);
196 }
197
198
199// Access ==============================================================================================================
200
201 template<typename T> requires(contains_element_v<T, TypesT...>)
202 T& get() {
203 return *static_cast<T*>(_handle);
204 }
205
206 template<typename T> requires(contains_element_v<T, TypesT...>)
207 const T& get() const {
208 return *static_cast<T*>(_handle);
209 }
210
211 template<size_t I, typename T = nth_element_t<I, TypesT...>> requires(contains_element_v<T, TypesT...>)
212 T& get() {
213 return *static_cast<T*>(_handle);
214 }
215
216 template<size_t I, typename T = nth_element_t<I, TypesT...>> requires(contains_element_v<T, TypesT...>)
217 const T& get() const {
218 return *static_cast<T*>(_handle);
219 }
220
221
222
223
224private:
225 union {
226 byte_t _bytes[size];
227 void* _handle;
228 };
229 size_t _type;
230
231 void _clear() {
232 if (_type == nulltype) {
233 return;
234 }
235
236 ((_type == find_element_v<TypesT, TypesT...> ?
237 fennec::destruct<TypesT>(_handle) :
238 (0)
239 ), ...);
240 _type = nulltype;
241 }
242};
243
244}
245
246#endif // FENNEC_CONTAINERS_VARIANT_H
typename conditional< B, TrueT, FalseT >::type conditional_t
Shorthand for typename conditional<ConditionV, TrueT, FalseT>::type
Definition conditional_types.h:90
A header containing the definition for a container with an optionally present variable.
Check if ClassT is default constructible.
Definition type_traits.h:1110
Base Class for Type Transformations.
Definition type_identity.h:49
A structure that represents a union between TypesT...
Definition variant.h:46
variant(variant &&v) noexcept
Move Constructor.
Definition variant.h:125
variant(const variant &v)
Copy Constructor.
Definition variant.h:107
~variant()
Destructor, if a type is held, destruct it.
Definition variant.h:142
variant(T &&t)
Conversion Constructor, constructs the type in TypesT... that is identical to T or the first that is ...
Definition variant.h:82
variant()
Default Constructor, constructs the first type in TypesT... that is default constructible.
Definition variant.h:69
variant(type_identity< T >, ArgsT &&...args)
Emplace Constructor, constructs the first type in TypesT... that is constructible with ArgsT....
Definition variant.h:96
Type Sequences
constexpr bool contains_element_v
Shorthand for contains_element_v<T, Ts...>::value
Definition type_sequences.h:181
constexpr size_t max_element_size_v
Shorthand for max_element_size<Ts...>::value
Definition type_sequences.h:123
constexpr bool is_unique_v
Shorthand for is_unique<Ts...>::value
Definition type_sequences.h:202
constexpr size_t find_element_v
Shorthand for find_element<T, Ts...>::value
Definition type_sequences.h:139
search_element< SearchT, TypesT... >::type search_element_t
Shorthand for search_element_t<T, Ts...>::type
Definition type_sequences.h:156
typename remove_reference< T >::type remove_reference_t
shorthand for typename remove_reference<T>::type
Definition type_transforms.h:206
unsigned char byte_t
A type capable of holding a single byte.
Definition types.h:216