1#ifndef ZSERIO_ANY_H_INC
2#define ZSERIO_ANY_H_INC
24#if !defined(ZSERIO_TYPEID_STATIC_ONLY)
25 #if !defined(_MSC_VER) && !defined(__GNUC__)
26 #warning "Unsupported compiler, type identification in zserio::Any runs in fallback mode"
27 #define ZSERIO_TYPEID_STATIC_ONLY
34#if defined(ZSERIO_TYPEID_STATIC_ONLY)
35 using type_id = uintptr_t;
38 static uintptr_t get()
44 static bool equals(type_id lhs, type_id rhs)
50#elif defined(_MSC_VER) || defined(__GXX_RTTI)
51 using type_id =
const std::type_info&;
59 static bool equals(type_id lhs, type_id rhs)
61 #if defined(_LIBCPP_TYPEINFO_COMPARISON_IMPLEMENTATION) && _LIBCPP_TYPEINFO_COMPARISON_IMPLEMENTATION == 3
68 return lhs == rhs || std::strcmp(lhs.name(), rhs.name()) == 0;
75 using type_id = uintptr_t;
79 static uintptr_t get()
84 static const auto id =
calcHashCode(zserio::HASH_SEED, std::string_view(__PRETTY_FUNCTION__));
85 return static_cast<uintptr_t
>(id);
88 static bool equals(type_id lhs, type_id rhs)
97template <
typename ALLOC>
101 virtual ~IHolder() =
default;
102 virtual IHolder* clone(
const ALLOC& allocator)
const = 0;
103 virtual IHolder* clone(
void* storage)
const = 0;
104 virtual IHolder* move(
const ALLOC& allocator) = 0;
105 virtual IHolder* move(
void* storage) = 0;
106 virtual void destroy(
const ALLOC& allocator) = 0;
107 virtual bool isType(detail::TypeIdHolder::type_id typeId)
const = 0;
111template <
typename T,
typename ALLOC>
112class HolderBase :
public IHolder<ALLOC>
115 template <
typename U>
118 m_typedHolder = std::forward<U>(value);
121 template <
typename... ARGS>
122 T& construct(
const ALLOC& allocator, ARGS&&... args)
127 if constexpr (!std::uses_allocator_v<T, ALLOC>)
129 return m_typedHolder.emplace(std::forward<ARGS>(args)...);
131 else if constexpr (std::is_constructible_v<T, std::allocator_arg_t,
const ALLOC&, ARGS...>)
133 return m_typedHolder.emplace(std::allocator_arg, allocator, std::forward<ARGS>(args)...);
135 else if constexpr (std::is_constructible_v<T, ARGS...,
const ALLOC&>)
137 return m_typedHolder.emplace(std::forward<ARGS>(args)..., allocator);
141 static_assert(always_false<T>::value,
142 "if uses_allocator_v<remove_cv_t<T>, ALLOC> is true, "
143 "T must be constructible with allocator argument");
147 void setHolder(
const std::optional<T>& value)
149 m_typedHolder = value;
152 void setHolder(std::optional<T>&& value)
154 m_typedHolder = std::move(value);
159 return m_typedHolder.value();
164 return m_typedHolder.value();
167 bool isType(detail::TypeIdHolder::type_id typeId)
const override
169 return detail::TypeIdHolder::equals(detail::TypeIdHolder::get<T>(), typeId);
173 std::optional<T>& getHolder()
175 return m_typedHolder;
178 const std::optional<T>& getHolder()
const
180 return m_typedHolder;
184 std::optional<T> m_typedHolder;
188template <
typename T,
typename ALLOC>
189class HeapHolder :
public HolderBase<T, ALLOC>
196 using this_type = HeapHolder<T, ALLOC>;
198 explicit HeapHolder(ConstructTag)
noexcept
201 static this_type* create(
const ALLOC& allocator)
203 using AllocType = RebindAlloc<ALLOC, this_type>;
204 using AllocTraits = std::allocator_traits<AllocType>;
206 AllocType typedAlloc = allocator;
207 typename AllocTraits::pointer ptr = AllocTraits::allocate(typedAlloc, 1);
209 AllocTraits::construct(typedAlloc, std::addressof(*ptr), ConstructTag{});
213 IHolder<ALLOC>* clone(
const ALLOC& allocator)
const override
215 this_type* holder = create(allocator);
216 holder->setHolder(this->getHolder());
220 IHolder<ALLOC>* clone(
void*)
const override
222 throw CppRuntimeException(
"BasicAny: Unexpected clone call.");
225 IHolder<ALLOC>* move(
const ALLOC& allocator)
override
227 this_type* holder = create(allocator);
228 holder->setHolder(std::move(this->getHolder()));
232 IHolder<ALLOC>* move(
void*)
override
234 throw CppRuntimeException(
"BasicAny: Unexpected move call.");
237 void destroy(
const ALLOC& allocator)
override
239 using AllocType = RebindAlloc<ALLOC, this_type>;
240 using AllocTraits = std::allocator_traits<AllocType>;
242 AllocType typedAlloc = allocator;
243 AllocTraits::destroy(typedAlloc,
this);
244 AllocTraits::deallocate(typedAlloc,
this, 1);
249template <
typename T,
typename ALLOC>
250class NonHeapHolder :
public HolderBase<T, ALLOC>
253 using this_type = NonHeapHolder<T, ALLOC>;
255 static this_type* create(
void* storage)
257 return new (storage) this_type();
260 IHolder<ALLOC>* clone(
const ALLOC&)
const override
262 throw CppRuntimeException(
"BasicAny: Unexpected clone call.");
265 IHolder<ALLOC>* clone(
void* storage)
const override
267 NonHeapHolder* holder =
new (storage) NonHeapHolder();
268 holder->setHolder(this->getHolder());
272 IHolder<ALLOC>* move(
const ALLOC&)
override
274 throw CppRuntimeException(
"BasicAny: Unexpected move call.");
277 IHolder<ALLOC>* move(
void* storage)
override
279 NonHeapHolder* holder =
new (storage) NonHeapHolder();
280 holder->setHolder(std::move(this->getHolder()));
284 void destroy(
const ALLOC&)
override
286 this->~NonHeapHolder();
290 NonHeapHolder() =
default;
293template <
typename ALLOC>
297 using MaxInPlaceType = std::aligned_storage<3 *
sizeof(
void*),
alignof(
void*)>::type;
299 detail::IHolder<ALLOC>* heap;
300 MaxInPlaceType inPlace;
303template <
typename T,
typename ALLOC>
304using has_non_heap_holder = std::integral_constant<bool,
305 sizeof(NonHeapHolder<T, ALLOC>) <=
sizeof(
typename UntypedHolder<ALLOC>::MaxInPlaceType) &&
306 std::is_nothrow_move_constructible<T>::value &&
307 alignof(T) <=
alignof(
typename UntypedHolder<ALLOC>::MaxInPlaceType)>;
314template <
typename ALLOC = std::allocator<u
int8_t>>
317 using AllocTraits = std::allocator_traits<ALLOC>;
338 m_untypedHolder.heap =
nullptr;
346 template <
typename T,
347 typename std::enable_if<!std::is_same<typename std::decay<T>::type,
BasicAny>::value &&
348 !std::is_same<typename std::decay<T>::type,
ALLOC>::value,
353 m_untypedHolder.heap =
nullptr;
354 set(std::forward<T>(value));
402 if constexpr (AllocTraits::propagate_on_container_copy_assignment::value)
420 move(std::move(
other));
432 move(std::move(
other));
447 if constexpr (AllocTraits::propagate_on_container_move_assignment::value)
451 move(std::move(
other));
464 template <
typename T,
465 typename std::enable_if<!std::is_same<typename std::decay<T>::type,
BasicAny>::value,
int>::type =
469 set(std::forward<T>(value));
481 if (!m_isInPlace && m_untypedHolder.heap !=
nullptr && !
other.m_isInPlace &&
484 std::swap(m_untypedHolder.heap,
other.m_untypedHolder.heap);
489 move(std::move(
other));
492 if constexpr (AllocTraits::propagate_on_container_swap::value)
513 template <
typename T>
524 template <
typename T,
typename...
ARGS>
536 template <
typename T>
546 return &
getHolder<T>(detail::has_non_heap_holder<T, ALLOC>())->get();
548 catch (
const std::exception&)
559 template <
typename T>
569 return &
getHolder<T>(detail::has_non_heap_holder<T, ALLOC>())->get();
571 catch (
const std::exception&)
584 template <
typename T>
588 return getHolder<T>(detail::has_non_heap_holder<T, ALLOC>())->get();
598 template <
typename T>
602 return getHolder<T>(detail::has_non_heap_holder<T, ALLOC>())->get();
610 template <
typename T>
613 return hasHolder() && getUntypedHolder()->isType(detail::TypeIdHolder::get<T>());
629 if (
other.m_isInPlace)
631 other.getUntypedHolder()->clone(&m_untypedHolder.inPlace);
634 else if (other.m_untypedHolder.heap !=
nullptr)
640 m_untypedHolder.heap =
nullptr;
646 if (other.m_isInPlace)
648 other.getUntypedHolder()->move(&m_untypedHolder.inPlace);
652 else if (other.m_untypedHolder.heap !=
nullptr)
657 m_untypedHolder.heap = other.m_untypedHolder.heap;
658 other.m_untypedHolder.heap =
nullptr;
669 m_untypedHolder.heap =
nullptr;
679 m_untypedHolder.heap =
nullptr;
683 bool hasHolder() const noexcept
685 return (m_isInPlace || m_untypedHolder.heap !=
nullptr);
688 template <
typename T>
689 detail::HolderBase<T, ALLOC>* createHolder()
693 if (getUntypedHolder()->
isType(detail::TypeIdHolder::get<T>()))
695 return getHolder<T>(detail::has_non_heap_holder<T, ALLOC>());
701 return createHolderImpl<T>(detail::has_non_heap_holder<T, ALLOC>());
704 template <
typename T>
705 detail::HolderBase<T, ALLOC>* createHolderImpl(std::true_type)
707 detail::NonHeapHolder<T, ALLOC>* holder =
708 detail::NonHeapHolder<T, ALLOC>::create(&m_untypedHolder.inPlace);
713 template <
typename T>
714 detail::HolderBase<T, ALLOC>* createHolderImpl(std::false_type)
716 detail::HeapHolder<T, ALLOC>* holder = detail::HeapHolder<T, ALLOC>::create(
get_allocator_ref());
717 m_untypedHolder.heap = holder;
721 template <
typename T>
722 void checkType()
const
731 void throwBadType()
const
733 throw CppRuntimeException(
"Bad type in BasicAny");
736 template <
typename T>
737 detail::HeapHolder<T, ALLOC>* getHeapHolder() noexcept
739 return static_cast<detail::HeapHolder<T, ALLOC>*
>(m_untypedHolder.heap);
742 template <
typename T>
743 const detail::HeapHolder<T, ALLOC>* getHeapHolder() const noexcept
745 return static_cast<detail::HeapHolder<T, ALLOC>*
>(m_untypedHolder.heap);
748 template <
typename T>
749 detail::NonHeapHolder<T, ALLOC>* getInplaceHolder() noexcept
751 return reinterpret_cast<detail::NonHeapHolder<T, ALLOC>*
>(&m_untypedHolder.inPlace);
754 template <
typename T>
755 const detail::NonHeapHolder<T, ALLOC>* getInplaceHolder() const noexcept
757 return reinterpret_cast<const detail::NonHeapHolder<T, ALLOC>*
>(&m_untypedHolder.inPlace);
760 template <
typename T>
761 detail::HolderBase<T, ALLOC>* getHolder(std::true_type)
noexcept
763 return static_cast<detail::HolderBase<T, ALLOC>*
>(getInplaceHolder<T>());
766 template <
typename T>
767 detail::HolderBase<T, ALLOC>* getHolder(std::false_type)
noexcept
769 return static_cast<detail::HolderBase<T, ALLOC>*
>(getHeapHolder<T>());
772 template <
typename T>
773 const detail::HolderBase<T, ALLOC>* getHolder(std::true_type)
const noexcept
775 return static_cast<const detail::HolderBase<T, ALLOC>*
>(getInplaceHolder<T>());
778 template <
typename T>
779 const detail::HolderBase<T, ALLOC>* getHolder(std::false_type)
const noexcept
781 return static_cast<const detail::HolderBase<T, ALLOC>*
>(getHeapHolder<T>());
784 detail::IHolder<ALLOC>* getUntypedHolder() noexcept
787 ?
reinterpret_cast<detail::IHolder<ALLOC>*
>(&m_untypedHolder.inPlace)
788 : m_untypedHolder.heap;
791 const detail::IHolder<ALLOC>* getUntypedHolder() const noexcept
794 ?
reinterpret_cast<const detail::IHolder<ALLOC>*
>(&m_untypedHolder.inPlace)
795 : m_untypedHolder.heap;
798 detail::UntypedHolder<ALLOC> m_untypedHolder;
799 bool m_isInPlace =
false;
allocator_type get_allocator() const
void set_allocator(const allocator_type &allocator)
allocator_type & get_allocator_ref()
BasicAny(const BasicAny &other)
T & emplace(ARGS &&... args)
bool isType() const noexcept
bool hasValue() const noexcept
BasicAny(const BasicAny &other, const ALLOC &allocator)
BasicAny & operator=(const BasicAny &other)
void swap(BasicAny &other)
BasicAny & operator=(BasicAny &&other)
BasicAny(T &&value, const ALLOC &allocator=ALLOC())
BasicAny & operator=(T &&value)
BasicAny(BasicAny &&other) noexcept
BasicAny(const ALLOC &allocator)
BasicAny(BasicAny &&other, const ALLOC &allocator)
const T * get_if() const noexcept
decltype(auto) get(BasicVariant< ALLOC, INDEX, T... > &var)
uint32_t calcHashCode(uint32_t seedValue, const ArrayView< T, ARRAY_TRAITS > &array)