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
3#ifndef TETL_VARIANT_VARIANT2_HPP
4#define TETL_VARIANT_VARIANT2_HPP
5
6#include <etl/_config/all.hpp>
7
18#include <etl/_meta/at.hpp>
19#include <etl/_meta/count.hpp>
54
55namespace etl {
56
57namespace detail {
58
59template <typename T>
60concept variant_copy_assignable = is_copy_constructible_v<T> and is_copy_assignable_v<T>;
61
62template <typename T>
63concept variant_trivially_copy_assignable = is_trivially_copy_constructible_v<T> and is_trivially_copy_assignable_v<T>;
64
65template <typename T>
66concept variant_move_assignable = is_move_constructible_v<T> and is_move_assignable_v<T>;
67
68template <typename T>
69concept variant_trivially_move_assignable = is_trivially_move_constructible_v<T> and is_trivially_move_assignable_v<T>;
70
71template <typename T>
72inline constexpr auto is_in_place_index = false;
73
74template <size_t I>
75inline constexpr auto is_in_place_index<in_place_index_t<I>> = true;
76
77template <typename T>
78inline constexpr auto is_in_place_type = false;
79
80template <typename T>
81inline constexpr auto is_in_place_type<in_place_type_t<T>> = true;
82
83constexpr auto make_variant_compare_op(auto op)
84{
85 return [op](auto const& l, auto const& r) -> bool {
86 if constexpr (etl::is_same_v<decltype(l), decltype(r)>) {
87 return op(l, r);
88 } else {
90 }
91 };
92}
93
94} // namespace detail
95
97template <typename... Ts>
99private:
100 // Avoid valueless_by_exception
101 static_assert((is_nothrow_move_constructible_v<Ts> and ...));
102
103 using index_type = smallest_size_t<sizeof...(Ts)>;
104 using first_type = meta::at_t<0, meta::list<Ts...>>;
105
106public:
107 constexpr variant() noexcept(is_nothrow_default_constructible_v<first_type>)
108 requires(is_default_constructible_v<first_type>)
109 : variant(in_place_index<0>)
110 {
111 }
112
113 // clang-format off
114 template <typename T>
115 requires (
116 (sizeof...(Ts) > 0)
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 )
122 // clang-format on
123 constexpr variant(T&& t) noexcept(is_nothrow_constructible_v<detail::variant_alternative_selector_t<T, Ts...>, T>)
124 : variant(in_place_type<detail::variant_alternative_selector_t<T, Ts...>>, etl::forward<T>(t))
125 {
126 }
127
128 template <size_t I, typename... Args>
129 requires((I < sizeof...(Ts)) and is_constructible_v<variant_alternative_t<I, variant>, Args...>)
130 explicit constexpr variant(in_place_index_t<I> /*index*/, Args&&... args)
131 : _index(static_cast<index_type>(I))
132 , _union(index_v<I>, etl::forward<Args>(args)...)
133 {
134 }
135
136 template <typename T, typename... Args>
137 requires(is_constructible_v<T, Args...> and meta::count_v<remove_cvref_t<T>, meta::list<Ts...>> == 1)
138 explicit constexpr variant(in_place_type_t<T> /*tag*/, Args&&... args)
139 : variant(in_place_index<meta::index_of_v<T, meta::list<Ts...>>>, etl::forward<Args>(args)...)
140 {
141 }
142
143 constexpr variant(variant const&) = default;
144
145 constexpr variant(variant const& other) noexcept((... and etl::is_nothrow_copy_constructible_v<Ts>))
147 : variant(other, copy_move_tag{})
148 {
149 }
150
151 constexpr variant(variant&&) = default;
152
153 constexpr variant(variant&& other) noexcept((... and is_nothrow_move_constructible_v<Ts>))
154 requires((... and is_move_constructible_v<Ts>) and not(... and is_trivially_move_constructible_v<Ts>))
155 : variant(etl::move(other), copy_move_tag{})
156 {
157 }
158
159 constexpr auto operator=(variant const&) -> variant& = default;
160
161 constexpr auto operator=(variant const& other
163 -> variant& requires(
164 (... and detail::variant_copy_assignable<Ts>) and not(... and detail::variant_trivially_copy_assignable<Ts>)
165 ) {
166 assign(other);
167 return *this;
168 }
169
170 constexpr auto operator=(variant&&) -> variant& = default;
171
172 constexpr auto operator=(variant&& other
174 -> variant& requires(
175 (... and detail::variant_move_assignable<Ts>) and !(... and detail::variant_trivially_move_assignable<Ts>)
176 ) {
177 assign(etl::move(other));
178 return *this;
179 }
180
181 template <typename T>
182 requires(not is_same_v<remove_cvref_t<T>, variant> and is_assignable_v<detail::variant_alternative_selector_t<T, Ts...>&, T> and is_assignable_v<detail::variant_alternative_selector_t<T, Ts...>, T>)
183 constexpr auto operator=(T&& t) noexcept(
184 (is_nothrow_assignable_v<detail::variant_alternative_selector_t<T, Ts...>&, T>
185 and is_nothrow_constructible_v<detail::variant_alternative_selector_t<T, Ts...>, T>)
186 ) -> variant&
187 {
188 emplace<detail::variant_alternative_selector_t<T, Ts...>>(etl::forward<T>(t));
189 return *this;
190 }
191
193 requires(... and is_trivially_destructible_v<Ts>)
194 = default;
195
196 constexpr ~variant() { destroy(); }
197
199 [[nodiscard]] constexpr auto index() const noexcept -> size_t { return static_cast<size_t>(_index); }
200
203 template <size_t I>
204 constexpr auto operator[](index_constant<I> index) & -> auto&
205 {
206 static_assert(I < sizeof...(Ts));
207 TETL_PRECONDITION(I == index());
208 return _union[index];
209 }
210
213 template <size_t I>
214 constexpr auto operator[](index_constant<I> index) const& -> auto const&
215 {
216 static_assert(I < sizeof...(Ts));
217 TETL_PRECONDITION(I == index());
218 return _union[index];
219 }
220
223 template <size_t I>
224 constexpr auto operator[](index_constant<I> index) && -> auto&&
225 {
226 static_assert(I < sizeof...(Ts));
227 TETL_PRECONDITION(I == index());
228 return etl::move(_union)[index];
229 }
230
233 template <size_t I>
234 constexpr auto operator[](index_constant<I> index) const&& -> auto const&&
235 {
236 static_assert(I < sizeof...(Ts));
237 TETL_PRECONDITION(I == index());
238 return etl::move(_union)[index];
239 }
240
241 template <typename T, typename... Args>
242 requires(is_constructible_v<T, Args...> and meta::count_v<T, meta::list<Ts...>> == 1)
243 constexpr auto emplace(Args&&... args) -> auto&
244 {
245 destroy();
246 return replace(index_v<meta::index_of_v<T, meta::list<Ts...>>>, etl::forward<Args>(args)...);
247 }
248
249 template <size_t I, typename... Args>
250 requires is_constructible_v<meta::at_t<I, meta::list<Ts...>>, Args...>
251 constexpr auto emplace(Args&&... args) -> auto&
252 {
253 destroy();
254 return replace(index_v<I>, etl::forward<Args>(args)...);
255 }
256
261 friend constexpr auto operator==(variant const& lhs, variant const& rhs) -> bool
262 {
263 if (lhs.index() != rhs.index()) {
264 return false;
265 }
266 return etl::visit(detail::make_variant_compare_op(etl::equal_to()), lhs, rhs);
267 }
268
274 friend constexpr auto operator<(variant const& lhs, variant const& rhs) -> bool
275 {
276 if (lhs.index() < rhs.index()) {
277 return true;
278 }
279 if (lhs.index() > rhs.index()) {
280 return false;
281 }
282
283 return etl::visit(detail::make_variant_compare_op(etl::less()), lhs, rhs);
284 }
285
291 friend constexpr auto operator<=(variant const& lhs, variant const& rhs) -> bool
292 {
293 if (lhs.index() < rhs.index()) {
294 return true;
295 }
296 if (lhs.index() > rhs.index()) {
297 return false;
298 }
299
300 return etl::visit(detail::make_variant_compare_op(etl::less_equal()), lhs, rhs);
301 }
302
308 friend constexpr auto operator>(variant const& lhs, variant const& rhs) -> bool
309 {
310 if (lhs.index() > rhs.index()) {
311 return true;
312 }
313 if (lhs.index() < rhs.index()) {
314 return false;
315 }
316
317 return etl::visit(detail::make_variant_compare_op(etl::greater()), lhs, rhs);
318 }
319
325 friend constexpr auto operator>=(variant const& lhs, variant const& rhs) -> bool
326 {
327 if (lhs.index() > rhs.index()) {
328 return true;
329 }
330 if (lhs.index() < rhs.index()) {
331 return false;
332 }
333
334 return etl::visit(detail::make_variant_compare_op(etl::greater_equal()), lhs, rhs);
335 }
336
337private:
338 struct copy_move_tag { };
339
340 template <typename Other>
341 constexpr variant(Other&& other, [[maybe_unused]] copy_move_tag tag)
342 : _index()
343 , _union(uninitialized_union())
344 {
345 etl::visit_with_index([&](auto param) {
346 replace(param.index, etl::move(param).value());
347 }, etl::forward<Other>(other));
348 }
349
350 template <typename Other>
351 constexpr auto assign(Other&& other) -> void
352 {
353 etl::visit_with_index([&](auto lhs, auto rhs) {
354 if constexpr (lhs.index == rhs.index) {
355 lhs.value() = etl::move(rhs.value());
356 } else {
357 destroy();
358 replace(rhs.index, etl::move(rhs.value()));
359 }
360 }, *this, etl::forward<Other>(other));
361 }
362
363 template <typename... Args>
364 constexpr auto replace(auto index, Args&&... args) -> auto&
365 {
366 etl::construct_at(etl::addressof(_union), index, etl::forward<Args>(args)...);
367 _index = static_cast<index_type>(index.value);
368 return (*this)[index];
369 }
370
371 constexpr auto destroy() -> void
372 {
373 etl::visit([](auto& v) { etl::destroy_at(etl::addressof(v)); }, *this);
374 }
375
376 TETL_NO_UNIQUE_ADDRESS index_type _index;
377 TETL_NO_UNIQUE_ADDRESS variadic_union<Ts...> _union;
378};
379
383template <typename T, typename... Ts>
384constexpr auto holds_alternative(variant<Ts...> const& v) noexcept -> bool
385{
386 static_assert(meta::count_v<T, meta::list<Ts...>> == 1);
387 return v.index() == meta::index_of_v<T, meta::list<Ts...>>;
388}
389
393template <size_t I, typename... Ts>
394constexpr auto unchecked_get(variant<Ts...>& v) -> auto&
395{
396 static_assert(I < sizeof...(Ts));
397 TETL_PRECONDITION(I == v.index());
398 return v[index_v<I>];
399}
400
404template <size_t I, typename... Ts>
405constexpr auto unchecked_get(variant<Ts...> const& v) -> auto const&
406{
407 static_assert(I < sizeof...(Ts));
408 TETL_PRECONDITION(I == v.index());
409 return v[index_v<I>];
410}
411
415template <size_t I, typename... Ts>
416constexpr auto unchecked_get(variant<Ts...>&& v) -> auto&&
417{
418 static_assert(I < sizeof...(Ts));
419 TETL_PRECONDITION(I == v.index());
420 return etl::move(v)[index_v<I>];
421}
422
426template <size_t I, typename... Ts>
427constexpr auto unchecked_get(variant<Ts...> const&& v) -> auto const&&
428{
429 static_assert(I < sizeof...(Ts));
430 TETL_PRECONDITION(I == v.index());
431 return etl::move(v)[index_v<I>];
432}
433
438template <size_t I, typename... Ts>
439constexpr auto get_if(variant<Ts...>* pv) noexcept -> add_pointer_t<variant_alternative_t<I, variant<Ts...>>>
440{
441 static_assert(I < sizeof...(Ts));
442 if (pv == nullptr or pv->index() != I) {
443 return nullptr;
444 }
446}
447
452template <size_t I, typename... Ts>
453constexpr auto get_if(variant<Ts...> const* pv) noexcept
454 -> add_pointer_t<variant_alternative_t<I, variant<Ts...>> const>
455{
456 static_assert(I < sizeof...(Ts));
457 if (pv == nullptr or pv->index() != I) {
458 return nullptr;
459 }
461}
462
465template <typename T, typename... Ts>
466constexpr auto get_if(variant<Ts...>* pv) noexcept -> add_pointer_t<T>
467{
468 return etl::get_if<meta::index_of_v<T, meta::list<Ts...>>>(pv);
469}
470
473template <typename T, typename... Ts>
474constexpr auto get_if(variant<Ts...> const* pv) noexcept -> add_pointer_t<T const>
475{
476 return etl::get_if<meta::index_of_v<T, meta::list<Ts...>>>(pv);
477}
478
479} // namespace etl
480
481#endif // TETL_VARIANT_VARIANT2_HPP
#define TETL_TRIVIAL_ABI
Definition attributes.hpp:47
#define TETL_NO_UNIQUE_ADDRESS
Definition attributes.hpp:41
#define TETL_PRECONDITION(...)
Definition check.hpp:16
constexpr auto replace(ForwardIt first, ForwardIt last, T const &oldValue, T const &newValue) -> void
Replaces all elements satisfying specific criteria with new_value in the range [first,...
Definition replace.hpp:15
constexpr auto move(InputIt first, InputIt last, OutputIt destination) -> OutputIt
Moves the elements in the range [first, last), to another range beginning at destination,...
Definition move.hpp:26
conditional_t<(N< static_cast< unsigned char >(-1)), unsigned char, conditional_t<(N< static_cast< unsigned short >(-1)), unsigned short, conditional_t<(N< static_cast< unsigned int >(-1)), unsigned int, conditional_t<(N< static_cast< unsigned long >(-1)), unsigned long, unsigned long long > > > > smallest_size_t
Smallest unsigned integer type that can represent values in the range [0, N].
Definition smallest_size_t.hpp:13
constexpr auto visit(F &&f, Vs &&... vs)
Applies the visitor vis (Callable that can be called with any combination of types from variants) to ...
Definition visit.hpp:177
constexpr auto visit_with_index(F &&f, Vs &&... vs)
Applies the visitor vis (Callable that can be called with any combination of types from variants) to ...
Definition visit.hpp:149
Definition at.hpp:9
constexpr auto count_v
Definition count.hpp:20
constexpr auto index_of_v
Definition index_of.hpp:16
typename at< I, List >::type at_t
Definition at.hpp:25
constexpr struct etl::ranges::destroy_fn destroy
Definition adjacent_find.hpp:8
constexpr bool is_copy_constructible_v
Definition is_copy_constructible.hpp:30
integral_constant< size_t, I > index_constant
Definition index_constant.hpp:12
constexpr bool is_constructible_v
Definition is_constructible.hpp:24
constexpr bool is_nothrow_copy_constructible_v
Definition is_nothrow_copy_constructible.hpp:27
constexpr auto addressof(T &arg) noexcept -> T *
Obtains the actual address of the object or function arg, even in presence of overloaded operator&.
Definition addressof.hpp:15
constexpr bool is_nothrow_default_constructible_v
Definition is_nothrow_default_constructible.hpp:26
constexpr bool is_nothrow_copy_assignable_v
Definition is_nothrow_copy_assignable.hpp:28
constexpr bool is_nothrow_move_constructible_v
Definition is_nothrow_move_constructible.hpp:20
constexpr auto index_v
Definition index_constant.hpp:15
constexpr bool is_move_constructible_v
Definition is_move_constructible.hpp:20
constexpr bool is_trivially_move_assignable_v
Definition is_trivially_move_assignable.hpp:29
constexpr bool is_trivially_copy_assignable_v
Definition is_trivially_copy_assignable.hpp:29
constexpr bool is_default_constructible_v
Definition is_default_constructible.hpp:26
constexpr bool is_trivially_destructible_v
Definition is_trivially_destructible.hpp:33
constexpr bool is_assignable_v
Definition is_assignable.hpp:20
typename variant_alternative< I, T >::type variant_alternative_t
Definition variant_alternative.hpp:21
constexpr auto construct_at(T *p, Args &&... args) -> T *
Creates a T object initialized with arguments args... at given address p.
Definition construct_at.hpp:38
constexpr auto unchecked_get(variant< Ts... > &v) -> auto &
constexpr bool is_trivially_copy_constructible_v
Definition is_trivially_copy_constructible.hpp:27
constexpr bool is_same_v
Definition is_same.hpp:11
constexpr auto is_trivially_move_constructible_v
Definition is_trivially_move_constructible.hpp:20
auto unreachable() -> void
Definition unreachable.hpp:10
constexpr bool is_nothrow_constructible_v
Definition is_nothrow_constructible.hpp:50
constexpr bool is_nothrow_assignable_v
Definition is_nothrow_assignable.hpp:24
constexpr bool is_nothrow_move_assignable_v
Definition is_nothrow_move_assignable.hpp:28
constexpr auto get_if(variant< Types... > *pv) noexcept -> add_pointer_t< T >
constexpr auto destroy_at(T *p) -> void
If T is not an array type, calls the destructor of the object pointed to by p, as if by p->~T()....
Definition destroy_at.hpp:16
constexpr auto forward(remove_reference_t< T > &param) noexcept -> T &&
Forwards lvalues as either lvalues or as rvalues, depending on T. When t is a forwarding reference (a...
Definition forward.hpp:18
constexpr bool is_move_assignable_v
Definition is_move_assignable.hpp:29
constexpr bool is_copy_assignable_v
Definition is_copy_assignable.hpp:28
Function object for performing comparisons. Unless specialised, invokes operator== on type T....
Definition equal_to.hpp:14
Function object for performing comparisons. Unless specialised, invokes operator>= on type T....
Definition greater_equal.hpp:14
Function object for performing comparisons. Unless specialised, invokes operator> on type T....
Definition greater.hpp:14
Disambiguation tags that can be passed to the constructors of etl::optional, etl::variant,...
Definition in_place_index.hpp:19
Disambiguation tags that can be passed to the constructors of etl::optional, etl::variant,...
Definition in_place_type.hpp:19
Function object for performing comparisons. Unless specialised, invokes operator<= on type T....
Definition less_equal.hpp:14
Function object for performing comparisons. Unless specialised, invokes operator< on type T....
Definition less.hpp:14
Definition list.hpp:10
Definition variant.hpp:98
constexpr variant(variant const &)=default
constexpr variant(in_place_type_t< T >, Args &&... args)
Definition variant.hpp:138
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:439
~variant()=default
friend constexpr auto operator==(variant const &lhs, variant const &rhs) -> bool
Equality operator.
Definition variant.hpp:261
constexpr auto emplace(Args &&... args) -> auto &
Definition variant.hpp:251
friend constexpr auto operator<=(variant const &lhs, variant const &rhs) -> bool
Less-equal operator.
Definition variant.hpp:291
constexpr variant(in_place_index_t< I >, Args &&... args)
Definition variant.hpp:130
constexpr auto unchecked_get(variant< Ts... > const &&v) -> auto const &&
Returns a reference to the object stored in the variant.
Definition variant.hpp:427
constexpr auto operator=(variant &&other) noexcept((... and is_nothrow_move_assignable_v< Ts >) and(... and is_nothrow_move_constructible_v< Ts >)) -> variant &requires((... and detail::variant_move_assignable< Ts >) and !(... and detail::variant_trivially_move_assignable< Ts >))
Definition variant.hpp:172
constexpr auto get_if(variant< Ts... > *pv) noexcept -> add_pointer_t< T >
Type-based non-throwing accessor: The call is ill-formed if T is not a unique element of Ts....
Definition variant.hpp:466
constexpr variant(T &&t) noexcept(is_nothrow_constructible_v< detail::variant_alternative_selector_t< T, Ts... >, T >)
Definition variant.hpp:123
constexpr auto operator[](index_constant< I > index) &-> auto &
Returns a reference to the object stored in the variant.
Definition variant.hpp:204
constexpr auto operator=(variant const &other) noexcept((... and is_nothrow_copy_assignable_v< Ts >) and(... and is_nothrow_copy_constructible_v< Ts >)) -> variant &requires((... and detail::variant_copy_assignable< Ts >) and not(... and detail::variant_trivially_copy_assignable< Ts >))
Definition variant.hpp:161
constexpr auto unchecked_get(variant< Ts... > &v) -> auto &
Returns a reference to the object stored in the variant.
Definition variant.hpp:394
constexpr auto operator[](index_constant< I > index) &&-> auto &&
Returns a reference to the object stored in the variant.
Definition variant.hpp:224
constexpr auto operator=(variant &&) -> variant &=default
constexpr auto unchecked_get(variant< Ts... > &&v) -> auto &&
Returns a reference to the object stored in the variant.
Definition variant.hpp:416
constexpr auto emplace(Args &&... args) -> auto &
Definition variant.hpp:243
constexpr variant(variant &&other) noexcept((... and is_nothrow_move_constructible_v< Ts >))
Definition variant.hpp:153
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:199
constexpr auto get_if(variant< Ts... > const *pv) noexcept -> add_pointer_t< T const >
Type-based non-throwing accessor: The call is ill-formed if T is not a unique element of Ts....
Definition variant.hpp:474
constexpr auto operator[](index_constant< I > index) const &-> auto const &
Returns a reference to the object stored in the variant.
Definition variant.hpp:214
friend constexpr auto operator<(variant const &lhs, variant const &rhs) -> bool
Less-than operator.
Definition variant.hpp:274
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:384
constexpr auto operator[](index_constant< I > index) const &&-> auto const &&
Returns a reference to the object stored in the variant.
Definition variant.hpp:234
constexpr auto operator=(variant const &) -> variant &=default
constexpr auto unchecked_get(variant< Ts... > const &v) -> auto const &
Returns a reference to the object stored in the variant.
Definition variant.hpp:405
friend constexpr auto operator>=(variant const &lhs, variant const &rhs) -> bool
Greater-equal operator.
Definition variant.hpp:325
constexpr ~variant()
Definition variant.hpp:196
constexpr variant() noexcept(is_nothrow_default_constructible_v< first_type >)
Definition variant.hpp:107
friend constexpr auto operator>(variant const &lhs, variant const &rhs) -> bool
Greater-than operator.
Definition variant.hpp:308
constexpr variant(variant const &other) noexcept((... and etl::is_nothrow_copy_constructible_v< Ts >))
Definition variant.hpp:145
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:453