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 // clang-format off
115 template <typename T>
116 requires (
117 (sizeof...(Ts) > 0)
118 and not is_same_v<remove_cvref_t<T>, variant>
119 and not detail::is_in_place_index<remove_cvref_t<T>>
120 and not detail::is_in_place_type<remove_cvref_t<T>>
121 and is_constructible_v<detail::variant_alternative_selector_t<T, Ts...>, T>
122 )
123 // clang-format on
125 : variant(in_place_type<detail::variant_alternative_selector_t<T, Ts...>>, etl::forward<T>(t))
126 {
127 }
128
129 template <size_t I, typename... Args>
130 requires((I < sizeof...(Ts)) and is_constructible_v<variant_alternative_t<I, variant>, Args...>)
131 explicit constexpr variant(in_place_index_t<I> /*index*/, Args&&... args)
132 : _index(static_cast<index_type>(I))
133 , _union(index_v<I>, etl::forward<Args>(args)...)
134 {
135 }
136
137 template <typename T, typename... Args>
138 requires(is_constructible_v<T, Args...> and mpl::count_v<remove_cvref_t<T>, mpl::list<Ts...>> == 1)
139 explicit constexpr variant(in_place_type_t<T> /*tag*/, Args&&... args)
140 : variant(in_place_index<mpl::index_of_v<T, mpl::list<Ts...>>>, etl::forward<Args>(args)...)
141 {
142 }
143
144 constexpr variant(variant const&) = default;
145
146 constexpr variant(variant const& other) noexcept((... and etl::is_nothrow_copy_constructible_v<Ts>))
147 requires((... and etl::is_copy_constructible_v<Ts>) and !(... and etl::is_trivially_copy_constructible_v<Ts>))
148 : variant(other, copy_move_tag{})
149 {
150 }
151
152 constexpr variant(variant&&) = default;
153
154 constexpr variant(variant&& other) noexcept((... and is_nothrow_move_constructible_v<Ts>))
155 requires((... and is_move_constructible_v<Ts>) and not(... and is_trivially_move_constructible_v<Ts>))
156 : variant(etl::move(other), copy_move_tag{})
157 {
158 }
159
160 constexpr auto operator=(variant const&) -> variant& = default;
161
162 constexpr auto operator=(variant const& other) noexcept(
164 ) -> variant&
165 requires(
166 (... and detail::variant_copy_assignable<Ts>) and not(... and detail::variant_trivially_copy_assignable<Ts>)
167 )
168 {
169 assign(other);
170 return *this;
171 }
172
173 constexpr auto operator=(variant&&) -> variant& = default;
174
175 constexpr auto operator=(variant&& other) noexcept(
177 ) -> variant&
178 requires(
179 (... and detail::variant_move_assignable<Ts>) and !(... and detail::variant_trivially_move_assignable<Ts>)
180 )
181 {
182 assign(etl::move(other));
183 return *this;
184 }
185
186 template <typename T>
187 requires(
188 not is_same_v<remove_cvref_t<T>, variant>
189 and is_assignable_v<detail::variant_alternative_selector_t<T, Ts...>&, T>
190 and is_assignable_v<detail::variant_alternative_selector_t<T, Ts...>, T>
191 )
192 constexpr auto operator=(T&& t) noexcept((
195 )) -> variant&
196 {
197 emplace<detail::variant_alternative_selector_t<T, Ts...>>(etl::forward<T>(t));
198 return *this;
199 }
200
201 ~variant()
202 requires(... and is_trivially_destructible_v<Ts>)
203 = default;
204
205 constexpr ~variant()
206 {
207 destroy();
208 }
209
210 /// Returns the zero-based index of the alternative that is currently held by the variant.
211 [[nodiscard]] constexpr auto index() const noexcept -> size_t
212 {
213 return static_cast<size_t>(_index);
214 }
215
216 /// Returns a reference to the object stored in the variant.
217 /// \pre I == index()
218 template <size_t I>
219 constexpr auto operator[](index_constant<I> index) & -> auto&
220 {
221 static_assert(I < sizeof...(Ts));
222 TETL_PRECONDITION(I == index());
223 return _union[index];
224 }
225
226 /// Returns a reference to the object stored in the variant.
227 /// \pre I == index()
228 template <size_t I>
229 constexpr auto operator[](index_constant<I> index) const& -> auto const&
230 {
231 static_assert(I < sizeof...(Ts));
232 TETL_PRECONDITION(I == index());
233 return _union[index];
234 }
235
236 /// Returns a reference to the object stored in the variant.
237 /// \pre I == index()
238 template <size_t I>
239 constexpr auto operator[](index_constant<I> index) && -> auto&&
240 {
241 static_assert(I < sizeof...(Ts));
242 TETL_PRECONDITION(I == index());
243 return etl::move(_union)[index];
244 }
245
246 /// Returns a reference to the object stored in the variant.
247 /// \pre I == index()
248 template <size_t I>
249 constexpr auto operator[](index_constant<I> index) const&& -> auto const&&
250 {
251 static_assert(I < sizeof...(Ts));
252 TETL_PRECONDITION(I == index());
253 return etl::move(_union)[index];
254 }
255
256 template <typename T, typename... Args>
257 requires(is_constructible_v<T, Args...> and mpl::count_v<T, mpl::list<Ts...>> == 1)
258 constexpr auto emplace(Args&&... args) -> auto&
259 {
260 destroy();
261 return replace(index_v<mpl::index_of_v<T, mpl::list<Ts...>>>, etl::forward<Args>(args)...);
262 }
263
264 template <size_t I, typename... Args>
265 requires is_constructible_v<mpl::at_t<I, mpl::list<Ts...>>, Args...>
266 constexpr auto emplace(Args&&... args) -> auto&
267 {
268 destroy();
269 return replace(index_v<I>, etl::forward<Args>(args)...);
270 }
271
272 /// Equality operator
273 ///
274 /// - If lhs.index() != rhs.index(), returns false;
275 /// - Otherwise returns get<lhs.index()>(lhs) == get<lhs.index()>(rhs)
276 friend constexpr auto operator==(variant const& lhs, variant const& rhs) -> bool
277 {
278 if (lhs.index() != rhs.index()) {
279 return false;
280 }
281 return etl::visit(detail::make_variant_compare_op(etl::equal_to()), lhs, rhs);
282 }
283
284 /// Less-than operator
285 ///
286 /// - If lhs.index() < rhs.index(), returns true;
287 /// - If lhs.index() > rhs.index(), returns false;
288 /// - Otherwise returns get<lhs.index()>(v) < get<lhs.index()>(w)
289 friend constexpr auto operator<(variant const& lhs, variant const& rhs) -> bool
290 {
291 if (lhs.index() < rhs.index()) {
292 return true;
293 }
294 if (lhs.index() > rhs.index()) {
295 return false;
296 }
297
298 return etl::visit(detail::make_variant_compare_op(etl::less()), lhs, rhs);
299 }
300
301 /// Less-equal operator
302 ///
303 /// - If lhs.index() < rhs.index(), returns true;
304 /// - If lhs.index() > rhs.index(), returns false;
305 /// - Otherwise returns get<lhs.index()>(v) <= get<lhs.index()>(w)
306 friend constexpr auto operator<=(variant const& lhs, variant const& rhs) -> bool
307 {
308 if (lhs.index() < rhs.index()) {
309 return true;
310 }
311 if (lhs.index() > rhs.index()) {
312 return false;
313 }
314
315 return etl::visit(detail::make_variant_compare_op(etl::less_equal()), lhs, rhs);
316 }
317
318 /// Greater-than operator
319 ///
320 /// - If lhs.index() > rhs.index(), returns true;
321 /// - If lhs.index() < rhs.index(), returns false;
322 /// - Otherwise returns get<lhs.index()>(v) > get<lhs.index()>(w)
323 friend constexpr auto operator>(variant const& lhs, variant const& rhs) -> bool
324 {
325 if (lhs.index() > rhs.index()) {
326 return true;
327 }
328 if (lhs.index() < rhs.index()) {
329 return false;
330 }
331
332 return etl::visit(detail::make_variant_compare_op(etl::greater()), lhs, rhs);
333 }
334
335 /// Greater-equal operator
336 ///
337 /// - If lhs.index() > rhs.index(), returns true;
338 /// - If lhs.index() < rhs.index(), returns false;
339 /// - Otherwise returns get<lhs.index()>(v) >= get<lhs.index()>(w)
340 friend constexpr auto operator>=(variant const& lhs, variant const& rhs) -> bool
341 {
342 if (lhs.index() > rhs.index()) {
343 return true;
344 }
345 if (lhs.index() < rhs.index()) {
346 return false;
347 }
348
349 return etl::visit(detail::make_variant_compare_op(etl::greater_equal()), lhs, rhs);
350 }
351
352private:
353 struct copy_move_tag { };
354
355 template <typename Other>
356 constexpr variant(Other&& other, [[maybe_unused]] copy_move_tag tag)
357 : _index()
358 , _union(uninitialized_union())
359 {
360 etl::visit_with_index([&](auto param) {
361 replace(param.index, etl::move(param).value());
362 }, etl::forward<Other>(other));
363 }
364
365 template <typename Other>
366 constexpr auto assign(Other&& other) -> void
367 {
368 etl::visit_with_index([&](auto lhs, auto rhs) {
369 if constexpr (lhs.index == rhs.index) {
370 lhs.value() = etl::move(rhs.value());
371 } else {
372 destroy();
373 replace(rhs.index, etl::move(rhs.value()));
374 }
375 }, *this, etl::forward<Other>(other));
376 }
377
378 template <typename... Args>
379 constexpr auto replace(auto index, Args&&... args) -> auto&
380 {
381 etl::construct_at(etl::addressof(_union), index, etl::forward<Args>(args)...);
382 _index = static_cast<index_type>(index.value);
383 return (*this)[index];
384 }
385
386 constexpr auto destroy() -> void
387 {
388 etl::visit([](auto& v) { etl::destroy_at(etl::addressof(v)); }, *this);
389 }
390
391 TETL_NO_UNIQUE_ADDRESS index_type _index;
393};
394
395/// Checks if the variant v holds the alternative T. The call is
396/// ill-formed if T does not appear exactly once in Ts...
397/// \relates variant
398template <typename T, typename... Ts>
399constexpr auto holds_alternative(variant<Ts...> const& v) noexcept -> bool
400{
401 static_assert(mpl::count_v<T, mpl::list<Ts...>> == 1);
402 return v.index() == mpl::index_of_v<T, mpl::list<Ts...>>;
403}
404
405/// Returns a reference to the object stored in the variant.
406/// \pre v.index() == I
407/// \relates variant
408template <size_t I, typename... Ts>
409constexpr auto unchecked_get(variant<Ts...>& v) -> auto&
410{
411 static_assert(I < sizeof...(Ts));
412 TETL_PRECONDITION(I == v.index());
413 return v[index_v<I>];
414}
415
416/// Returns a reference to the object stored in the variant.
417/// \pre v.index() == I
418/// \relates variant
419template <size_t I, typename... Ts>
420constexpr auto unchecked_get(variant<Ts...> const& v) -> auto const&
421{
422 static_assert(I < sizeof...(Ts));
423 TETL_PRECONDITION(I == v.index());
424 return v[index_v<I>];
425}
426
427/// Returns a reference to the object stored in the variant.
428/// \pre v.index() == I
429/// \relates variant
430template <size_t I, typename... Ts>
431constexpr auto unchecked_get(variant<Ts...>&& v) -> auto&&
432{
433 static_assert(I < sizeof...(Ts));
434 TETL_PRECONDITION(I == v.index());
435 return etl::move(v)[index_v<I>];
436}
437
438/// Returns a reference to the object stored in the variant.
439/// \pre v.index() == I
440/// \relates variant
441template <size_t I, typename... Ts>
442constexpr auto unchecked_get(variant<Ts...> const&& v) -> auto const&&
443{
444 static_assert(I < sizeof...(Ts));
445 TETL_PRECONDITION(I == v.index());
446 return etl::move(v)[index_v<I>];
447}
448
449/// \brief If pv is not a null pointer and pv->index() == I, returns a pointer
450/// to the value stored in the variant pointed to by pv. Otherwise, returns
451/// a null pointer value. The call is ill-formed if I is not a valid index in the variant.
452/// \relates variant
453template <size_t I, typename... Ts>
454constexpr auto get_if(variant<Ts...>* pv) noexcept -> add_pointer_t<variant_alternative_t<I, variant<Ts...>>>
455{
456 static_assert(I < sizeof...(Ts));
457 if (pv == nullptr or pv->index() != I) {
458 return nullptr;
459 }
460 return etl::addressof(etl::unchecked_get<I>(*pv));
461}
462
463/// \brief If pv is not a null pointer and pv->index() == I, returns a pointer
464/// to the value stored in the variant pointed to by pv. Otherwise, returns
465/// a null pointer value. The call is ill-formed if I is not a valid index in the variant.
466/// \relates variant
467template <size_t I, typename... Ts>
468constexpr auto get_if(variant<Ts...> const* pv) noexcept
469 -> add_pointer_t<variant_alternative_t<I, variant<Ts...>> const>
470{
471 static_assert(I < sizeof...(Ts));
472 if (pv == nullptr or pv->index() != I) {
473 return nullptr;
474 }
475 return etl::addressof(etl::unchecked_get<I>(*pv));
476}
477
478/// Type-based non-throwing accessor: The call is ill-formed if T is not a unique element of Ts....
479/// \relates variant
480template <typename T, typename... Ts>
481constexpr auto get_if(variant<Ts...>* pv) noexcept -> add_pointer_t<T>
482{
483 return etl::get_if<mpl::index_of_v<T, mpl::list<Ts...>>>(pv);
484}
485
486/// Type-based non-throwing accessor: The call is ill-formed if T is not a unique element of Ts....
487/// \relates variant
488template <typename T, typename... Ts>
489constexpr auto get_if(variant<Ts...> const* pv) noexcept -> add_pointer_t<T const>
490{
491 return etl::get_if<mpl::index_of_v<T, mpl::list<Ts...>>>(pv);
492}
493
494} // namespace etl
495
496#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:266
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:454
constexpr variant(in_place_type_t< T >, Args &&... args)
Definition variant.hpp:139
friend constexpr auto operator==(variant const &lhs, variant const &rhs) -> bool
Equality operator.
Definition variant.hpp:276
friend constexpr auto operator<=(variant const &lhs, variant const &rhs) -> bool
Less-equal operator.
Definition variant.hpp:306
constexpr variant(in_place_index_t< I >, Args &&... args)
Definition variant.hpp:131
constexpr variant(T &&t) noexcept(is_nothrow_constructible_v< detail::variant_alternative_selector_t< T, Ts... >, T >)
Definition variant.hpp:124
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:192
constexpr auto operator[](index_constant< I > index) &-> auto &
Returns a reference to the object stored in the variant.
Definition variant.hpp:219
constexpr auto operator[](index_constant< I > index) &&-> auto &&
Returns a reference to the object stored in the variant.
Definition variant.hpp:239
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:211
constexpr auto operator[](index_constant< I > index) const &-> auto const &
Returns a reference to the object stored in the variant.
Definition variant.hpp:229
friend constexpr auto operator<(variant const &lhs, variant const &rhs) -> bool
Less-than operator.
Definition variant.hpp:289
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:399
constexpr auto operator[](index_constant< I > index) const &&-> auto const &&
Returns a reference to the object stored in the variant.
Definition variant.hpp:249
constexpr auto operator=(variant const &) -> variant &=default
friend constexpr auto operator>=(variant const &lhs, variant const &rhs) -> bool
Greater-equal operator.
Definition variant.hpp:340
constexpr ~variant()
Definition variant.hpp:205
friend constexpr auto operator>(variant const &lhs, variant const &rhs) -> bool
Greater-than operator.
Definition variant.hpp:323
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:468
constexpr auto emplace(Args &&... args) -> auto &
Definition variant.hpp:258
Definition variadic_union.hpp:19