Zserio C++17 runtime library  0.5.0
Built for Zserio 2.17.0
Any.h
Go to the documentation of this file.
1 #ifndef ZSERIO_ANY_H_INC
2 #define ZSERIO_ANY_H_INC
3 
4 #include <cstddef>
5 #include <optional>
6 #include <type_traits>
7 
10 #include "zserio/Optional.h"
11 #include "zserio/RebindAlloc.h"
12 #include "zserio/Traits.h"
13 #include "zserio/Types.h"
14 
15 namespace zserio
16 {
17 
18 namespace detail
19 {
20 
21 class TypeIdHolder
22 {
23 public:
24  using type_id = const int*;
25 
26  template <typename T>
27  static type_id get()
28  {
29  static int currentTypeId;
30 
31  return &currentTypeId;
32  }
33 };
34 
35 // Interface for object holders
36 template <typename ALLOC>
37 class IHolder
38 {
39 public:
40  virtual ~IHolder() = default;
41  virtual IHolder* clone(const ALLOC& allocator) const = 0;
42  virtual IHolder* clone(void* storage) const = 0;
43  virtual IHolder* move(const ALLOC& allocator) = 0;
44  virtual IHolder* move(void* storage) = 0;
45  virtual void destroy(const ALLOC& allocator) = 0;
46  virtual bool isType(detail::TypeIdHolder::type_id typeId) const = 0;
47 };
48 
49 // Base of object holders, holds a value in the std::optional
50 template <typename T, typename ALLOC>
51 class HolderBase : public IHolder<ALLOC>
52 {
53 public:
54  template <typename U>
55  void set(U&& value)
56  {
57  m_typedHolder = std::forward<U>(value);
58  }
59 
60  void setHolder(const std::optional<T>& value)
61  {
62  m_typedHolder = value;
63  }
64 
65  void setHolder(std::optional<T>&& value)
66  {
67  m_typedHolder = std::move(value);
68  }
69 
70  T& get()
71  {
72  return m_typedHolder.value();
73  }
74 
75  const T& get() const
76  {
77  return m_typedHolder.value();
78  }
79 
80  bool isType(detail::TypeIdHolder::type_id typeId) const override
81  {
82  return detail::TypeIdHolder::get<T>() == typeId;
83  }
84 
85 protected:
86  std::optional<T>& getHolder()
87  {
88  return m_typedHolder;
89  }
90 
91  const std::optional<T>& getHolder() const
92  {
93  return m_typedHolder;
94  }
95 
96 private:
97  std::optional<T> m_typedHolder;
98 };
99 
100 // Holder allocated on heap
101 template <typename T, typename ALLOC>
102 class HeapHolder : public HolderBase<T, ALLOC>
103 {
104 private:
105  struct ConstructTag
106  {};
107 
108 public:
109  using this_type = HeapHolder<T, ALLOC>;
110 
111  explicit HeapHolder(ConstructTag) noexcept
112  {}
113 
114  static this_type* create(const ALLOC& allocator)
115  {
116  using AllocType = RebindAlloc<ALLOC, this_type>;
117  using AllocTraits = std::allocator_traits<AllocType>;
118 
119  AllocType typedAlloc = allocator;
120  typename AllocTraits::pointer ptr = AllocTraits::allocate(typedAlloc, 1);
121  // this never throws because HeapHolder constructor never throws
122  AllocTraits::construct(typedAlloc, std::addressof(*ptr), ConstructTag{});
123  return ptr;
124  }
125 
126  IHolder<ALLOC>* clone(const ALLOC& allocator) const override
127  {
128  this_type* holder = create(allocator);
129  holder->setHolder(this->getHolder());
130  return holder;
131  }
132 
133  IHolder<ALLOC>* clone(void*) const override
134  {
135  throw CppRuntimeException("BasicAny: Unexpected clone call.");
136  }
137 
138  IHolder<ALLOC>* move(const ALLOC& allocator) override
139  {
140  this_type* holder = create(allocator);
141  holder->setHolder(std::move(this->getHolder()));
142  return holder;
143  }
144 
145  IHolder<ALLOC>* move(void*) override
146  {
147  throw CppRuntimeException("BasicAny: Unexpected move call.");
148  }
149 
150  void destroy(const ALLOC& allocator) override
151  {
152  using AllocType = RebindAlloc<ALLOC, this_type>;
153  using AllocTraits = std::allocator_traits<AllocType>;
154 
155  AllocType typedAlloc = allocator;
156  AllocTraits::destroy(typedAlloc, this);
157  AllocTraits::deallocate(typedAlloc, this, 1);
158  }
159 };
160 
161 // Holder allocated in the in-place storage
162 template <typename T, typename ALLOC>
163 class NonHeapHolder : public HolderBase<T, ALLOC>
164 {
165 public:
166  using this_type = NonHeapHolder<T, ALLOC>;
167 
168  static this_type* create(void* storage)
169  {
170  return new (storage) this_type();
171  }
172 
173  IHolder<ALLOC>* clone(const ALLOC&) const override
174  {
175  throw CppRuntimeException("BasicAny: Unexpected clone call.");
176  }
177 
178  IHolder<ALLOC>* clone(void* storage) const override
179  {
180  NonHeapHolder* holder = new (storage) NonHeapHolder();
181  holder->setHolder(this->getHolder());
182  return holder;
183  }
184 
185  IHolder<ALLOC>* move(const ALLOC&) override
186  {
187  throw CppRuntimeException("BasicAny: Unexpected move call.");
188  }
189 
190  IHolder<ALLOC>* move(void* storage) override
191  {
192  NonHeapHolder* holder = new (storage) NonHeapHolder();
193  holder->setHolder(std::move(this->getHolder()));
194  return holder;
195  }
196 
197  void destroy(const ALLOC&) override
198  {
199  this->~NonHeapHolder();
200  }
201 
202 private:
203  NonHeapHolder() = default;
204 };
205 
206 template <typename ALLOC>
207 union UntypedHolder
208 {
209  // 2 * sizeof(void*) for T + sizeof(void*) for Holder's vptr
210  using MaxInPlaceType = std::aligned_storage<3 * sizeof(void*), alignof(void*)>::type;
211 
212  detail::IHolder<ALLOC>* heap;
213  MaxInPlaceType inPlace;
214 };
215 
216 template <typename T, typename ALLOC>
217 using has_non_heap_holder = std::integral_constant<bool,
218  sizeof(NonHeapHolder<T, ALLOC>) <= sizeof(typename UntypedHolder<ALLOC>::MaxInPlaceType) &&
219  std::is_nothrow_move_constructible<T>::value &&
220  alignof(T) <= alignof(typename UntypedHolder<ALLOC>::MaxInPlaceType)>;
221 
222 } // namespace detail
223 
227 template <typename ALLOC = std::allocator<uint8_t>>
228 class BasicAny : public AllocatorHolder<ALLOC>
229 {
230  using AllocTraits = std::allocator_traits<ALLOC>;
233 
234 public:
236  using allocator_type = ALLOC;
237 
242  BasicAny(ALLOC())
243  {}
244 
248  explicit BasicAny(const ALLOC& allocator) :
249  AllocatorHolder<ALLOC>(allocator)
250  {
251  m_untypedHolder.heap = nullptr;
252  }
253 
259  template <typename T,
260  typename std::enable_if<!std::is_same<typename std::decay<T>::type, BasicAny>::value &&
261  !std::is_same<typename std::decay<T>::type, ALLOC>::value,
262  int>::type = 0>
263  explicit BasicAny(T&& value, const ALLOC& allocator = ALLOC()) :
264  AllocatorHolder<ALLOC>(allocator)
265  {
266  m_untypedHolder.heap = nullptr;
267  set(std::forward<T>(value));
268  }
269 
274  {
275  clearHolder();
276  }
277 
283  BasicAny(const BasicAny& other) :
284  AllocatorHolder<ALLOC>(
285  AllocTraits::select_on_container_copy_construction(other.get_allocator_ref()))
286  {
287  copy(other);
288  }
289 
296  BasicAny(const BasicAny& other, const ALLOC& allocator) :
297  AllocatorHolder<ALLOC>(allocator)
298  {
299  copy(other);
300  }
301 
309  BasicAny& operator=(const BasicAny& other)
310  {
311  if (this != &other)
312  {
313  // TODO: do not dealloc unless necessary
314  clearHolder();
315  if constexpr (AllocTraits::propagate_on_container_copy_assignment::value)
316  {
318  }
319  copy(other);
320  }
321 
322  return *this;
323  }
324 
330  BasicAny(BasicAny&& other) noexcept :
331  AllocatorHolder<ALLOC>(std::move(other.get_allocator_ref()))
332  {
333  move(std::move(other));
334  }
335 
342  BasicAny(BasicAny&& other, const ALLOC& allocator) :
343  AllocatorHolder<ALLOC>(allocator)
344  {
345  move(std::move(other));
346  }
347 
356  {
357  if (this != &other)
358  {
359  clearHolder();
360  if constexpr (AllocTraits::propagate_on_container_move_assignment::value)
361  {
362  set_allocator(std::move(other.get_allocator_ref()));
363  }
364  move(std::move(other));
365  }
366 
367  return *this;
368  }
369 
377  template <typename T,
378  typename std::enable_if<!std::is_same<typename std::decay<T>::type, BasicAny>::value, int>::type =
379  0>
380  BasicAny& operator=(T&& value)
381  {
382  set(std::forward<T>(value));
383 
384  return *this;
385  }
386 
390  void reset()
391  {
392  clearHolder();
393  }
394 
400  template <typename T>
401  void set(T&& value)
402  {
403  createHolder<typename std::decay<T>::type>()->set(std::forward<T>(value));
404  }
405 
413  template <typename T>
414  T& get()
415  {
416  checkType<T>();
417  return getHolder<T>(detail::has_non_heap_holder<T, ALLOC>())->get();
418  }
419 
427  template <typename T>
428  const T& get() const
429  {
430  checkType<T>();
431  return getHolder<T>(detail::has_non_heap_holder<T, ALLOC>())->get();
432  }
433 
439  template <typename T>
440  bool isType() const
441  {
442  return hasHolder() && getUntypedHolder()->isType(detail::TypeIdHolder::get<T>());
443  }
444 
450  bool hasValue() const
451  {
452  return hasHolder();
453  }
454 
455 private:
456  void copy(const BasicAny& other)
457  {
458  if (other.m_isInPlace)
459  {
460  other.getUntypedHolder()->clone(&m_untypedHolder.inPlace);
461  m_isInPlace = true;
462  }
463  else if (other.m_untypedHolder.heap != nullptr)
464  {
465  m_untypedHolder.heap = other.getUntypedHolder()->clone(get_allocator_ref());
466  }
467  else
468  {
469  m_untypedHolder.heap = nullptr;
470  }
471  }
472 
473  void move(BasicAny&& other)
474  {
475  if (other.m_isInPlace)
476  {
477  other.getUntypedHolder()->move(&m_untypedHolder.inPlace);
478  m_isInPlace = true;
479  other.clearHolder();
480  }
481  else if (other.m_untypedHolder.heap != nullptr)
482  {
483  if (get_allocator_ref() == other.get_allocator_ref())
484  {
485  // take over the other's storage
486  m_untypedHolder.heap = other.m_untypedHolder.heap;
487  other.m_untypedHolder.heap = nullptr;
488  }
489  else
490  {
491  // cannot steal the storage, allocate our own and move the holder
492  m_untypedHolder.heap = other.getUntypedHolder()->move(get_allocator_ref());
493  other.clearHolder();
494  }
495  }
496  else
497  {
498  m_untypedHolder.heap = nullptr;
499  }
500  }
501 
502  void clearHolder()
503  {
504  if (hasHolder())
505  {
506  getUntypedHolder()->destroy(get_allocator_ref());
507  m_isInPlace = false;
508  m_untypedHolder.heap = nullptr;
509  }
510  }
511 
512  bool hasHolder() const
513  {
514  return (m_isInPlace || m_untypedHolder.heap != nullptr);
515  }
516 
517  template <typename T>
518  detail::HolderBase<T, ALLOC>* createHolder()
519  {
520  if (hasHolder())
521  {
522  if (getUntypedHolder()->isType(detail::TypeIdHolder::get<T>()))
523  {
524  return getHolder<T>(detail::has_non_heap_holder<T, ALLOC>());
525  }
526 
527  clearHolder();
528  }
529 
530  return createHolderImpl<T>(detail::has_non_heap_holder<T, ALLOC>());
531  }
532 
533  template <typename T>
534  detail::HolderBase<T, ALLOC>* createHolderImpl(std::true_type)
535  {
536  detail::NonHeapHolder<T, ALLOC>* holder =
537  detail::NonHeapHolder<T, ALLOC>::create(&m_untypedHolder.inPlace);
538  m_isInPlace = true;
539  return holder;
540  }
541 
542  template <typename T>
543  detail::HolderBase<T, ALLOC>* createHolderImpl(std::false_type)
544  {
545  detail::HeapHolder<T, ALLOC>* holder = detail::HeapHolder<T, ALLOC>::create(get_allocator_ref());
546  m_untypedHolder.heap = holder;
547  return holder;
548  }
549 
550  template <typename T>
551  void checkType() const
552  {
553  if (!isType<T>())
554  {
555  throwBadType();
556  }
557  }
558 
560  void throwBadType() const
561  {
562  throw CppRuntimeException("Bad type in BasicAny");
563  }
564 
565  template <typename T>
566  detail::HeapHolder<T, ALLOC>* getHeapHolder()
567  {
568  return static_cast<detail::HeapHolder<T, ALLOC>*>(m_untypedHolder.heap);
569  }
570 
571  template <typename T>
572  const detail::HeapHolder<T, ALLOC>* getHeapHolder() const
573  {
574  return static_cast<detail::HeapHolder<T, ALLOC>*>(m_untypedHolder.heap);
575  }
576 
577  template <typename T>
578  detail::NonHeapHolder<T, ALLOC>* getInplaceHolder()
579  {
580  return reinterpret_cast<detail::NonHeapHolder<T, ALLOC>*>(&m_untypedHolder.inPlace);
581  }
582 
583  template <typename T>
584  const detail::NonHeapHolder<T, ALLOC>* getInplaceHolder() const
585  {
586  return reinterpret_cast<const detail::NonHeapHolder<T, ALLOC>*>(&m_untypedHolder.inPlace);
587  }
588 
589  template <typename T>
590  detail::HolderBase<T, ALLOC>* getHolder(std::true_type)
591  {
592  return static_cast<detail::HolderBase<T, ALLOC>*>(getInplaceHolder<T>());
593  }
594 
595  template <typename T>
596  detail::HolderBase<T, ALLOC>* getHolder(std::false_type)
597  {
598  return static_cast<detail::HolderBase<T, ALLOC>*>(getHeapHolder<T>());
599  }
600 
601  template <typename T>
602  const detail::HolderBase<T, ALLOC>* getHolder(std::true_type) const
603  {
604  return static_cast<const detail::HolderBase<T, ALLOC>*>(getInplaceHolder<T>());
605  }
606 
607  template <typename T>
608  const detail::HolderBase<T, ALLOC>* getHolder(std::false_type) const
609  {
610  return static_cast<const detail::HolderBase<T, ALLOC>*>(getHeapHolder<T>());
611  }
612 
613  detail::IHolder<ALLOC>* getUntypedHolder()
614  {
615  return (m_isInPlace)
616  ? reinterpret_cast<detail::IHolder<ALLOC>*>(&m_untypedHolder.inPlace)
617  : m_untypedHolder.heap;
618  }
619 
620  const detail::IHolder<ALLOC>* getUntypedHolder() const
621  {
622  return (m_isInPlace)
623  ? reinterpret_cast<const detail::IHolder<ALLOC>*>(&m_untypedHolder.inPlace)
624  : m_untypedHolder.heap;
625  }
626 
627  detail::UntypedHolder<ALLOC> m_untypedHolder;
628  bool m_isInPlace = false;
629 };
630 
632 using Any = BasicAny<>;
633 
634 } // namespace zserio
635 
636 #endif // ifndef ZSERIO_ANY_H_INC
void set_allocator(const allocator_type &allocator)
BasicAny(const BasicAny &other)
Definition: Any.h:283
bool isType() const
Definition: Any.h:440
const T & get() const
Definition: Any.h:428
T & get()
Definition: Any.h:414
bool hasValue() const
Definition: Any.h:450
BasicAny(const BasicAny &other, const ALLOC &allocator)
Definition: Any.h:296
BasicAny & operator=(BasicAny &&other)
Definition: Any.h:355
BasicAny(T &&value, const ALLOC &allocator=ALLOC())
Definition: Any.h:263
BasicAny & operator=(const BasicAny &other)
Definition: Any.h:309
BasicAny(BasicAny &&other) noexcept
Definition: Any.h:330
void set(T &&value)
Definition: Any.h:401
ALLOC allocator_type
Definition: Any.h:236
void reset()
Definition: Any.h:390
BasicAny(const ALLOC &allocator)
Definition: Any.h:248
BasicAny & operator=(T &&value)
Definition: Any.h:380
BasicAny(BasicAny &&other, const ALLOC &allocator)
Definition: Any.h:342
decltype(auto) get(BasicVariant< ALLOC, INDEX, T... > &var)
Definition: Variant.h:812