1 #ifndef ZSERIO_ANY_H_INC
2 #define ZSERIO_ANY_H_INC
22 #if !defined(ZSERIO_TYPEID_STATIC_ONLY)
23 #if !defined(_MSC_VER) && !defined(__GNUC__)
24 #warning "Unsupported compiler, type identification in zserio::Any runs in fallback mode"
25 #define ZSERIO_TYPEID_STATIC_ONLY
32 #if defined(ZSERIO_TYPEID_STATIC_ONLY)
33 using type_id = uintptr_t;
36 static uintptr_t
get()
43 #elif defined(_MSC_VER) || defined(__GXX_RTTI)
44 using type_id =
const std::type_info&;
53 using type_id = uintptr_t;
57 static uintptr_t
get()
62 static const auto id =
calcHashCode(zserio::HASH_SEED, std::string_view(__PRETTY_FUNCTION__));
63 return static_cast<uintptr_t
>(id);
70 template <
typename ALLOC>
74 virtual ~IHolder() =
default;
75 virtual IHolder* clone(
const ALLOC& allocator)
const = 0;
76 virtual IHolder* clone(
void* storage)
const = 0;
77 virtual IHolder* move(
const ALLOC& allocator) = 0;
78 virtual IHolder* move(
void* storage) = 0;
79 virtual void destroy(
const ALLOC& allocator) = 0;
80 virtual bool isType(detail::TypeIdHolder::type_id typeId)
const = 0;
84 template <
typename T,
typename ALLOC>
85 class HolderBase :
public IHolder<ALLOC>
91 m_typedHolder = std::forward<U>(value);
94 template <
typename... ARGS>
95 T& construct(
const ALLOC& allocator, ARGS&&... args)
100 if constexpr (!std::uses_allocator_v<T, ALLOC>)
102 return m_typedHolder.emplace(std::forward<ARGS>(args)...);
104 else if constexpr (std::is_constructible_v<T, std::allocator_arg_t, const ALLOC&, ARGS...>)
106 return m_typedHolder.emplace(std::allocator_arg, allocator, std::forward<ARGS>(args)...);
108 else if constexpr (std::is_constructible_v<T, ARGS..., const ALLOC&>)
110 return m_typedHolder.emplace(std::forward<ARGS>(args)..., allocator);
114 static_assert(always_false<T>::value,
115 "if uses_allocator_v<remove_cv_t<T>, ALLOC> is true, "
116 "T must be constructible with allocator argument");
120 void setHolder(
const std::optional<T>& value)
122 m_typedHolder = value;
125 void setHolder(std::optional<T>&& value)
127 m_typedHolder = std::move(value);
132 return m_typedHolder.value();
137 return m_typedHolder.value();
140 bool isType(detail::TypeIdHolder::type_id typeId)
const override
142 return detail::TypeIdHolder::get<T>() == typeId;
146 std::optional<T>& getHolder()
148 return m_typedHolder;
151 const std::optional<T>& getHolder()
const
153 return m_typedHolder;
157 std::optional<T> m_typedHolder;
161 template <
typename T,
typename ALLOC>
162 class HeapHolder :
public HolderBase<T, ALLOC>
169 using this_type = HeapHolder<T, ALLOC>;
171 explicit HeapHolder(ConstructTag) noexcept
174 static this_type* create(
const ALLOC& allocator)
176 using AllocType = RebindAlloc<ALLOC, this_type>;
177 using AllocTraits = std::allocator_traits<AllocType>;
179 AllocType typedAlloc = allocator;
180 typename AllocTraits::pointer ptr = AllocTraits::allocate(typedAlloc, 1);
182 AllocTraits::construct(typedAlloc, std::addressof(*ptr), ConstructTag{});
186 IHolder<ALLOC>* clone(
const ALLOC& allocator)
const override
188 this_type* holder = create(allocator);
189 holder->setHolder(this->getHolder());
193 IHolder<ALLOC>* clone(
void*)
const override
195 throw CppRuntimeException(
"BasicAny: Unexpected clone call.");
198 IHolder<ALLOC>* move(
const ALLOC& allocator)
override
200 this_type* holder = create(allocator);
201 holder->setHolder(std::move(this->getHolder()));
205 IHolder<ALLOC>* move(
void*)
override
207 throw CppRuntimeException(
"BasicAny: Unexpected move call.");
210 void destroy(
const ALLOC& allocator)
override
212 using AllocType = RebindAlloc<ALLOC, this_type>;
213 using AllocTraits = std::allocator_traits<AllocType>;
215 AllocType typedAlloc = allocator;
216 AllocTraits::destroy(typedAlloc,
this);
217 AllocTraits::deallocate(typedAlloc,
this, 1);
222 template <
typename T,
typename ALLOC>
223 class NonHeapHolder :
public HolderBase<T, ALLOC>
226 using this_type = NonHeapHolder<T, ALLOC>;
228 static this_type* create(
void* storage)
230 return new (storage) this_type();
233 IHolder<ALLOC>* clone(
const ALLOC&)
const override
235 throw CppRuntimeException(
"BasicAny: Unexpected clone call.");
238 IHolder<ALLOC>* clone(
void* storage)
const override
240 NonHeapHolder* holder =
new (storage) NonHeapHolder();
241 holder->setHolder(this->getHolder());
245 IHolder<ALLOC>* move(
const ALLOC&)
override
247 throw CppRuntimeException(
"BasicAny: Unexpected move call.");
250 IHolder<ALLOC>* move(
void* storage)
override
252 NonHeapHolder* holder =
new (storage) NonHeapHolder();
253 holder->setHolder(std::move(this->getHolder()));
257 void destroy(
const ALLOC&)
override
259 this->~NonHeapHolder();
263 NonHeapHolder() =
default;
266 template <
typename ALLOC>
270 using MaxInPlaceType = std::aligned_storage<3 *
sizeof(
void*),
alignof(
void*)>::type;
272 detail::IHolder<ALLOC>* heap;
273 MaxInPlaceType inPlace;
276 template <
typename T,
typename ALLOC>
277 using has_non_heap_holder = std::integral_constant<bool,
278 sizeof(NonHeapHolder<T, ALLOC>) <=
sizeof(
typename UntypedHolder<ALLOC>::MaxInPlaceType) &&
279 std::is_nothrow_move_constructible<T>::value &&
280 alignof(T) <=
alignof(
typename UntypedHolder<ALLOC>::MaxInPlaceType)>;
287 template <
typename ALLOC = std::allocator<u
int8_t>>
290 using AllocTraits = std::allocator_traits<ALLOC>;
311 m_untypedHolder.heap =
nullptr;
319 template <
typename T,
320 typename std::enable_if<!std::is_same<typename std::decay<T>::type,
BasicAny>::value &&
321 !std::is_same<typename std::decay<T>::type, ALLOC>::value,
323 explicit BasicAny(T&& value,
const ALLOC& allocator = ALLOC()) :
326 m_untypedHolder.heap =
nullptr;
327 set(std::forward<T>(value));
375 if constexpr (AllocTraits::propagate_on_container_copy_assignment::value)
393 move(std::move(other));
405 move(std::move(other));
420 if constexpr (AllocTraits::propagate_on_container_move_assignment::value)
424 move(std::move(other));
437 template <
typename T,
438 typename std::enable_if<!std::is_same<typename std::decay<T>::type,
BasicAny>::value,
int>::type =
442 set(std::forward<T>(value));
454 if (!m_isInPlace && m_untypedHolder.heap !=
nullptr && !other.m_isInPlace &&
457 std::swap(m_untypedHolder.heap, other.m_untypedHolder.heap);
462 move(std::move(other));
463 other.move(std::move(tmp));
465 if constexpr (AllocTraits::propagate_on_container_swap::value)
486 template <
typename T>
489 createHolder<typename std::decay<T>::type>()->
set(std::forward<T>(value));
497 template <
typename T,
typename... ARGS>
500 return createHolder<typename std::decay<T>::type>()->construct(
509 template <
typename T>
519 return &getHolder<T>(detail::has_non_heap_holder<T, ALLOC>())->get();
521 catch (
const std::exception&)
532 template <
typename T>
542 return &getHolder<T>(detail::has_non_heap_holder<T, ALLOC>())->get();
544 catch (
const std::exception&)
557 template <
typename T>
561 return getHolder<T>(detail::has_non_heap_holder<T, ALLOC>())->get();
571 template <
typename T>
575 return getHolder<T>(detail::has_non_heap_holder<T, ALLOC>())->get();
583 template <
typename T>
586 return hasHolder() && getUntypedHolder()->isType(detail::TypeIdHolder::get<T>());
602 if (other.m_isInPlace)
604 other.getUntypedHolder()->clone(&m_untypedHolder.inPlace);
607 else if (other.m_untypedHolder.heap !=
nullptr)
613 m_untypedHolder.heap =
nullptr;
619 if (other.m_isInPlace)
621 other.getUntypedHolder()->move(&m_untypedHolder.inPlace);
625 else if (other.m_untypedHolder.heap !=
nullptr)
630 m_untypedHolder.heap = other.m_untypedHolder.heap;
631 other.m_untypedHolder.heap =
nullptr;
642 m_untypedHolder.heap =
nullptr;
652 m_untypedHolder.heap =
nullptr;
656 bool hasHolder() const noexcept
658 return (m_isInPlace || m_untypedHolder.heap !=
nullptr);
661 template <
typename T>
662 detail::HolderBase<T, ALLOC>* createHolder()
666 if (getUntypedHolder()->
isType(detail::TypeIdHolder::get<T>()))
668 return getHolder<T>(detail::has_non_heap_holder<T, ALLOC>());
674 return createHolderImpl<T>(detail::has_non_heap_holder<T, ALLOC>());
677 template <
typename T>
678 detail::HolderBase<T, ALLOC>* createHolderImpl(std::true_type)
680 detail::NonHeapHolder<T, ALLOC>* holder =
681 detail::NonHeapHolder<T, ALLOC>::create(&m_untypedHolder.inPlace);
686 template <
typename T>
687 detail::HolderBase<T, ALLOC>* createHolderImpl(std::false_type)
689 detail::HeapHolder<T, ALLOC>* holder = detail::HeapHolder<T, ALLOC>::create(
get_allocator_ref());
690 m_untypedHolder.heap = holder;
694 template <
typename T>
695 void checkType()
const
704 void throwBadType()
const
706 throw CppRuntimeException(
"Bad type in BasicAny");
709 template <
typename T>
710 detail::HeapHolder<T, ALLOC>* getHeapHolder() noexcept
712 return static_cast<detail::HeapHolder<T, ALLOC>*
>(m_untypedHolder.heap);
715 template <
typename T>
716 const detail::HeapHolder<T, ALLOC>* getHeapHolder() const noexcept
718 return static_cast<detail::HeapHolder<T, ALLOC>*
>(m_untypedHolder.heap);
721 template <
typename T>
722 detail::NonHeapHolder<T, ALLOC>* getInplaceHolder() noexcept
724 return reinterpret_cast<detail::NonHeapHolder<T, ALLOC>*
>(&m_untypedHolder.inPlace);
727 template <
typename T>
728 const detail::NonHeapHolder<T, ALLOC>* getInplaceHolder() const noexcept
730 return reinterpret_cast<const detail::NonHeapHolder<T, ALLOC>*
>(&m_untypedHolder.inPlace);
733 template <
typename T>
734 detail::HolderBase<T, ALLOC>* getHolder(std::true_type) noexcept
736 return static_cast<detail::HolderBase<T, ALLOC>*
>(getInplaceHolder<T>());
739 template <
typename T>
740 detail::HolderBase<T, ALLOC>* getHolder(std::false_type) noexcept
742 return static_cast<detail::HolderBase<T, ALLOC>*
>(getHeapHolder<T>());
745 template <
typename T>
746 const detail::HolderBase<T, ALLOC>* getHolder(std::true_type)
const noexcept
748 return static_cast<const detail::HolderBase<T, ALLOC>*
>(getInplaceHolder<T>());
751 template <
typename T>
752 const detail::HolderBase<T, ALLOC>* getHolder(std::false_type)
const noexcept
754 return static_cast<const detail::HolderBase<T, ALLOC>*
>(getHeapHolder<T>());
757 detail::IHolder<ALLOC>* getUntypedHolder() noexcept
760 ?
reinterpret_cast<detail::IHolder<ALLOC>*
>(&m_untypedHolder.inPlace)
761 : m_untypedHolder.heap;
764 const detail::IHolder<ALLOC>* getUntypedHolder() const noexcept
767 ?
reinterpret_cast<const detail::IHolder<ALLOC>*
>(&m_untypedHolder.inPlace)
768 : m_untypedHolder.heap;
771 detail::UntypedHolder<ALLOC> m_untypedHolder;
772 bool m_isInPlace =
false;
allocator_type & get_allocator_ref()
void set_allocator(const allocator_type &allocator)
BasicAny(const BasicAny &other)
T & emplace(ARGS &&... args)
bool isType() const noexcept
bool hasValue() const noexcept
BasicAny(const BasicAny &other, const ALLOC &allocator)
void swap(BasicAny &other)
BasicAny & operator=(BasicAny &&other)
BasicAny(T &&value, const ALLOC &allocator=ALLOC())
const T * get_if() const noexcept
BasicAny & operator=(const BasicAny &other)
BasicAny(BasicAny &&other) noexcept
BasicAny(const ALLOC &allocator)
BasicAny & operator=(T &&value)
BasicAny(BasicAny &&other, const ALLOC &allocator)
decltype(auto) get(BasicVariant< ALLOC, INDEX, T... > &var)
uint32_t calcHashCode(uint32_t seedValue, const ArrayView< T, ARRAY_TRAITS > &array)