1 #ifndef ZSERIO_VARIANT_H_INC
2 #define ZSERIO_VARIANT_H_INC
17 template <
typename T,
bool BIG = (sizeof(T) > 3 *
sizeof(
void*))>
18 struct variant_element
21 using is_heap_allocated = std::false_type;
25 struct variant_element<T, true>
28 using is_heap_allocated = std::true_type;
31 template <
size_t I,
typename... T>
34 using type = std::tuple_element_t<I, std::tuple<T...>>;
37 template <
auto I,
typename... T>
38 using type_at_t =
typename type_at<static_cast<size_t>(I), T...>::type;
40 template <
auto I,
typename... T>
41 struct is_variant_heap_allocated : variant_element<type_at_t<I, T...>>::is_heap_allocated
44 template <
auto I,
class... T>
45 constexpr
bool is_variant_heap_allocated_v = is_variant_heap_allocated<I, T...>::value;
47 template <
typename... T>
48 struct any_variant_heap_allocated : std::false_type
51 template <
typename T,
typename... ARGS>
52 struct any_variant_heap_allocated<T, ARGS...>
53 : std::bool_constant<variant_element<T>::is_heap_allocated::value ||
54 any_variant_heap_allocated<ARGS...>::value>
57 template <
typename... ARGS>
58 inline constexpr
bool any_variant_heap_allocated_v = any_variant_heap_allocated<ARGS...>::value;
92 template <
typename ALLOC,
typename INDEX,
typename... T>
95 using AllocTraits = std::allocator_traits<ALLOC>;
103 using VariantType = std::variant<typename detail::variant_element<T>::type...>;
122 if constexpr (detail::is_variant_heap_allocated_v<0, T...>)
136 template <INDEX I,
typename... ARGS,
137 std::enable_if_t<!detail::is_variant_heap_allocated_v<I, T...>>* =
nullptr,
152 template <INDEX I,
typename... ARGS,
153 std::enable_if_t<!detail::is_variant_heap_allocated_v<I, T...>>* =
nullptr>
168 template <INDEX I,
typename... ARGS,
typename U = detail::type_at_t<static_cast<size_t>(I), T...>,
169 std::enable_if_t<detail::is_variant_heap_allocated_v<I, T...>>* =
nullptr>
172 m_data(
std::
in_place_index<static_cast<size_t>(I)>, allocateValue<U>(
std::forward<ARGS>(args)...))
183 template <INDEX I,
typename... ARGS,
typename U = detail::type_at_t<static_cast<size_t>(I), T...>,
184 std::enable_if_t<detail::is_variant_heap_allocated_v<I, T...>>* =
nullptr,
187 m_data(
std::
in_place_index<static_cast<size_t>(I)>, allocateValue<U>(
std::forward<ARGS>(args)...))
238 if constexpr (AllocTraits::propagate_on_container_copy_assignment::value)
258 move(std::move(other));
272 move(std::move(other));
287 if constexpr (AllocTraits::propagate_on_container_move_assignment::value)
291 move(std::move(other));
307 if constexpr (detail::any_variant_heap_allocated_v<T...>)
309 return m_data.valueless_by_exception() ||
310 valuelessByMoveSeq(std::make_index_sequence<
sizeof...(T)>());
314 return m_data.valueless_by_exception();
323 template <INDEX I,
typename... ARGS>
327 if constexpr (detail::is_variant_heap_allocated_v<I, T...>)
329 using U = detail::type_at_t<I, T...>;
330 U* ptr = allocateValue<U>(std::forward<ARGS>(args)...);
331 return *m_data.template emplace<static_cast<size_t>(I)>(ptr);
335 return m_data.template emplace<static_cast<size_t>(I)>(std::forward<ARGS>(args)...);
344 if constexpr (detail::any_variant_heap_allocated_v<T...>)
346 return valuelessByMoveSeq(std::make_index_sequence<
sizeof...(T)>())
347 ?
static_cast<INDEX
>(std::variant_npos)
348 :
static_cast<INDEX
>(m_data.index());
352 return static_cast<INDEX
>(m_data.index());
359 template <INDEX I,
typename U = detail::type_at_t<I, T...>>
366 if constexpr (detail::is_variant_heap_allocated_v<I, T...>)
368 return *std::get_if<static_cast<size_t>(I)>(&m_data);
372 return std::get_if<static_cast<size_t>(I)>(&m_data);
379 template <INDEX I,
typename U = detail::type_at_t<I, T...>>
386 if constexpr (detail::is_variant_heap_allocated_v<I, T...>)
388 return *std::get_if<static_cast<size_t>(I)>(&m_data);
392 return std::get_if<static_cast<size_t>(I)>(&m_data);
401 template <INDEX I,
typename U = detail::type_at_t<I, T...>>
404 auto ptr = get_if<I>();
407 throw BadVariantAccess(
"Variant: Attempt to retrieve an inactive element at index ")
408 <<
static_cast<size_t>(I) <<
". Active element index is " <<
static_cast<size_t>(
index());
418 template <INDEX I,
typename U = detail::type_at_t<I, T...>>
421 auto ptr = get_if<I>();
424 throw BadVariantAccess(
"Variant: Attempt to retrieve an inactive element at index ")
425 <<
static_cast<size_t>(I) <<
". Active element index is " <<
static_cast<size_t>(
index());
437 m_data.swap(other.m_data);
438 if constexpr (AllocTraits::propagate_on_container_swap::value)
452 template <
typename F>
453 auto visit(F&& fun) -> decltype(fun(std::declval<detail::type_at_t<0, T...>>()))
459 using R = decltype(fun(std::declval<detail::type_at_t<0, T...>>()));
460 if constexpr (std::is_same_v<R, void>)
462 std::nullptr_t dummy;
463 visitSeq(std::forward<F>(fun), dummy, std::make_index_sequence<
sizeof...(T)>());
468 visitSeq(std::forward<F>(fun), ret, std::make_index_sequence<
sizeof...(T)>());
480 template <
typename F>
481 auto visit(F&& fun)
const -> decltype(fun(std::declval<detail::type_at_t<0, T...>>()))
487 using R = decltype(fun(std::declval<detail::type_at_t<0, T...>>()));
488 if constexpr (std::is_same_v<R, void>)
490 std::nullptr_t dummy;
491 visitSeq(std::forward<F>(fun), dummy, std::make_index_sequence<
sizeof...(T)>());
496 visitSeq(std::forward<F>(fun), ret, std::make_index_sequence<
sizeof...(T)>());
512 return equalSeq(other, std::make_index_sequence<
sizeof...(T)>());
522 return !(*
this == other);
536 return lessSeq(other, std::make_index_sequence<
sizeof...(T)>());
546 return other < *
this;
556 return !(other < *
this);
566 return !(other > *
this);
570 template <
size_t... I>
571 bool valuelessByMoveSeq(std::index_sequence<I...>)
const
573 return (valuelessByMove<I>() || ...);
577 bool valuelessByMove()
const
579 if constexpr (detail::is_variant_heap_allocated_v<I, T...>)
581 if (I != m_data.index())
585 return *std::get_if<I>(&m_data) ==
nullptr;
593 template <
size_t... I,
typename F,
typename R>
594 void visitSeq(F&& fun, R& returnValue, std::index_sequence<I...>)
596 (visit<I>(fun, returnValue), ...);
599 template <
size_t... I,
typename F,
typename R>
600 void visitSeq(F&& fun, R& returnValue, std::index_sequence<I...>)
const
602 (visit<I>(fun, returnValue), ...);
605 template <
size_t I,
typename F,
typename R>
606 void visit(F&& fun, R& returnValue)
608 if (I !=
static_cast<size_t>(
index()))
612 if constexpr (std::is_same_v<R, std::nullptr_t>)
614 std::forward<F>(fun)(get<static_cast<INDEX>(I)>());
618 returnValue = std::forward<F>(fun)(get<static_cast<INDEX>(I)>());
622 template <
size_t I,
typename F,
typename R>
623 void visit(F&& fun, R& returnValue)
const
625 if (I !=
static_cast<size_t>(
index()))
629 if constexpr (std::is_same_v<R, std::nullptr_t>)
631 std::forward<F>(fun)(get<static_cast<INDEX>(I)>());
635 returnValue = std::forward<F>(fun)(get<static_cast<INDEX>(I)>());
639 template <
size_t... I>
640 bool equalSeq(
const BasicVariant& other, std::index_sequence<I...>)
const
642 return (equal<I>(other) && ...);
648 if (I !=
static_cast<size_t>(
index()))
652 return get<static_cast<INDEX>(I)>() == other.get<
static_cast<INDEX
>(I)>();
655 template <
size_t... I>
656 bool lessSeq(
const BasicVariant& other, std::index_sequence<I...>)
const
658 return (less<I>(other) && ...);
664 if (I !=
static_cast<size_t>(
index()))
668 return get<static_cast<INDEX>(I)>() < other.get<
static_cast<INDEX
>(I)>();
671 template <
typename U,
typename... ARGS>
672 U* allocateValue(ARGS&&... args)
674 using AllocType = RebindAlloc<ALLOC, U>;
675 using ConcreteAllocTraits = std::allocator_traits<AllocType>;
677 auto ptr = ConcreteAllocTraits::allocate(typedAlloc, 1);
680 ConcreteAllocTraits::construct(typedAlloc, std::addressof(*ptr), std::forward<ARGS>(args)...);
684 ConcreteAllocTraits::deallocate(typedAlloc, ptr, 1);
692 template <
typename U>
693 void destroyValue(U* ptr)
697 using AllocType = RebindAlloc<ALLOC, U>;
698 using ConcreteAllocTraits = std::allocator_traits<AllocType>;
700 ConcreteAllocTraits::destroy(typedAlloc, ptr);
701 ConcreteAllocTraits::deallocate(typedAlloc, ptr, 1);
709 copySeq(other, std::make_index_sequence<
sizeof...(T)>());
712 template <
size_t... I>
713 void copySeq(
const BasicVariant& other, std::index_sequence<I...>)
715 (copy<I>(other), ...);
721 if (I !=
static_cast<size_t>(other.index()))
725 emplace<static_cast<INDEX>(I)>(other.get<
static_cast<INDEX
>(I)>());
732 moveSeq(std::move(other), std::make_index_sequence<
sizeof...(T)>());
735 template <
size_t... I>
736 void moveSeq(
BasicVariant&& other, std::index_sequence<I...>)
738 (move<I>(std::move(other)), ...);
744 if (I !=
static_cast<size_t>(other.index()))
748 if constexpr (detail::is_variant_heap_allocated_v<I, T...>)
752 auto& ptr = *std::get_if<I>(&other.m_data);
753 m_data.template emplace<I>(ptr);
758 using U = detail::type_at_t<I, T...>;
759 U* ptr = allocateValue<U>(std::move(**std::get_if<I>(&other.m_data)));
760 m_data.template emplace<I>(ptr);
765 auto& value = *std::get_if<I>(&other.m_data);
766 m_data.template emplace<I>(std::move(value));
772 clearSeq(std::make_index_sequence<
sizeof...(T)>());
775 template <
size_t... I>
776 void clearSeq(std::index_sequence<I...>)
784 if (I !=
static_cast<size_t>(
index()))
788 if constexpr (detail::is_variant_heap_allocated_v<I, T...>)
790 auto& ptr = *std::get_if<I>(&m_data);
801 template <
typename INDEX,
typename... T>
811 template <
auto I,
typename ALLOC,
typename INDEX,
typename... T>
814 static_assert(std::is_same_v<decltype(I), INDEX>,
"Index has a wrong type");
815 return var.template get<I>();
825 template <
auto I,
typename ALLOC,
typename INDEX,
typename... T>
828 static_assert(std::is_same_v<decltype(I), INDEX>,
"Index has a wrong type");
829 return var.template get<I>();
837 template <
auto I,
typename ALLOC,
typename INDEX,
typename... T>
840 static_assert(std::is_same_v<decltype(I), INDEX>,
"Index has a wrong type");
841 return var->template get_if<I>();
849 template <
auto I,
typename ALLOC,
typename INDEX,
typename... T>
852 static_assert(std::is_same_v<decltype(I), INDEX>,
"Index has a wrong type");
853 return var->template get_if<I>();
864 template <
typename F,
typename ALLOC,
typename INDEX,
typename... T>
867 return var.visit(std::forward<F>(fun));
878 template <
typename F,
typename ALLOC,
typename INDEX,
typename... T>
881 return var.visit(std::forward<F>(fun));
890 template <
typename ALLOC,
typename INDEX,
typename... T>
893 uint32_t result = seed;
895 var.
visit([&result](
const auto& value) {
906 template <
typename ALLOC,
typename INDEX,
typename... T>
907 struct hash<
zserio::BasicVariant<ALLOC, INDEX, T...>>
allocator_type & get_allocator_ref()
void set_allocator(const allocator_type &allocator)
auto visit(F &&fun) const -> decltype(fun(std::declval< detail::type_at_t< 0, T... >>()))
BasicVariant(const BasicVariant &other, const ALLOC &allocator)
void swap(BasicVariant &other)
BasicVariant(BasicVariant &&other, const ALLOC &allocator)
BasicVariant(const ALLOC &allocator)
bool operator<=(const BasicVariant &other) const
bool operator<(const BasicVariant &other) const
bool operator>(const BasicVariant &other) const
BasicVariant & operator=(BasicVariant &&other)
bool valueless_by_exception() const noexcept
BasicVariant & operator=(const BasicVariant &other)
BasicVariant(in_place_index_t< I >, ARGS &&... args)
std::variant< typename detail::variant_element< T >::type... > VariantType
BasicVariant(in_place_index_t< I >, const ALLOC &allocator, ARGS &&... args)
bool operator==(const BasicVariant &other) const
BasicVariant(in_place_index_t< I >, const ALLOC &allocator, ARGS &&... args)
const U * get_if() const noexcept
bool operator>=(const BasicVariant &other) const
BasicVariant(in_place_index_t< I >, ARGS &&... args)
auto visit(F &&fun) -> decltype(fun(std::declval< detail::type_at_t< 0, T... >>()))
INDEX index() const noexcept
BasicVariant(const BasicVariant &other)
BasicVariant(BasicVariant &&other)
decltype(auto) emplace(ARGS &&... args)
bool operator!=(const BasicVariant &other) const
CppRuntimeException(const char *message="")
decltype(auto) visit(F &&fun, BasicVariant< ALLOC, INDEX, T... > &var)
constexpr in_place_index_t< I > in_place_index
decltype(auto) get(BasicVariant< ALLOC, INDEX, T... > &var)
uint32_t calcHashCode(uint32_t seedValue, const ArrayView< T, ARRAY_TRAITS > &array)
decltype(auto) get_if(BasicVariant< ALLOC, INDEX, T... > *var)
constexpr bool is_first_allocator_v
size_t operator()(const zserio::BasicVariant< ALLOC, INDEX, T... > &var) const