join 1.0
lightweight network framework library
Loading...
Searching...
No Matches
variant.hpp
Go to the documentation of this file.
1
25#ifndef JOIN_CORE_VARIANT_HPP
26#define JOIN_CORE_VARIANT_HPP
27
28// libjoin.
29#include <join/traits.hpp>
30#include <join/utils.hpp>
31
32// C++.
33#include <algorithm>
34#include <typeinfo>
35#include <compare>
36#include <tuple>
37
38// C.
39#include <cstddef>
40#include <cstdint>
41
42namespace join
43{
44 namespace details
45 {
49 template <typename T>
50 struct OrderingOf : std::compare_three_way_result<T>
51 {
52 };
53
57 template <>
58 struct OrderingOf<std::nullptr_t>
59 {
60 using type = std::strong_ordering;
61 };
62
66 template <typename... Ts>
67 using VariantOrdering = std::common_comparison_category_t<typename OrderingOf<Ts>::type...>;
68
72 template <typename... Ts>
74 {
75 public:
82 static void copy (std::size_t which, const void* src, void* dst)
83 {
84 static const CopyFunc table[] = {&copyImpl<Ts>...};
85 table[which](src, dst);
86 }
87
94 static void move (std::size_t which, void* src, void* dst)
95 {
96 static const MoveFunc table[] = {&moveImpl<Ts>...};
97 table[which](src, dst);
98 }
99
105 static void destroy (std::size_t which, void* data)
106 {
107 static const DestroyFunc table[] = {&destroyImpl<Ts>...};
108 table[which](data);
109 }
110
118 static bool equal (std::size_t which, const void* a, const void* b)
119 {
120 static const EqualFunc table[] = {&equalImpl<Ts>...};
121 return table[which](a, b);
122 }
123
131 template <typename Ordering>
132 static Ordering compare (std::size_t which, const void* a, const void* b)
133 {
134 static const CompareFunc<Ordering> table[] = {&compareImpl<Ordering, Ts>...};
135 return table[which](a, b);
136 }
137
138 private:
140 using CopyFunc = void (*) (const void*, void*);
141
143 using MoveFunc = void (*) (void*, void*);
144
146 using DestroyFunc = void (*) (void*);
147
149 using EqualFunc = bool (*) (const void*, const void*);
150
152 template <typename Ordering>
153 using CompareFunc = Ordering (*) (const void*, const void*);
154
160 template <typename T>
161 static void copyImpl (const void* src, void* dst)
162 {
163 new (dst) T (*reinterpret_cast<const T*> (src));
164 }
165
171 template <typename T>
172 static void moveImpl (void* src, void* dst)
173 {
174 new (dst) T (std::move (*reinterpret_cast<T*> (src)));
175 }
176
181 template <typename T>
182 static void destroyImpl (void* data)
183 {
184 reinterpret_cast<T*> (data)->~T ();
185 }
186
193 template <typename T>
194 static bool equalImpl (const void* a, const void* b)
195 {
196 return *reinterpret_cast<const T*> (a) == *reinterpret_cast<const T*> (b);
197 }
198
205 template <typename Ordering, typename T>
206 static Ordering compareImpl (const void* a, const void* b)
207 {
208 if constexpr (std::is_null_pointer_v<T>)
209 {
210 return std::strong_ordering::equivalent;
211 }
212 else
213 {
214 return *reinterpret_cast<const T*> (a) <=> *reinterpret_cast<const T*> (b);
215 }
216 }
217 };
218 }
219
223 template <typename... Ts>
225 {
227 static constexpr bool _trivial = (std::is_trivially_copyable_v<Ts> && ...);
228
230 static constexpr bool _defaultConstructible =
231 std::is_default_constructible_v<std::tuple_element_t<0, std::tuple<Ts...>>>;
232
234 static constexpr bool _copyConstructible = (std::is_copy_constructible_v<Ts> && ...);
235
237 static constexpr bool _moveConstructible = (std::is_move_constructible_v<Ts> && ...);
238
240 static constexpr bool _copyAssignable =
241 _copyConstructible && _moveConstructible && (std::is_copy_assignable_v<Ts> && ...);
242
244 static constexpr bool _moveAssignable = _moveConstructible && (std::is_move_assignable_v<Ts> && ...);
245
246 public:
247 static_assert (0 < sizeof...(Ts), "Variant must have at least one alternative");
248 static_assert ((!std::is_void_v<Ts> && ...), "Variant must have no void alternative");
249 static_assert ((!std::is_array_v<Ts> && ...), "Variant must have no array alternative.");
250 static_assert ((!std::is_reference_v<Ts> && ...), "Variant must have no reference alternative");
251
255 constexpr Variant ()
256 requires _defaultConstructible
257 : Variant (std::in_place_index<0>)
258 {
259 }
260
265 constexpr Variant (const Variant& other)
266 requires (_trivial && _copyConstructible)
267 = default;
268
273 constexpr Variant (const Variant& other)
274 requires (!_trivial && _copyConstructible)
275 : _which (other._which)
276 {
277 details::VariantHelper<Ts...>::copy (other._which, other.storage (), storage ());
278 }
279
284 constexpr Variant (Variant&& other)
285 requires (_trivial && _moveConstructible)
286 = default;
287
292 constexpr Variant (Variant&& other) noexcept
293 requires (!_trivial && _moveConstructible)
294 : _which (other._which)
295 {
296 details::VariantHelper<Ts...>::move (other._which, other.storage (), storage ());
297 }
298
303 template <typename T, typename Match = match_t<T&&, Ts...>>
304 constexpr Variant (T&& t)
305 requires is_unique_v<Match, Ts...> && std::is_constructible_v<Match, T&&>
306 : Variant (std::in_place_index<find_index_v<Match, Ts...>>, std::forward<T> (t))
307 {
308 }
309
314 template <typename T, typename... Args>
315 constexpr explicit Variant (std::in_place_type_t<T>, Args&&... args)
316 requires is_unique_v<T, Ts...> && std::is_constructible_v<T, Args&&...>
317 : Variant (std::in_place_index<find_index_v<T, Ts...>>, std::forward<Args> (args)...)
318 {
319 }
320
325 template <typename T, typename Up, typename... Args>
326 constexpr explicit Variant (std::in_place_type_t<T>, std::initializer_list<Up> il, Args&&... args)
327 requires is_unique_v<T, Ts...> && std::is_constructible_v<T, std::initializer_list<Up>&, Args&&...>
328 : Variant (std::in_place_index<find_index_v<T, Ts...>>, il, std::forward<Args> (args)...)
329 {
330 }
331
336 template <std::size_t I, typename... Args>
337 constexpr explicit Variant (std::in_place_index_t<I>, Args&&... args)
338 requires std::is_constructible_v<std::tuple_element_t<I, std::tuple<Ts...>>, Args&&...>
339 : _which (I)
340 {
341 new (storage ()) std::tuple_element_t<I, std::tuple<Ts...>> (std::forward<Args> (args)...);
342 }
343
348 template <std::size_t I, typename Up, typename... Args>
349 constexpr explicit Variant (std::in_place_index_t<I>, std::initializer_list<Up> il, Args&&... args)
350 requires std::is_constructible_v<std::tuple_element_t<I, std::tuple<Ts...>>, std::initializer_list<Up>&,
351 Args&&...>
352 : _which (I)
353 {
354 new (storage ()) std::tuple_element_t<I, std::tuple<Ts...>> (il, std::forward<Args> (args)...);
355 }
356
361 requires _trivial
362 = default;
363
368 requires (!_trivial)
369 {
370 details::VariantHelper<Ts...>::destroy (_which, storage ());
371 }
372
378 constexpr Variant& operator= (const Variant& other)
379 requires (_trivial && _copyAssignable)
380 = default;
381
387 constexpr Variant& operator= (const Variant& other)
388 requires (!_trivial && _copyAssignable)
389 {
390 Variant tmp (other);
391 swap (tmp);
392 return *this;
393 }
394
400 constexpr Variant& operator= (Variant&& other)
401 requires (_trivial && _moveAssignable)
402 = default;
403
409 constexpr Variant& operator= (Variant&& other) noexcept
410 requires (!_trivial && _moveAssignable)
411 {
412 swap (other);
413 return *this;
414 }
415
421 template <typename T, typename Match = match_t<T&&, Ts...>>
422 constexpr Variant& operator= (T&& t)
423 requires is_unique_v<Match, Ts...> && std::is_constructible_v<Match, T&&>
424 {
425 set<find_index_v<Match, Ts...>> (std::forward<T> (t));
426 return *this;
427 }
428
433 template <typename T>
434 constexpr bool is () const
435 requires is_unique_v<T, Ts...>
436 {
437 return (index () == find_index_v<T, Ts...>);
438 }
439
444 template <std::size_t I>
445 constexpr bool is () const
446 requires (I < sizeof...(Ts))
447 {
448 return (index () == I);
449 }
450
455 template <typename T, typename... Args>
456 constexpr T& set (Args&&... args)
457 requires is_unique_v<T, Ts...> && std::is_constructible_v<T, Args&&...>
458 {
459 return set<find_index_v<T, Ts...>> (std::forward<Args> (args)...);
460 }
461
466 template <typename T, typename Up, typename... Args>
467 constexpr T& set (std::initializer_list<Up> il, Args&&... args)
468 requires is_unique_v<T, Ts...> && std::is_constructible_v<T, std::initializer_list<Up>&, Args&&...>
469 {
470 return set<find_index_v<T, Ts...>> (il, std::forward<Args> (args)...);
471 }
472
477 template <std::size_t I, typename... Args>
478 constexpr std::tuple_element_t<I, std::tuple<Ts...>>& set (Args&&... args)
479 requires std::is_constructible_v<std::tuple_element_t<I, std::tuple<Ts...>>, Args&&...>
480 {
481 Variant tmp (std::in_place_index<I>, std::forward<Args> (args)...);
482 *this = std::move (tmp);
483 return get<I> ();
484 }
485
490 template <std::size_t I, class Up, class... Args>
491 constexpr std::tuple_element_t<I, std::tuple<Ts...>>& set (std::initializer_list<Up> il, Args&&... args)
492 requires std::is_constructible_v<std::tuple_element_t<I, std::tuple<Ts...>>, std::initializer_list<Up>&,
493 Args&&...>
494 {
495 Variant tmp (std::in_place_index<I>, il, std::forward<Args> (args)...);
496 *this = std::move (tmp);
497 return get<I> ();
498 }
499
504 template <typename T>
505 constexpr T& get ()
506 requires is_unique_v<T, Ts...>
507 {
509 {
510 throwException<std::bad_cast> ();
511 }
512 return *reinterpret_cast<T*> (storage ());
513 }
514
519 template <typename T>
520 constexpr const T& get () const
521 requires is_unique_v<T, Ts...>
522 {
524 {
525 throwException<std::bad_cast> ();
526 }
527 return *reinterpret_cast<const T*> (storage ());
528 }
529
534 template <std::size_t I>
535 constexpr std::tuple_element_t<I, std::tuple<Ts...>>& get ()
536 requires (I < sizeof...(Ts))
537 {
538 if (index () != I)
539 {
540 throwException<std::bad_cast> ();
541 }
542 return *reinterpret_cast<std::tuple_element_t<I, std::tuple<Ts...>>*> (storage ());
543 }
544
549 template <std::size_t I>
550 constexpr const std::tuple_element_t<I, std::tuple<Ts...>>& get () const
551 requires (I < sizeof...(Ts))
552 {
553 if (index () != I)
554 {
555 throwException<std::bad_cast> ();
556 }
557 return *reinterpret_cast<const std::tuple_element_t<I, std::tuple<Ts...>>*> (storage ());
558 }
559
564 template <typename T>
565 constexpr T* getIf ()
566 requires is_unique_v<T, Ts...>
567 {
569 {
570 return nullptr;
571 }
572 return reinterpret_cast<T*> (storage ());
573 }
574
579 template <typename T>
580 constexpr const T* getIf () const
581 requires is_unique_v<T, Ts...>
582 {
584 {
585 return nullptr;
586 }
587 return reinterpret_cast<const T*> (storage ());
588 }
589
594 template <std::size_t I>
595 constexpr std::tuple_element_t<I, std::tuple<Ts...>>* getIf ()
596 requires (I < sizeof...(Ts))
597 {
598 if (index () != I)
599 {
600 return nullptr;
601 }
602 return reinterpret_cast<std::tuple_element_t<I, std::tuple<Ts...>>*> (storage ());
603 }
604
609 template <std::size_t I>
610 constexpr const std::tuple_element_t<I, std::tuple<Ts...>>* getIf () const
611 requires (I < sizeof...(Ts))
612 {
613 if (index () != I)
614 {
615 return nullptr;
616 }
617 return reinterpret_cast<const std::tuple_element_t<I, std::tuple<Ts...>>*> (storage ());
618 }
619
624 constexpr std::size_t index () const noexcept
625 {
626 if (_which >= sizeof...(Ts))
627 {
628 __builtin_unreachable ();
629 }
630 return _which;
631 }
632
637 void swap (Variant& other) noexcept
638 {
639 if constexpr (_trivial)
640 {
641 std::swap (_data, other._data);
642 std::swap (_which, other._which);
643 }
644 else
645 {
646 alignas (Ts...) unsigned char tmp[std::max ({sizeof (Ts)...})];
647 details::VariantHelper<Ts...>::move (_which, storage (), &tmp);
648 details::VariantHelper<Ts...>::destroy (_which, storage ());
649 details::VariantHelper<Ts...>::move (other._which, other.storage (), storage ());
650 details::VariantHelper<Ts...>::destroy (other._which, other.storage ());
651 details::VariantHelper<Ts...>::move (_which, &tmp, other.storage ());
653 std::swap (_which, other._which);
654 }
655 }
656
657 protected:
663 constexpr bool equal (const Variant& rhs) const
664 {
665 if (index () != rhs.index ())
666 {
667 return false;
668 }
669 return details::VariantHelper<Ts...>::equal (index (), storage (), rhs.storage ());
670 }
671
677 constexpr auto compare (const Variant& rhs) const
678 {
679 using Ordering = details::VariantOrdering<Ts...>;
680 if (index () != rhs.index ())
681 {
682 return Ordering (index () <=> rhs.index ());
683 }
684 return details::VariantHelper<Ts...>::template compare<Ordering> (index (), storage (), rhs.storage ());
685 }
686
687 // friendship with equal operator.
688 template <typename... _Ts>
689 friend constexpr bool operator== (const Variant<_Ts...>& lhs, const Variant<_Ts...>& rhs);
690
691 // friendship with three-way comparison operator.
692 template <typename... _Ts>
693 friend constexpr details::VariantOrdering<_Ts...> operator<=> (const Variant<_Ts...>& lhs,
694 const Variant<_Ts...>& rhs);
695
696 private:
701 constexpr void* storage ()
702 {
703 return static_cast<void*> (std::addressof (_data));
704 }
705
710 constexpr const void* storage () const
711 {
712 return static_cast<const void*> (std::addressof (_data));
713 }
714
716 alignas (Ts...) unsigned char _data[std::max ({sizeof (Ts)...})];
717
719 uint32_t _which = 0;
720 };
721
728 template <typename... Ts>
729 constexpr bool operator== (const Variant<Ts...>& lhs, const Variant<Ts...>& rhs)
730 {
731 return lhs.equal (rhs);
732 }
733
740 template <typename... Ts>
742 {
743 return lhs.compare (rhs);
744 }
745
751 template <typename... Ts>
752 void swap (Variant<Ts...>& lhs, Variant<Ts...>& rhs) noexcept
753 {
754 lhs.swap (rhs);
755 }
756}
757
758#endif
variant class.
Definition variant.hpp:225
constexpr T & set(std::initializer_list< Up > il, Args &&... args)
set the variable type of the object identified by type and assign it a value.
Definition variant.hpp:467
constexpr std::tuple_element_t< I, std::tuple< Ts... > > * getIf()
get the variable value address of the object type identified by index.
Definition variant.hpp:595
constexpr Variant(std::in_place_index_t< I >, Args &&... args)
constructs a variant with the alternative T specified by the index I.
Definition variant.hpp:337
constexpr const T & get() const
get the variable value of the object type identified by type.
Definition variant.hpp:520
constexpr bool is() const
check that the member type in use is the same than the one specified.
Definition variant.hpp:434
constexpr Variant()
default constructor.
Definition variant.hpp:255
constexpr Variant(Variant &&other)=default
move constructor.
constexpr const T * getIf() const
get the variable value address of the object type identified by type.
Definition variant.hpp:580
constexpr Variant(T &&t)
constructs a Variant holding the alternative selected by overload resolution.
Definition variant.hpp:304
constexpr std::tuple_element_t< I, std::tuple< Ts... > > & get()
get the variable value of the object type identified by index.
Definition variant.hpp:535
friend constexpr bool operator==(const Variant< _Ts... > &lhs, const Variant< _Ts... > &rhs)
constexpr const std::tuple_element_t< I, std::tuple< Ts... > > * getIf() const
get the variable value address of the object type identified by index.
Definition variant.hpp:610
constexpr Variant(std::in_place_type_t< T >, Args &&... args)
constructs a Variant with the specified alternative T.
Definition variant.hpp:315
constexpr std::size_t index() const noexcept
return the index of the alternative that is currently held by the variant.
Definition variant.hpp:624
constexpr T & set(Args &&... args)
set the variable type of the object identified by type and assign it a value.
Definition variant.hpp:456
friend constexpr details::VariantOrdering< _Ts... > operator<=>(const Variant< _Ts... > &lhs, const Variant< _Ts... > &rhs)
constexpr std::tuple_element_t< I, std::tuple< Ts... > > & set(Args &&... args)
set the variable type of the object identified by index and assign it a value.
Definition variant.hpp:478
constexpr Variant(const Variant &other)
copy constructor.
Definition variant.hpp:273
constexpr T & get()
get the variable value of the object type identified by type.
Definition variant.hpp:505
constexpr auto compare(const Variant &rhs) const
three-way compare.
Definition variant.hpp:677
~Variant()=default
destroy the Variant instance.
constexpr std::tuple_element_t< I, std::tuple< Ts... > > & set(std::initializer_list< Up > il, Args &&... args)
set the variable type of the object identified by index and assign it a value.
Definition variant.hpp:491
constexpr Variant & operator=(const Variant &other)=default
copy assignment.
void swap(Variant &other) noexcept
swap this variant with another.
Definition variant.hpp:637
constexpr Variant(std::in_place_index_t< I >, std::initializer_list< Up > il, Args &&... args)
constructs a variant with the alternative T specified by the index I.
Definition variant.hpp:349
constexpr Variant(const Variant &other)=default
copy constructor.
constexpr Variant(Variant &&other) noexcept
move constructor.
Definition variant.hpp:292
constexpr T * getIf()
get the variable value address of the object type identified by type.
Definition variant.hpp:565
constexpr const std::tuple_element_t< I, std::tuple< Ts... > > & get() const
get the variable value of the object type identified by index.
Definition variant.hpp:550
constexpr Variant(std::in_place_type_t< T >, std::initializer_list< Up > il, Args &&... args)
constructs a Variant with the specified alternative T.
Definition variant.hpp:326
constexpr bool equal(const Variant &rhs) const
check if equal.
Definition variant.hpp:663
constexpr bool is() const
check that the index in use is the same than the one specified.
Definition variant.hpp:445
std::common_comparison_category_t< typename OrderingOf< Ts >::type... > VariantOrdering
comparison category of a variant.
Definition variant.hpp:67
Definition acceptor.hpp:32
bool operator==(const BasicLinkLayerEndpoint< Protocol > &a, const BasicLinkLayerEndpoint< Protocol > &b) noexcept
compare if endpoints are equal.
Definition endpoint.hpp:196
constexpr std::size_t find_index_v
get element position in a parameter pack according its type.
Definition traits.hpp:81
Type & swap(Type &val)
swaps byte orders.
Definition utils.hpp:86
constexpr bool is_unique_v
check if a type only appears one time in a parameter pack.
Definition traits.hpp:100
typename std::invoke_result_t< overload< Ts... >, T >::type match_t
find a type that match one of the alternatives of a parameter pack.
Definition traits.hpp:75
std::strong_ordering operator<=>(const BasicLinkLayerEndpoint< Protocol > &a, const BasicLinkLayerEndpoint< Protocol > &b) noexcept
three-way compare endpoints.
Definition endpoint.hpp:208
Definition error.hpp:144
std::strong_ordering type
Definition variant.hpp:60
comparison category of an alternative.
Definition variant.hpp:51
helper class for variant operations using jump tables for O(1) dispatch.
Definition variant.hpp:74
static void copy(std::size_t which, const void *src, void *dst)
copy one object to another.
Definition variant.hpp:82
static bool equal(std::size_t which, const void *a, const void *b)
compare if two objects of the same alternative are equal.
Definition variant.hpp:118
static void destroy(std::size_t which, void *data)
destroy the currently active object.
Definition variant.hpp:105
static Ordering compare(std::size_t which, const void *a, const void *b)
three-way compare two objects of the same alternative.
Definition variant.hpp:132
static void move(std::size_t which, void *src, void *dst)
move one object to another.
Definition variant.hpp:94