tetl 0.1.0
Embedded Template Library
Loading...
Searching...
No Matches
variant.hpp
Go to the documentation of this file.
1// SPDX-License-Identifier: BSL-1.0
2// SPDX-FileCopyrightText: Copyright (C) 2020 Tobias Hienzsch
3
4#ifndef TETL_VARIANT_VARIANT2_HPP
5#define TETL_VARIANT_VARIANT2_HPP
6
7#include <etl/_config/all.hpp>
8
9#include <etl/_contracts/check.hpp>
10#include <etl/_cstddef/size_t.hpp>
11#include <etl/_functional/equal_to.hpp>
12#include <etl/_functional/greater.hpp>
13#include <etl/_functional/greater_equal.hpp>
14#include <etl/_functional/less.hpp>
15#include <etl/_functional/less_equal.hpp>
16#include <etl/_memory/addressof.hpp>
17#include <etl/_memory/construct_at.hpp>
18#include <etl/_memory/destroy_at.hpp>
19#include <etl/_mpl/at.hpp>
20#include <etl/_mpl/count.hpp>
21#include <etl/_mpl/index_of.hpp>
22#include <etl/_type_traits/add_pointer.hpp>
23#include <etl/_type_traits/index_constant.hpp>
24#include <etl/_type_traits/is_assignable.hpp>
25#include <etl/_type_traits/is_constructible.hpp>
26#include <etl/_type_traits/is_copy_assignable.hpp>
27#include <etl/_type_traits/is_copy_constructible.hpp>
28#include <etl/_type_traits/is_default_constructible.hpp>
29#include <etl/_type_traits/is_move_assignable.hpp>
30#include <etl/_type_traits/is_move_constructible.hpp>
31#include <etl/_type_traits/is_nothrow_assignable.hpp>
32#include <etl/_type_traits/is_nothrow_constructible.hpp>
33#include <etl/_type_traits/is_nothrow_copy_assignable.hpp>
34#include <etl/_type_traits/is_nothrow_copy_constructible.hpp>
35#include <etl/_type_traits/is_nothrow_default_constructible.hpp>
36#include <etl/_type_traits/is_nothrow_move_assignable.hpp>
37#include <etl/_type_traits/is_nothrow_move_constructible.hpp>
38#include <etl/_type_traits/is_same.hpp>
39#include <etl/_type_traits/is_trivially_copy_assignable.hpp>
40#include <etl/_type_traits/is_trivially_copy_constructible.hpp>
41#include <etl/_type_traits/is_trivially_move_assignable.hpp>
42#include <etl/_type_traits/is_trivially_move_constructible.hpp>
43#include <etl/_type_traits/remove_cvref.hpp>
44#include <etl/_type_traits/smallest_size_t.hpp>
45#include <etl/_utility/forward.hpp>
46#include <etl/_utility/in_place_index.hpp>
47#include <etl/_utility/in_place_type.hpp>
48#include <etl/_utility/unreachable.hpp>
49#include <etl/_variant/variadic_union.hpp>
50#include <etl/_variant/variant_alternative.hpp>
51#include <etl/_variant/variant_alternative_selector.hpp>
52#include <etl/_variant/variant_fwd.hpp>
53#include <etl/_variant/variant_size.hpp>
54#include <etl/_variant/visit.hpp>
55
56namespace etl {
57
58namespace detail {
59
60template <typename T>
61concept variant_copy_assignable = is_copy_constructible_v<T> and is_copy_assignable_v<T>;
62
63template <typename T>
64concept variant_trivially_copy_assignable = is_trivially_copy_constructible_v<T> and is_trivially_copy_assignable_v<T>;
65
66template <typename T>
67concept variant_move_assignable = is_move_constructible_v<T> and is_move_assignable_v<T>;
68
69template <typename T>
70concept variant_trivially_move_assignable = is_trivially_move_constructible_v<T> and is_trivially_move_assignable_v<T>;
71
72template <typename T>
73inline constexpr auto is_in_place_index = false;
74
75template <size_t I>
76inline constexpr auto is_in_place_index<in_place_index_t<I>> = true;
77
78template <typename T>
79inline constexpr auto is_in_place_type = false;
80
81template <typename T>
82inline constexpr auto is_in_place_type<in_place_type_t<T>> = true;
83
84constexpr auto make_variant_compare_op(auto op)
85{
86 return [op](auto const& l, auto const& r) -> bool {
87 if constexpr (etl::is_same_v<decltype(l), decltype(r)>) {
88 return op(l, r);
89 } else {
90 etl::unreachable();
91 }
92 };
93}
94
95} // namespace detail
96
97/// \ingroup variant
98template <typename... Ts>
100private:
101 // Avoid valueless_by_exception
102 static_assert((is_nothrow_move_constructible_v<Ts> and ...));
103
104 using index_type = smallest_size_t<sizeof...(Ts)>;
105 using first_type = mpl::at_t<0, mpl::list<Ts...>>;
106
107public:
108 constexpr variant() noexcept(is_nothrow_default_constructible_v<first_type>)
109 requires(is_default_constructible_v<first_type>)
110 : variant(in_place_index<0>)
111 {
112 }
113
114 template <typename T>
115 requires(
116 (sizeof...(Ts) > 0)
117 and not is_same_v<remove_cvref_t<T>, variant>
118 and not detail::is_in_place_index<remove_cvref_t<T>>
119 and not detail::is_in_place_type<remove_cvref_t<T>>
120 and is_constructible_v<detail::variant_alternative_selector_t<T, Ts...>, T>
121 )
123 : variant(in_place_type<detail::variant_alternative_selector_t<T, Ts...>>, etl::forward<T>(t))
124 {
125 }
126
127 template <size_t I, typename... Args>
128 requires((I < sizeof...(Ts)) and is_constructible_v<variant_alternative_t<I, variant>, Args...>)
129 explicit constexpr variant(in_place_index_t<I> /*index*/, Args&&... args)
130 : _index(static_cast<index_type>(I))
131 , _union(index_v<I>, etl::forward<Args>(args)...)
132 {
133 }
134
135 template <typename T, typename... Args>
136 requires(is_constructible_v<T, Args...> and mpl::count_v<remove_cvref_t<T>, mpl::list<Ts...>> == 1)
137 explicit constexpr variant(in_place_type_t<T> /*tag*/, Args&&... args)
138 : variant(in_place_index<mpl::index_of_v<T, mpl::list<Ts...>>>, etl::forward<Args>(args)...)
139 {
140 }
141
142 constexpr variant(variant const&) = default;
143
144 constexpr variant(variant const& other) noexcept((... and etl::is_nothrow_copy_constructible_v<Ts>))
145 requires((... and etl::is_copy_constructible_v<Ts>) and not(... and etl::is_trivially_copy_constructible_v<Ts>))
146 : variant(other, copy_move_tag{})
147 {
148 }
149
150 constexpr variant(variant&&) = default;
151
152 constexpr variant(variant&& other) noexcept((... and is_nothrow_move_constructible_v<Ts>))
153 requires((... and is_move_constructible_v<Ts>) and not(... and is_trivially_move_constructible_v<Ts>))
154 : variant(etl::move(other), copy_move_tag{})
155 {
156 }
157
158 constexpr auto operator=(variant const&) -> variant& = default;
159
160 constexpr auto operator=(variant const& other) noexcept(
162 ) -> variant&
163 requires(
164 (... and detail::variant_copy_assignable<Ts>) and not(... and detail::variant_trivially_copy_assignable<Ts>)
165 )
166 {
167 assign(other);
168 return *this;
169 }
170
171 constexpr auto operator=(variant&&) -> variant& = default;
172
173 constexpr auto operator=(variant&& other) noexcept(
175 ) -> variant&
176 requires(
177 (... and detail::variant_move_assignable<Ts>) and not(... and detail::variant_trivially_move_assignable<Ts>)
178 )
179 {
180 assign(etl::move(other));
181 return *this;
182 }
183
184 template <typename T>
185 requires(
186 not is_same_v<remove_cvref_t<T>, variant>
187 and is_assignable_v<detail::variant_alternative_selector_t<T, Ts...>&, T>
188 and is_assignable_v<detail::variant_alternative_selector_t<T, Ts...>, T>
189 )
190 constexpr auto operator=(T&& t) noexcept((
193 )) -> variant&
194 {
195 emplace<detail::variant_alternative_selector_t<T, Ts...>>(etl::forward<T>(t));
196 return *this;
197 }
198
199 ~variant()
200 requires(... and is_trivially_destructible_v<Ts>)
201 = default;
202
203 constexpr ~variant()
204 {
205 destroy();
206 }
207
208 /// Returns the zero-based index of the alternative that is currently held by the variant.
209 [[nodiscard]] constexpr auto index() const noexcept -> size_t
210 {
211 return static_cast<size_t>(_index);
212 }
213
214 /// Returns a reference to the object stored in the variant.
215 /// \pre I == index()
216 template <size_t I>
217 constexpr auto operator[](index_constant<I> index) & -> auto&
218 {
219 static_assert(I < sizeof...(Ts));
220 TETL_PRECONDITION(I == index());
221 return _union[index];
222 }
223
224 /// Returns a reference to the object stored in the variant.
225 /// \pre I == index()
226 template <size_t I>
227 constexpr auto operator[](index_constant<I> index) const& -> auto const&
228 {
229 static_assert(I < sizeof...(Ts));
230 TETL_PRECONDITION(I == index());
231 return _union[index];
232 }
233
234 /// Returns a reference to the object stored in the variant.
235 /// \pre I == index()
236 template <size_t I>
237 constexpr auto operator[](index_constant<I> index) && -> auto&&
238 {
239 static_assert(I < sizeof...(Ts));
240 TETL_PRECONDITION(I == index());
241 return etl::move(_union)[index];
242 }
243
244 /// Returns a reference to the object stored in the variant.
245 /// \pre I == index()
246 template <size_t I>
247 constexpr auto operator[](index_constant<I> index) const&& -> auto const&&
248 {
249 static_assert(I < sizeof...(Ts));
250 TETL_PRECONDITION(I == index());
251 return etl::move(_union)[index];
252 }
253
254 template <typename T, typename... Args>
255 requires(is_constructible_v<T, Args...> and mpl::count_v<T, mpl::list<Ts...>> == 1)
256 constexpr auto emplace(Args&&... args) -> auto&
257 {
258 destroy();
259 return replace(index_v<mpl::index_of_v<T, mpl::list<Ts...>>>, etl::forward<Args>(args)...);
260 }
261
262 template <size_t I, typename... Args>
263 requires is_constructible_v<mpl::at_t<I, mpl::list<Ts...>>, Args...>
264 constexpr auto emplace(Args&&... args) -> auto&
265 {
266 destroy();
267 return replace(index_v<I>, etl::forward<Args>(args)...);
268 }
269
270 /// Equality operator
271 ///
272 /// - If lhs.index() != rhs.index(), returns false;
273 /// - Otherwise returns get<lhs.index()>(lhs) == get<lhs.index()>(rhs)
274 friend constexpr auto operator==(variant const& lhs, variant const& rhs) -> bool
275 {
276 if (lhs.index() != rhs.index()) {
277 return false;
278 }
279 return etl::visit(detail::make_variant_compare_op(etl::equal_to()), lhs, rhs);
280 }
281
282 /// Less-than operator
283 ///
284 /// - If lhs.index() < rhs.index(), returns true;
285 /// - If lhs.index() > rhs.index(), returns false;
286 /// - Otherwise returns get<lhs.index()>(v) < get<lhs.index()>(w)
287 friend constexpr auto operator<(variant const& lhs, variant const& rhs) -> bool
288 {
289 if (lhs.index() < rhs.index()) {
290 return true;
291 }
292 if (lhs.index() > rhs.index()) {
293 return false;
294 }
295
296 return etl::visit(detail::make_variant_compare_op(etl::less()), lhs, rhs);
297 }
298
299 /// Less-equal operator
300 ///
301 /// - If lhs.index() < rhs.index(), returns true;
302 /// - If lhs.index() > rhs.index(), returns false;
303 /// - Otherwise returns get<lhs.index()>(v) <= get<lhs.index()>(w)
304 friend constexpr auto operator<=(variant const& lhs, variant const& rhs) -> bool
305 {
306 if (lhs.index() < rhs.index()) {
307 return true;
308 }
309 if (lhs.index() > rhs.index()) {
310 return false;
311 }
312
313 return etl::visit(detail::make_variant_compare_op(etl::less_equal()), lhs, rhs);
314 }
315
316 /// Greater-than operator
317 ///
318 /// - If lhs.index() > rhs.index(), returns true;
319 /// - If lhs.index() < rhs.index(), returns false;
320 /// - Otherwise returns get<lhs.index()>(v) > get<lhs.index()>(w)
321 friend constexpr auto operator>(variant const& lhs, variant const& rhs) -> bool
322 {
323 if (lhs.index() > rhs.index()) {
324 return true;
325 }
326 if (lhs.index() < rhs.index()) {
327 return false;
328 }
329
330 return etl::visit(detail::make_variant_compare_op(etl::greater()), lhs, rhs);
331 }
332
333 /// Greater-equal operator
334 ///
335 /// - If lhs.index() > rhs.index(), returns true;
336 /// - If lhs.index() < rhs.index(), returns false;
337 /// - Otherwise returns get<lhs.index()>(v) >= get<lhs.index()>(w)
338 friend constexpr auto operator>=(variant const& lhs, variant const& rhs) -> bool
339 {
340 if (lhs.index() > rhs.index()) {
341 return true;
342 }
343 if (lhs.index() < rhs.index()) {
344 return false;
345 }
346
347 return etl::visit(detail::make_variant_compare_op(etl::greater_equal()), lhs, rhs);
348 }
349
350private:
351 struct copy_move_tag { };
352
353 template <typename Other>
354 constexpr variant(Other&& other, [[maybe_unused]] copy_move_tag tag)
355 : _index()
356 , _union(uninitialized_union())
357 {
358 etl::visit_with_index([&](auto param) {
359 replace(param.index, etl::move(param).value());
360 }, etl::forward<Other>(other));
361 }
362
363 template <typename Other>
364 constexpr auto assign(Other&& other) -> void
365 {
366 etl::visit_with_index([&](auto lhs, auto rhs) {
367 if constexpr (lhs.index == rhs.index) {
368 lhs.value() = etl::move(rhs.value());
369 } else {
370 destroy();
371 replace(rhs.index, etl::move(rhs.value()));
372 }
373 }, *this, etl::forward<Other>(other));
374 }
375
376 template <typename... Args>
377 constexpr auto replace(auto index, Args&&... args) -> auto&
378 {
379 etl::construct_at(etl::addressof(_union), index, etl::forward<Args>(args)...);
380 _index = static_cast<index_type>(index.value);
381 return (*this)[index];
382 }
383
384 constexpr auto destroy() -> void
385 {
386 etl::visit([](auto& v) { etl::destroy_at(etl::addressof(v)); }, *this);
387 }
388
389 TETL_NO_UNIQUE_ADDRESS index_type _index;
391};
392
393/// Checks if the variant v holds the alternative T. The call is
394/// ill-formed if T does not appear exactly once in Ts...
395/// \relates variant
396template <typename T, typename... Ts>
397constexpr auto holds_alternative(variant<Ts...> const& v) noexcept -> bool
398{
399 static_assert(mpl::count_v<T, mpl::list<Ts...>> == 1);
400 return v.index() == mpl::index_of_v<T, mpl::list<Ts...>>;
401}
402
403/// Returns a reference to the object stored in the variant.
404/// \pre v.index() == I
405/// \relates variant
406template <size_t I, typename... Ts>
407constexpr auto unchecked_get(variant<Ts...>& v) -> auto&
408{
409 static_assert(I < sizeof...(Ts));
410 TETL_PRECONDITION(I == v.index());
411 return v[index_v<I>];
412}
413
414/// Returns a reference to the object stored in the variant.
415/// \pre v.index() == I
416/// \relates variant
417template <size_t I, typename... Ts>
418constexpr auto unchecked_get(variant<Ts...> const& v) -> auto const&
419{
420 static_assert(I < sizeof...(Ts));
421 TETL_PRECONDITION(I == v.index());
422 return v[index_v<I>];
423}
424
425/// Returns a reference to the object stored in the variant.
426/// \pre v.index() == I
427/// \relates variant
428template <size_t I, typename... Ts>
429constexpr auto unchecked_get(variant<Ts...>&& v) -> auto&&
430{
431 static_assert(I < sizeof...(Ts));
432 TETL_PRECONDITION(I == v.index());
433 return etl::move(v)[index_v<I>];
434}
435
436/// Returns a reference to the object stored in the variant.
437/// \pre v.index() == I
438/// \relates variant
439template <size_t I, typename... Ts>
440constexpr auto unchecked_get(variant<Ts...> const&& v) -> auto const&&
441{
442 static_assert(I < sizeof...(Ts));
443 TETL_PRECONDITION(I == v.index());
444 return etl::move(v)[index_v<I>];
445}
446
447/// \brief If pv is not a null pointer and pv->index() == I, returns a pointer
448/// to the value stored in the variant pointed to by pv. Otherwise, returns
449/// a null pointer value. The call is ill-formed if I is not a valid index in the variant.
450/// \relates variant
451template <size_t I, typename... Ts>
452constexpr auto get_if(variant<Ts...>* pv) noexcept -> add_pointer_t<variant_alternative_t<I, variant<Ts...>>>
453{
454 static_assert(I < sizeof...(Ts));
455 if (pv == nullptr or pv->index() != I) {
456 return nullptr;
457 }
458 return etl::addressof(etl::unchecked_get<I>(*pv));
459}
460
461/// \brief If pv is not a null pointer and pv->index() == I, returns a pointer
462/// to the value stored in the variant pointed to by pv. Otherwise, returns
463/// a null pointer value. The call is ill-formed if I is not a valid index in the variant.
464/// \relates variant
465template <size_t I, typename... Ts>
466constexpr auto get_if(variant<Ts...> const* pv) noexcept
467 -> add_pointer_t<variant_alternative_t<I, variant<Ts...>> const>
468{
469 static_assert(I < sizeof...(Ts));
470 if (pv == nullptr or pv->index() != I) {
471 return nullptr;
472 }
473 return etl::addressof(etl::unchecked_get<I>(*pv));
474}
475
476/// Type-based non-throwing accessor: The call is ill-formed if T is not a unique element of Ts....
477/// \relates variant
478template <typename T, typename... Ts>
479constexpr auto get_if(variant<Ts...>* pv) noexcept -> add_pointer_t<T>
480{
481 return etl::get_if<mpl::index_of_v<T, mpl::list<Ts...>>>(pv);
482}
483
484/// Type-based non-throwing accessor: The call is ill-formed if T is not a unique element of Ts....
485/// \relates variant
486template <typename T, typename... Ts>
487constexpr auto get_if(variant<Ts...> const* pv) noexcept -> add_pointer_t<T const>
488{
489 return etl::get_if<mpl::index_of_v<T, mpl::list<Ts...>>>(pv);
490}
491
492} // namespace etl
493
494#endif // TETL_VARIANT_VARIANT2_HPP
Definition at.hpp:10
Definition adjacent_find.hpp:9
constexpr auto unchecked_get(variant< Ts... > const &&v) -> auto const &&
constexpr auto unchecked_get(variant< Ts... > &v) -> auto &
constexpr auto unchecked_get(variant< Ts... > &&v) -> auto &&
constexpr auto unchecked_get(variant< Ts... > const &v) -> auto const &
constexpr auto get_if(variant< Types... > *pv) noexcept -> add_pointer_t< T >
constexpr auto get_if(variant< Types... > const *pv) noexcept -> add_pointer_t< T const >
Function object for performing comparisons. Unless specialised, invokes operator== on type T....
Definition equal_to.hpp:15
Function object for performing comparisons. Unless specialised, invokes operator>= on type T....
Definition greater_equal.hpp:15
Function object for performing comparisons. Unless specialised, invokes operator> on type T....
Definition greater.hpp:15
Disambiguation tags that can be passed to the constructors of etl::optional, etl::variant,...
Definition in_place_index.hpp:20
Disambiguation tags that can be passed to the constructors of etl::optional, etl::variant,...
Definition in_place_type.hpp:20
Function object for performing comparisons. Unless specialised, invokes operator<= on type T....
Definition less_equal.hpp:15
Function object for performing comparisons. Unless specialised, invokes operator< on type T....
Definition less.hpp:15
Definition list.hpp:11
Definition uninitialized_union.hpp:9
Definition variant.hpp:99
constexpr variant(variant const &)=default
constexpr auto emplace(Args &&... args) -> auto &
Definition variant.hpp:264
constexpr auto get_if(variant< Ts... > *pv) noexcept -> add_pointer_t< variant_alternative_t< I, variant< Ts... > > >
If pv is not a null pointer and pv->index() == I, returns a pointer to the value stored in the varian...
Definition variant.hpp:452
constexpr variant(in_place_type_t< T >, Args &&... args)
Definition variant.hpp:137
friend constexpr auto operator==(variant const &lhs, variant const &rhs) -> bool
Equality operator.
Definition variant.hpp:274
friend constexpr auto operator<=(variant const &lhs, variant const &rhs) -> bool
Less-equal operator.
Definition variant.hpp:304
constexpr variant(in_place_index_t< I >, Args &&... args)
Definition variant.hpp:129
constexpr variant(T &&t) noexcept(is_nothrow_constructible_v< detail::variant_alternative_selector_t< T, Ts... >, T >)
Definition variant.hpp:122
constexpr auto operator=(T &&t) noexcept((is_nothrow_assignable_v< detail::variant_alternative_selector_t< T, Ts... > &, T > and is_nothrow_constructible_v< detail::variant_alternative_selector_t< T, Ts... >, T >)) -> variant &
Definition variant.hpp:190
constexpr auto operator[](index_constant< I > index) &-> auto &
Returns a reference to the object stored in the variant.
Definition variant.hpp:217
constexpr auto operator[](index_constant< I > index) &&-> auto &&
Returns a reference to the object stored in the variant.
Definition variant.hpp:237
constexpr auto operator=(variant &&) -> variant &=default
constexpr auto index() const noexcept -> size_t
Returns the zero-based index of the alternative that is currently held by the variant.
Definition variant.hpp:209
constexpr auto operator[](index_constant< I > index) const &-> auto const &
Returns a reference to the object stored in the variant.
Definition variant.hpp:227
friend constexpr auto operator<(variant const &lhs, variant const &rhs) -> bool
Less-than operator.
Definition variant.hpp:287
constexpr auto holds_alternative(variant< Ts... > const &v) noexcept -> bool
Checks if the variant v holds the alternative T. The call is ill-formed if T does not appear exactly ...
Definition variant.hpp:397
constexpr auto operator[](index_constant< I > index) const &&-> auto const &&
Returns a reference to the object stored in the variant.
Definition variant.hpp:247
constexpr auto operator=(variant const &) -> variant &=default
friend constexpr auto operator>=(variant const &lhs, variant const &rhs) -> bool
Greater-equal operator.
Definition variant.hpp:338
constexpr ~variant()
Definition variant.hpp:203
friend constexpr auto operator>(variant const &lhs, variant const &rhs) -> bool
Greater-than operator.
Definition variant.hpp:321
constexpr variant(variant &&)=default
constexpr auto get_if(variant< Ts... > const *pv) noexcept -> add_pointer_t< variant_alternative_t< I, variant< Ts... > > const >
If pv is not a null pointer and pv->index() == I, returns a pointer to the value stored in the varian...
Definition variant.hpp:466
constexpr auto emplace(Args &&... args) -> auto &
Definition variant.hpp:256
Definition variadic_union.hpp:19