Zserio C++17 runtime library  1.1.0
Built for Zserio 2.18.1
Variant.h
Go to the documentation of this file.
1 #ifndef ZSERIO_VARIANT_H_INC
2 #define ZSERIO_VARIANT_H_INC
3 
5 #include "zserio/Any.h"
7 #include "zserio/HashCodeUtil.h"
8 #include "zserio/Traits.h"
9 
10 namespace zserio
11 {
12 
13 namespace detail
14 {
15 
16 template <size_t I, typename... T>
17 struct type_at
18 {
19  using type = std::tuple_element_t<I, std::tuple<T...>>;
20 };
21 
22 template <auto I, typename... T>
23 using type_at_t = typename type_at<static_cast<size_t>(I), T...>::type;
24 
25 template <size_t N, typename V, typename F>
26 void for_active_item(const V&, F&&, std::false_type)
27 {}
28 
29 template <size_t N, typename V, typename F>
30 void for_active_item(const V& var, F&& fun, std::true_type = {})
31 {
32  using INDEX = typename V::IndexType;
33  if (static_cast<size_t>(var.index()) == N - 1)
34  {
35  std::forward<F>(fun)(*var.template get_if<static_cast<INDEX>(N - 1)>());
36  }
37  for_active_item<N - 1>(var, std::forward<F>(fun), std::bool_constant<(N - 1 > 0)>());
38 }
39 
40 template <size_t N, typename V, typename F>
41 void for_active_item(V& var, F&& fun, std::true_type = {})
42 {
43  using INDEX = typename V::IndexType;
44  if (static_cast<size_t>(var.index()) == N - 1)
45  {
46  std::forward<F>(fun)(*var.template get_if<static_cast<INDEX>(N - 1)>());
47  }
48  for_active_item<N - 1>(var, std::forward<F>(fun), std::bool_constant<(N - 1 > 0)>());
49 }
50 
51 template <size_t N, typename V, typename F>
52 void for_active_item_2(const V&, const V&, F&&, std::false_type)
53 {}
54 
55 template <size_t N, typename V, typename F>
56 void for_active_item_2(const V& var1, const V& var2, F&& fun, std::true_type = {})
57 {
58  // assert(var1.index() == var2.index());
59  using INDEX = typename V::IndexType;
60  if (static_cast<size_t>(var1.index()) == N - 1)
61  {
62  std::forward<F>(fun)(*var1.template get_if<static_cast<INDEX>(N - 1)>(),
63  *var2.template get_if<static_cast<INDEX>(N - 1)>());
64  }
65  for_active_item_2<N - 1>(var1, var2, std::forward<F>(fun), std::bool_constant<(N - 1 > 0)>());
66 }
67 
68 } // namespace detail
69 
74 {
75 public:
77 
78  ~BadVariantAccess() override;
79 };
80 
81 template <auto I>
83 {};
84 
88 template <auto I>
90 
101 template <typename ALLOC, typename INDEX, typename... T>
103 {
104  using AllocTraits = std::allocator_traits<ALLOC>;
105 
106 public:
107  using allocator_type = ALLOC;
108  using IndexType = INDEX;
109 
114  BasicVariant(ALLOC())
115  {}
116 
124  explicit BasicVariant(const ALLOC& allocator) :
125  m_data(allocator)
126  {
127  emplace<INDEX{}>(); // enforce no empty state like std::variant
128  }
129 
138  template <INDEX I, typename... ARGS, std::enable_if_t<!is_first_allocator_v<ARGS...>>* = nullptr>
139  explicit BasicVariant(in_place_index_t<I>, ARGS&&... args)
140  {
141  emplace<I>(std::forward<ARGS>(args)...);
142  }
143 
153  template <INDEX I, typename... ARGS>
154  BasicVariant(in_place_index_t<I>, const ALLOC& allocator, ARGS&&... args) :
155  m_data(allocator)
156  {
157  emplace<I>(std::forward<ARGS>(args)...);
158  }
159 
165  BasicVariant(const BasicVariant& other) :
166  m_data(other.m_data),
167  m_index(other.m_index)
168  {}
169 
178  BasicVariant(const BasicVariant& other, const ALLOC& allocator) :
179  m_data(other.m_data, allocator),
180  m_index(other.m_index)
181  {}
182 
193  {
194  m_data = other.m_data;
195  m_index = other.m_index;
196  return *this;
197  }
198 
207  m_data(std::move(other.m_data)),
208  m_index(other.m_index)
209  {}
210 
219  BasicVariant(BasicVariant&& other, const ALLOC& allocator) :
220  m_data(std::move(other.m_data), allocator),
221  m_index(other.m_index)
222  {}
223 
227  ~BasicVariant() = default;
228 
237  {
238  if (this != &other)
239  {
240  m_data = std::move(other.m_data);
241  m_index = other.m_index;
242  }
243 
244  return *this;
245  }
246 
255  bool valueless_by_exception() const noexcept
256  {
257  return static_cast<size_t>(index()) == std::variant_npos || !m_data.hasValue();
258  }
259 
265  template <INDEX I, typename... ARGS>
266  decltype(auto) emplace(ARGS&&... args)
267  {
268  using U = detail::type_at_t<I, T...>;
269  auto& ret = m_data.template emplace<U>(std::forward<ARGS>(args)...);
270  m_index = I;
271  return ret;
272  }
273 
277  INDEX index() const noexcept
278  {
279  return m_data.hasValue() ? m_index : static_cast<INDEX>(std::variant_npos);
280  }
281 
285  template <INDEX I, typename U = detail::type_at_t<I, T...>>
286  U* get_if() noexcept
287  {
288  if (I != index())
289  {
290  return nullptr;
291  }
292  return m_data.template get_if<U>();
293  }
294 
298  template <INDEX I, typename U = detail::type_at_t<I, T...>>
299  const U* get_if() const noexcept
300  {
301  if (I != index())
302  {
303  return nullptr;
304  }
305  return m_data.template get_if<U>();
306  }
307 
313  template <INDEX I, typename U = detail::type_at_t<I, T...>>
314  U& get()
315  {
316  auto ptr = get_if<I>();
317  if (!ptr)
318  {
319  throw BadVariantAccess("Variant: Attempt to retrieve an inactive element at index ")
320  << static_cast<size_t>(I) << ". Active element index is " << static_cast<size_t>(index());
321  }
322  return *ptr;
323  }
324 
330  template <INDEX I, typename U = detail::type_at_t<I, T...>>
331  const U& get() const
332  {
333  auto ptr = get_if<I>();
334  if (!ptr)
335  {
336  throw BadVariantAccess("Variant: Attempt to retrieve an inactive element at index ")
337  << static_cast<size_t>(I) << ". Active element index is " << static_cast<size_t>(index());
338  }
339  return *ptr;
340  }
341 
347  void swap(BasicVariant& other)
348  {
349  m_data.swap(other.m_data);
350  std::swap(m_index, other.m_index);
351  }
352 
360  template <typename F>
361  auto visit(F&& fun) -> decltype(fun(std::declval<detail::type_at_t<0, T...>>()))
362  {
364  {
365  throw BadVariantAccess("Variant: Cannot visit variant in valueless state");
366  }
367  using R = decltype(fun(std::declval<detail::type_at_t<0, T...>>()));
368  if constexpr (std::is_same_v<R, void>)
369  {
370  detail::for_active_item<sizeof...(T)>(*this, [func = std::forward<F>(fun)](auto& elem) {
371  std::move(func)(elem);
372  });
373  }
374  else
375  {
376  R ret;
377  detail::for_active_item<sizeof...(T)>(*this, [&ret, func = std::forward<F>(fun)](auto& elem) {
378  ret = std::move(func)(elem);
379  });
380  return ret;
381  }
382  }
383 
391  template <typename F>
392  auto visit(F&& fun) const -> decltype(fun(std::declval<detail::type_at_t<0, T...>>()))
393  {
395  {
396  throw BadVariantAccess("Variant: Cannot visit variant in valueless state");
397  }
398  using R = decltype(fun(std::declval<detail::type_at_t<0, T...>>()));
399  if constexpr (std::is_same_v<R, void>)
400  {
401  detail::for_active_item<sizeof...(T)>(*this, [func = std::forward<F>(fun)](auto& elem) {
402  std::move(func)(elem);
403  });
404  }
405  else
406  {
407  R ret;
408  detail::for_active_item<sizeof...(T)>(*this, [&ret, func = std::forward<F>(fun)](auto& elem) {
409  ret = std::move(func)(elem);
410  });
411  return ret;
412  }
413  }
414 
420  bool operator==(const BasicVariant& other) const
421  {
422  if (index() != other.index())
423  {
424  return false;
425  }
426 
427  bool ret = false;
428  detail::for_active_item_2<sizeof...(T)>(*this, other, [&ret](auto& elem1, auto& elem2) {
429  ret = elem1 == elem2;
430  });
431  return ret;
432  }
433 
439  bool operator!=(const BasicVariant& other) const
440  {
441  return !(*this == other);
442  }
443 
449  bool operator<(const BasicVariant& other) const
450  {
451  if (index() != other.index())
452  {
453  return index() < other.index();
454  }
455 
456  bool ret = false;
457  detail::for_active_item_2<sizeof...(T)>(*this, other, [&ret](auto& elem1, auto& elem2) {
458  ret = elem1 < elem2;
459  });
460  return ret;
461  }
462 
468  bool operator>(const BasicVariant& other) const
469  {
470  return other < *this;
471  }
472 
478  bool operator<=(const BasicVariant& other) const
479  {
480  return !(other < *this);
481  }
482 
488  bool operator>=(const BasicVariant& other) const
489  {
490  return !(other > *this);
491  }
492 
493 private:
494  BasicAny<ALLOC> m_data;
495  INDEX m_index;
496 };
497 
498 // Using declarations
499 
500 template <typename INDEX, typename... T>
502 
510 template <auto I, typename ALLOC, typename INDEX, typename... T>
511 decltype(auto) get(BasicVariant<ALLOC, INDEX, T...>& var)
512 {
513  static_assert(std::is_same_v<decltype(I), INDEX>, "Index has a wrong type");
514  return var.template get<I>();
515 }
516 
524 template <auto I, typename ALLOC, typename INDEX, typename... T>
525 decltype(auto) get(const BasicVariant<ALLOC, INDEX, T...>& var)
526 {
527  static_assert(std::is_same_v<decltype(I), INDEX>, "Index has a wrong type");
528  return var.template get<I>();
529 }
530 
536 template <auto I, typename ALLOC, typename INDEX, typename... T>
537 decltype(auto) get_if(BasicVariant<ALLOC, INDEX, T...>* var)
538 {
539  static_assert(std::is_same_v<decltype(I), INDEX>, "Index has a wrong type");
540  return var->template get_if<I>();
541 }
542 
548 template <auto I, typename ALLOC, typename INDEX, typename... T>
549 decltype(auto) get_if(const BasicVariant<ALLOC, INDEX, T...>* var)
550 {
551  static_assert(std::is_same_v<decltype(I), INDEX>, "Index has a wrong type");
552  return var->template get_if<I>();
553 }
554 
563 template <typename F, typename ALLOC, typename INDEX, typename... T>
564 decltype(auto) visit(F&& fun, BasicVariant<ALLOC, INDEX, T...>& var)
565 {
566  return var.visit(std::forward<F>(fun));
567 }
568 
577 template <typename F, typename ALLOC, typename INDEX, typename... T>
578 decltype(auto) visit(F&& fun, const BasicVariant<ALLOC, INDEX, T...>& var)
579 {
580  return var.visit(std::forward<F>(fun));
581 }
582 
589 template <typename ALLOC, typename INDEX, typename... T>
590 uint32_t calcHashCode(uint32_t seed, const BasicVariant<ALLOC, INDEX, T...>& var)
591 {
592  uint32_t result = seed;
593  result = calcHashCode(result, static_cast<size_t>(var.index()));
594  var.visit([&result](const auto& value) {
595  result = calcHashCode(result, value);
596  });
597  return result;
598 }
599 
600 } // namespace zserio
601 
602 namespace std
603 {
604 
605 template <typename ALLOC, typename INDEX, typename... T>
606 struct hash<zserio::BasicVariant<ALLOC, INDEX, T...>>
607 {
609  {
610  return zserio::calcHashCode(zserio::HASH_SEED, var);
611  }
612 };
613 
614 } // namespace std
615 
616 #endif // ZSERIO_VARIANT_H_INC
bool hasValue() const noexcept
Definition: Any.h:594
void swap(BasicAny &other)
Definition: Any.h:452
auto visit(F &&fun) const -> decltype(fun(std::declval< detail::type_at_t< 0, T... >>()))
Definition: Variant.h:392
BasicVariant(const BasicVariant &other, const ALLOC &allocator)
Definition: Variant.h:178
void swap(BasicVariant &other)
Definition: Variant.h:347
BasicVariant(BasicVariant &&other, const ALLOC &allocator)
Definition: Variant.h:219
BasicVariant(const ALLOC &allocator)
Definition: Variant.h:124
bool operator<=(const BasicVariant &other) const
Definition: Variant.h:478
bool operator<(const BasicVariant &other) const
Definition: Variant.h:449
bool operator>(const BasicVariant &other) const
Definition: Variant.h:468
BasicVariant & operator=(BasicVariant &&other)
Definition: Variant.h:236
BasicVariant(in_place_index_t< I >, ARGS &&... args)
Definition: Variant.h:139
U * get_if() noexcept
Definition: Variant.h:286
bool valueless_by_exception() const noexcept
Definition: Variant.h:255
BasicVariant & operator=(const BasicVariant &other)
Definition: Variant.h:192
BasicVariant(in_place_index_t< I >, const ALLOC &allocator, ARGS &&... args)
Definition: Variant.h:154
bool operator==(const BasicVariant &other) const
Definition: Variant.h:420
const U * get_if() const noexcept
Definition: Variant.h:299
bool operator>=(const BasicVariant &other) const
Definition: Variant.h:488
auto visit(F &&fun) -> decltype(fun(std::declval< detail::type_at_t< 0, T... >>()))
Definition: Variant.h:361
INDEX index() const noexcept
Definition: Variant.h:277
BasicVariant(const BasicVariant &other)
Definition: Variant.h:165
BasicVariant(BasicVariant &&other)
Definition: Variant.h:206
decltype(auto) emplace(ARGS &&... args)
Definition: Variant.h:266
const U & get() const
Definition: Variant.h:331
bool operator!=(const BasicVariant &other) const
Definition: Variant.h:439
CppRuntimeException(const char *message="")
Definition: BitBuffer.h:602
decltype(auto) visit(F &&fun, BasicVariant< ALLOC, INDEX, T... > &var)
Definition: Variant.h:564
constexpr in_place_index_t< I > in_place_index
Definition: Variant.h:89
decltype(auto) get(BasicVariant< ALLOC, INDEX, T... > &var)
Definition: Variant.h:511
uint32_t calcHashCode(uint32_t seedValue, const ArrayView< T, ARRAY_TRAITS > &array)
Definition: ArrayView.h:882
decltype(auto) get_if(BasicVariant< ALLOC, INDEX, T... > *var)
Definition: Variant.h:537
constexpr bool is_first_allocator_v
Definition: Traits.h:122
size_t operator()(const zserio::BasicVariant< ALLOC, INDEX, T... > &var) const
Definition: Variant.h:608