FastLED 3.9.15
Loading...
Searching...
No Matches
variant.h
Go to the documentation of this file.
1#pragma once
2
3#include "fl/inplacenew.h" // for fl::move, fl::forward, in‐place new
4#include "fl/type_traits.h" // for fl::enable_if, fl::is_same, etc.
5#include "fl/bit_cast.h" // for safe type-punning
6
7namespace fl {
8
9// A variant that can hold any of N different types
10template <typename... Types>
11class alignas(max_align<Types...>::value) Variant {
12 public:
13 using Tag = u8;
14 static constexpr Tag Empty = 0;
15
16 // –– ctors/dtors/assign as before …
17
18 Variant() noexcept : _tag(Empty) {}
19
20 template <typename T, typename = typename fl::enable_if<
21 contains_type<T, Types...>::value>::type>
22 Variant(const T &value) : _tag(Empty) {
23 construct<T>(value);
24 }
25
26 template <typename T, typename = typename fl::enable_if<
27 contains_type<T, Types...>::value>::type>
28 Variant(T &&value) : _tag(Empty) {
29 construct<T>(fl::move(value));
30 }
31
32 Variant(const Variant &other) : _tag(Empty) {
33 if (!other.empty()) {
34 copy_construct_from(other);
35 }
36 }
37
38 Variant(Variant &&other) noexcept : _tag(Empty) {
39 if (!other.empty()) {
41 // After moving, mark other as empty to prevent destructor calls on moved-from objects
42 other._tag = Empty;
43 }
44 }
45
46 ~Variant() { reset(); }
47
48 Variant &operator=(const Variant &other) {
49 if (this != &other) {
50 reset();
51 if (!other.empty()) {
53 }
54 }
55 return *this;
56 }
57
58 Variant &operator=(Variant &&other) noexcept {
59 if (this != &other) {
60 reset();
61 if (!other.empty()) {
63 // After moving, mark other as empty to prevent destructor calls on moved-from objects
64 other._tag = Empty;
65 }
66 }
67 return *this;
68 }
69
70 template <typename T, typename = typename fl::enable_if<
71 contains_type<T, Types...>::value>::type>
72 Variant &operator=(const T &value) {
73 reset();
74 construct<T>(value);
75 return *this;
76 }
77
78 template <typename T, typename = typename fl::enable_if<
79 contains_type<T, Types...>::value>::type>
80 Variant &operator=(T &&value) {
81 reset();
82 construct<T>(fl::move(value));
83 return *this;
84 }
85
86 // –– modifiers, observers, ptr/get, etc. unchanged …
87
88 template <typename T, typename... Args>
89 typename fl::enable_if<contains_type<T, Types...>::value, T &>::type
90 emplace(Args &&...args) {
91 reset();
93 return ptr<T>();
94 }
95
96 void reset() noexcept {
97 if (!empty()) {
99 _tag = Empty;
100 }
101 }
102
103 Tag tag() const noexcept { return _tag; }
104 bool empty() const noexcept { return _tag == Empty; }
105
106 template <typename T> bool is() const noexcept {
107 return _tag == type_to_tag<T>();
108 }
109
110 template <typename T> T *ptr() {
111 if (!is<T>()) return nullptr;
112 // Use bit_cast_ptr for safe type-punning on properly aligned storage
113 // The storage is guaranteed to be properly aligned by alignas(max_align<Types...>::value)
114 return fl::bit_cast_ptr<T>(&_storage[0]);
115 }
116
117 template <typename T> const T *ptr() const {
118 if (!is<T>()) return nullptr;
119 // Use bit_cast_ptr for safe type-punning on properly aligned storage
120 // The storage is guaranteed to be properly aligned by alignas(max_align<Types...>::value)
122 }
123
129 template <typename T> T &get() {
130 // Dereference ptr() directly - will crash with null pointer access if wrong type
131 // This provides fast failure semantics similar to std::variant
132 return *ptr<T>();
133 }
134
140 template <typename T> const T &get() const {
141 // Dereference ptr() directly - will crash with null pointer access if wrong type
142 // This provides fast failure semantics similar to std::variant
143 return *ptr<T>();
144 }
145
146 template <typename T> bool equals(const T &other) const {
147 if (auto p = ptr<T>()) {
148 return *p == other;
149 }
150 return false;
151 }
152
153 // –– visitor using O(1) function‐pointer table
154 template <typename Visitor> void visit(Visitor &visitor) {
155 if (_tag == Empty)
156 return;
157
158 // Fn is "a pointer to function taking (void* storage, Visitor&)"
159 using Fn = void (*)(void *, Visitor &);
160
161 // Build a constexpr array of one thunk per type in Types...
162 // Each thunk casts the storage back to the right T* and calls
163 // visitor.accept
164 static constexpr Fn table[] = {
165 &Variant::template visit_fn<Types, Visitor>...};
166
167 // _tag is 1-based, so dispatch in O(1) via one indirect call:
168 // Check bounds to prevent out-of-bounds access
169 size_t index = _tag - 1;
170 if (index < sizeof...(Types)) {
171 table[index](&_storage, visitor);
172 }
173 }
174
175 template <typename Visitor> void visit(Visitor &visitor) const {
176 if (_tag == Empty)
177 return;
178
179 // Fn is "a pointer to function taking (const void* storage, Visitor&)"
180 using Fn = void (*)(const void *, Visitor &);
181
182 // Build a constexpr array of one thunk per type in Types...
183 static constexpr Fn table[] = {
184 &Variant::template visit_fn_const<Types, Visitor>...};
185
186 // _tag is 1-based, so dispatch in O(1) via one indirect call:
187 // Check bounds to prevent out-of-bounds access
188 size_t index = _tag - 1;
189 if (index < sizeof...(Types)) {
190 table[index](&_storage, visitor);
191 }
192 }
193
194 private:
195 // –– helper for the visit table
196 template <typename T, typename Visitor>
197 static void visit_fn(void *storage, Visitor &v) {
198 // Use bit_cast_ptr for safe type-punning on properly aligned storage
199 // The storage is guaranteed to be properly aligned by alignas(max_align<Types...>::value)
200 T* typed_ptr = fl::bit_cast_ptr<T>(storage);
201 v.accept(*typed_ptr);
202 }
203
204 template <typename T, typename Visitor>
205 static void visit_fn_const(const void *storage, Visitor &v) {
206 // Use bit_cast_ptr for safe type-punning on properly aligned storage
207 // The storage is guaranteed to be properly aligned by alignas(max_align<Types...>::value)
208 const T* typed_ptr = fl::bit_cast_ptr<const T>(storage);
209 v.accept(*typed_ptr);
210 }
211
212 // –– destroy via table
213 void destroy_current() noexcept {
214 using Fn = void (*)(void *);
215 static constexpr Fn table[] = {&Variant::template destroy_fn<Types>...};
216 if (_tag != Empty) {
217 table[_tag - 1](&_storage);
218 }
219 }
220
221 template <typename T> static void destroy_fn(void *storage) {
222 // Use bit_cast_ptr for safe type-punning on properly aligned storage
223 // The storage is guaranteed to be properly aligned by alignas(max_align<Types...>::value)
224 T* typed_ptr = fl::bit_cast_ptr<T>(storage);
225 typed_ptr->~T();
226 }
227
228 // –– copy‐construct via table
229 void copy_construct_from(const Variant &other) {
230 using Fn = void (*)(void *, const Variant &);
231 static constexpr Fn table[] = {&Variant::template copy_fn<Types>...};
232 table[other._tag - 1](&_storage, other);
233 _tag = other._tag;
234 }
235
236 template <typename T>
237 static void copy_fn(void *storage, const Variant &other) {
238 // Use bit_cast_ptr for safe type-punning on properly aligned storage
239 // The storage is guaranteed to be properly aligned by alignas(max_align<Types...>::value)
240 const T* source_ptr = fl::bit_cast_ptr<const T>(&other._storage[0]);
241 new (storage) T(*source_ptr);
242 }
243
244 // –– move‐construct via table
245 void move_construct_from(Variant &other) noexcept {
246 using Fn = void (*)(void *, Variant &);
247 static constexpr Fn table[] = {&Variant::template move_fn<Types>...};
248 table[other._tag - 1](&_storage, other);
249 _tag = other._tag;
250 other.reset();
251 }
252
253 template <typename T> static void move_fn(void *storage, Variant &other) {
254 // Use bit_cast_ptr for safe type-punning on properly aligned storage
255 // The storage is guaranteed to be properly aligned by alignas(max_align<Types...>::value)
256 T* source_ptr = fl::bit_cast_ptr<T>(&other._storage[0]);
257 new (storage) T(fl::move(*source_ptr));
258 }
259
260 // –– everything below here (type_traits, construct<T>, type_to_tag,
261 // storage)
262 // stays exactly as you wrote it:
263
264 // … max_size, max_align, contains_type, type_to_tag_impl, etc. …
265
266 // Helper to map a type to its tag value
267 template <typename T> static constexpr Tag type_to_tag() {
268 return type_to_tag_impl<T, Types...>::value;
269 }
270
271 // Implementation details for type_to_tag
272 template <typename T, typename... Ts> struct type_to_tag_impl;
273
274 template <typename T> struct type_to_tag_impl<T> {
275 static constexpr Tag value = 0; // Not found
276 };
277
278 template <typename T, typename U, typename... Rest>
279 struct type_to_tag_impl<T, U, Rest...> {
280 static constexpr Tag value =
282 ? 1
283 : (type_to_tag_impl<T, Rest...>::value == 0
284 ? 0
285 : type_to_tag_impl<T, Rest...>::value + 1);
286 };
287
288 template <typename T, typename... Args> void construct(Args &&...args) {
289 new (&_storage) T(fl::forward<Args>(args)...);
291 }
292
293 alignas(
295
297};
298
299} // namespace fl
Variant & operator=(Variant &&other) noexcept
Definition variant.h:58
Variant(const T &value)
Definition variant.h:22
Variant & operator=(const T &value)
Definition variant.h:72
const T * ptr() const
Definition variant.h:117
Variant & operator=(const Variant &other)
Definition variant.h:48
Tag tag() const noexcept
Definition variant.h:103
void construct(Args &&...args)
Definition variant.h:288
bool is() const noexcept
Definition variant.h:106
Variant() noexcept
Definition variant.h:18
Variant(const Variant &other)
Definition variant.h:32
T * ptr()
Definition variant.h:110
void reset() noexcept
Definition variant.h:96
static constexpr Tag type_to_tag()
Definition variant.h:267
fl::enable_if< contains_type< T, Types... >::value, T & >::type emplace(Args &&...args)
Definition variant.h:90
Variant(T &&value)
Definition variant.h:28
void destroy_current() noexcept
Definition variant.h:213
void visit(Visitor &visitor) const
Definition variant.h:175
Variant & operator=(T &&value)
Definition variant.h:80
void visit(Visitor &visitor)
Definition variant.h:154
bool empty() const noexcept
Definition variant.h:104
const T & get() const
Get a const reference to the stored value of type T.
Definition variant.h:140
void copy_construct_from(const Variant &other)
Definition variant.h:229
T & get()
Get a reference to the stored value of type T.
Definition variant.h:129
bool equals(const T &other) const
Definition variant.h:146
Variant(Variant &&other) noexcept
Definition variant.h:38
void move_construct_from(Variant &other) noexcept
Definition variant.h:245
constexpr remove_reference< T >::type && move(T &&t) noexcept
Definition move.h:27
unsigned char u8
Definition int.h:17
To * bit_cast_ptr(void *storage) noexcept
Definition bit_cast.h:54
constexpr T && forward(typename remove_reference< T >::type &t) noexcept
IMPORTANT!
Definition crgb.h:20
corkscrew_args args
Definition old.h:150
static constexpr Tag value
Definition variant.h:275
static constexpr bool value
Definition type_traits.h:84