SPH
Variant.h
Go to the documentation of this file.
1 #pragma once
2 
7 
8 #include "common/Assert.h"
9 #include "common/Globals.h"
10 #include "math/MathUtils.h"
12 
14 
17 template <typename... TArgs>
18 class AlignedUnion {
19 private:
20  std::aligned_union_t<4, TArgs...> storage;
21 
22 public:
26  template <typename T, typename... Ts>
27  INLINE void emplace(Ts&&... args) {
28  SPH_ASSERT(isAligned(storage));
29  new (&storage) T(std::forward<Ts>(args)...);
30  }
31 
35  template <typename T>
36  INLINE void destroy() {
37  get<T>().~T();
38  }
39 
43  template <typename T>
44  INLINE T& get() {
45  return reinterpret_cast<T&>(storage);
46  }
47 
49  template <typename T>
50  INLINE const T& get() const {
51  return reinterpret_cast<const T&>(storage);
52  }
53 };
54 
56 namespace VariantHelpers {
57 
59 template <typename... TArgs>
60 struct DefaultCreate {
61  AlignedUnion<TArgs...>& storage;
62 
63  template <typename T>
64  void visit() {
65  storage.template emplace<T>();
66  }
67 };
68 
70 template <typename... TArgs>
71 struct Delete {
72  AlignedUnion<TArgs...>& storage;
73 
74  template <typename T>
75  void visit() {
76  storage.template destroy<T>();
77  }
78 };
79 
81 template <typename... TArgs>
83  AlignedUnion<TArgs...>& storage;
84 
85  template <typename T, typename TOther>
86  void visit(const TOther& other) {
87  storage.template emplace<T>(other.template get<T>());
88  }
89 
90  template <typename T,
91  typename TOther,
92  typename = std::enable_if_t<!std::is_lvalue_reference<TOther>::value>>
93  void visit(TOther&& other) {
94  storage.template emplace<T>(std::move(other.template get<T>()));
95  }
96 };
97 
99 template <typename... TArgs>
100 struct Assign {
101  AlignedUnion<TArgs...>& storage;
102 
103  template <typename T, typename TOther>
104  void visit(TOther&& other) {
105  using TRaw = std::decay_t<TOther>;
106  storage.template get<TRaw>() = std::forward<TOther>(other);
107  }
108 };
109 
111 template <typename... TArgs>
113  AlignedUnion<TArgs...>& storage;
114 
115  template <typename T, typename TOther>
116  void visit(const TOther& other) {
117  storage.template get<T>() = other.template get<T>();
118  }
119 
120  template <typename T,
121  typename TOther,
122  typename = std::enable_if_t<!std::is_lvalue_reference<TOther>::value>>
123  void visit(TOther&& other) {
124  storage.template get<T>() = std::move(other.template get<T>());
125  }
126 };
127 
129 template <typename... TArgs>
130 struct Swap {
131  AlignedUnion<TArgs...>& storage;
132 
133  template <typename T, typename TOther>
134  void visit(TOther&& other) {
135  std::swap(storage.template get<T>(), other.template get<T>());
136  }
137 };
138 
139 } // namespace VariantHelpers
140 
143 template <typename T0, typename... TArgs>
145  template <typename TVisitor, typename... Ts>
146  static void visit(Size idx, TVisitor&& visitor, Ts&&... args) {
147  if (idx == 0) {
148  visitor.template visit<T0>(std::forward<Ts>(args)...);
149  } else {
151  idx - 1, std::forward<TVisitor>(visitor), std::forward<Ts>(args)...);
152  }
153  }
154 };
156 template <typename T0>
157 struct VariantIterator<T0> {
158  template <typename TVisitor, typename... Ts>
159  static void visit(Size idx, TVisitor&& visitor, Ts&&... args) {
160  SPH_ASSERT(idx == 0);
161  visitor.template visit<T0>(std::forward<Ts>(args)...);
162  }
163 };
164 
166 static constexpr struct ConstructTypeIdxTag {
167 } CONSTRUCT_TYPE_IDX;
168 
170 template <typename... TArgs>
171 class Variant {
172 private:
173  AlignedUnion<TArgs...> storage;
174  int typeIdx;
175 
176  void destroy() {
177  VariantHelpers::Delete<TArgs...> deleter{ storage };
178  VariantIterator<TArgs...>::visit(typeIdx, deleter);
179  }
180 
181  static_assert(sizeof...(TArgs) >= 1, "Must have at least 1 variant");
182 
183 public:
189  using T = SelectType<0, TArgs...>;
190  storage.template emplace<T>();
191  typeIdx = 0;
192  }
193 
195  template <typename T, typename = std::enable_if_t<!std::is_same<std::decay_t<T>, Variant>::value>>
196  Variant(T&& value) {
197  using RawT = std::decay_t<T>;
198  constexpr int idx = getTypeIndex<RawT, TArgs...>;
199  static_assert(idx != -1, "Type must be listed in Variant");
200  storage.template emplace<RawT>(std::forward<T>(value));
201  typeIdx = idx;
202  }
203 
207  Variant(const ConstructTypeIdxTag, const Size typeIdx)
208  : typeIdx(typeIdx) {
209  SPH_ASSERT(typeIdx < sizeof...(TArgs));
210  VariantHelpers::DefaultCreate<TArgs...> creator{ storage };
211  VariantIterator<TArgs...>::visit(typeIdx, creator);
212  }
213 
214  Variant(const Variant& other) {
215  VariantHelpers::CopyMoveCreate<TArgs...> creator{ storage };
216  VariantIterator<TArgs...>::visit(other.typeIdx, creator, other);
217  typeIdx = other.typeIdx;
218  }
219 
220  Variant(Variant&& other) {
221  VariantHelpers::CopyMoveCreate<TArgs...> creator{ storage };
222  VariantIterator<TArgs...>::visit(other.typeIdx, creator, std::move(other));
223  typeIdx = other.typeIdx;
224  }
225 
227  destroy();
228  }
229 
235  template <typename T, typename = std::enable_if_t<!std::is_same<std::decay_t<T>, Variant>::value>>
236  Variant& operator=(T&& value) {
237  using RawT = std::decay_t<T>;
238  constexpr int idx = getTypeIndex<RawT, TArgs...>;
239  static_assert(idx != -1, "Type must be listed in Variant");
240  if (int(typeIdx) != idx) {
241  // different type, destroy current and re-create
242  destroy();
243  storage.template emplace<RawT>(std::forward<T>(value));
244  } else {
245  // same type, can utilize assignment operator
246  storage.template get<RawT>() = std::forward<T>(value);
247  }
248  typeIdx = idx;
249  return *this;
250  }
251 
257  Variant& operator=(const Variant& other) {
258  if (typeIdx != other.typeIdx) {
259  destroy();
260  VariantHelpers::CopyMoveCreate<TArgs...> creator{ storage };
261  VariantIterator<TArgs...>::visit(other.typeIdx, creator, other);
262  } else {
263  VariantHelpers::CopyMoveAssign<TArgs...> assigner{ storage };
264  VariantIterator<TArgs...>::visit(other.typeIdx, assigner, other);
265  }
266  typeIdx = other.typeIdx;
267  return *this;
268  }
269 
275  if (typeIdx != other.typeIdx) {
276  destroy();
277  VariantHelpers::CopyMoveCreate<TArgs...> creator{ storage };
278  VariantIterator<TArgs...>::visit(other.typeIdx, creator, std::move(other));
279  } else {
280  VariantHelpers::CopyMoveAssign<TArgs...> assigner{ storage };
281  VariantIterator<TArgs...>::visit(other.typeIdx, assigner, std::move(other));
282  }
283  typeIdx = other.typeIdx;
284  return *this;
285  }
286 
290  void swap(Variant& other) {
291  SPH_ASSERT(typeIdx == other.typeIdx);
292  VariantHelpers::Swap<TArgs...> swapper{ storage };
293  VariantIterator<TArgs...>::visit(typeIdx, swapper, other);
294  }
295 
299  template <typename T, typename... Ts>
300  void emplace(Ts&&... args) {
301  using RawT = std::decay_t<T>;
302  constexpr int idx = getTypeIndex<RawT, TArgs...>;
303  static_assert(idx != -1, "Type must be listed in Variant");
304  destroy();
305  storage.template emplace<RawT>(std::forward<Ts>(args)...);
306  typeIdx = idx;
307  }
308 
311  return typeIdx;
312  }
313 
315  template <typename T>
316  INLINE bool has() const {
317  constexpr int idx = getTypeIndex<std::decay_t<T>, TArgs...>;
318  return typeIdx == idx;
319  }
320 
324  template <typename T>
325  INLINE static constexpr bool canHold() {
326  using RawT = std::decay_t<T>;
327  return getTypeIndex<RawT, TArgs...> != -1;
328  }
329 
334  template <typename T>
335  INLINE T& get() {
336  constexpr int idx = getTypeIndex<std::decay_t<T>, TArgs...>;
337  static_assert(idx != -1, "Cannot convert variant to this type");
338  SPH_ASSERT(typeIdx == idx);
339  return storage.template get<T>();
340  }
341 
343  template <typename T>
344  INLINE const T& get() const {
345  constexpr int idx = getTypeIndex<std::decay_t<T>, TArgs...>;
346  static_assert(idx != -1, "Cannot convert variant to this type");
347  SPH_ASSERT(typeIdx == idx);
348  return storage.template get<T>();
349  }
350 
355  template <typename T, typename = std::enable_if_t<!std::is_same<std::decay_t<T>, Variant>::value>>
356  operator T&() {
357  return get<T>();
358  }
359 
361  template <typename T, typename = std::enable_if_t<!std::is_same<std::decay_t<T>, Variant>::value>>
362  operator const T&() const {
363  return get<T>();
364  }
365 
370  template <typename T>
371  Optional<T> tryGet() const {
372  constexpr int idx = getTypeIndex<std::decay_t<T>, TArgs...>;
373  static_assert(idx != -1, "Cannot convert variant to this type");
374  if (typeIdx != getTypeIndex<std::decay_t<T>, TArgs...>) {
375  return NOTHING;
376  }
377  return storage.template get<T>();
378  }
379 };
380 
381 
382 namespace Detail {
383 template <Size N, typename T0, typename... TArgs>
384 struct ForValue {
385  template <typename TVariant, typename TFunctor>
386  INLINE static decltype(auto) action(TVariant&& variant, TFunctor&& functor) {
387  if (variant.getTypeIdx() == N) {
388  return functor(variant.template get<T0>());
389  } else {
391  std::forward<TVariant>(variant), std::forward<TFunctor>(functor));
392  }
393  }
394 };
395 
396 // specialization for NothingType, does not call the functor
397 template <Size N, typename... TArgs>
398 struct ForValue<N, NothingType, TArgs...> {
399  template <typename TVariant, typename TFunctor>
400  INLINE static decltype(auto) action(TVariant&& variant, TFunctor&& functor) {
401  if (variant.getTypeIdx() != N) {
403  std::forward<TVariant>(variant), std::forward<TFunctor>(functor));
404  }
405  STOP;
406  }
407 };
408 
409 template <Size N, typename T0>
410 struct ForValue<N, T0> {
411  template <typename TVariant, typename TFunctor>
412  INLINE static decltype(auto) action(TVariant&& variant, TFunctor&& functor) {
413  return functor(variant.template get<T0>());
414  }
415 };
416 } // namespace Detail
417 
427 template <typename TFunctor, typename... TArgs>
428 INLINE decltype(auto) forValue(Variant<TArgs...>& variant, TFunctor&& functor) {
429  return Detail::ForValue<0, TArgs...>::action(variant, std::forward<TFunctor>(functor));
430 }
431 
434 template <typename TFunctor, typename... TArgs>
435 INLINE decltype(auto) forValue(const Variant<TArgs...>& variant, TFunctor&& functor) {
436  return Detail::ForValue<0, TArgs...>::action(variant, std::forward<TFunctor>(functor));
437 }
438 
440 
442 namespace std {
443 template <typename... TArgs>
444 void swap(Sph::Variant<TArgs...>& v1, Sph::Variant<TArgs...>& v2) {
445  v1.swap(v2);
446 }
447 } // namespace std
INLINE bool isAligned(const T &value)
Custom assertions.
#define SPH_ASSERT(x,...)
Definition: Assert.h:94
#define STOP
Definition: Assert.h:106
NAMESPACE_SPH_BEGIN
Definition: BarnesHut.cpp:13
Global parameters of the code.
uint32_t Size
Integral type used to index arrays (by default).
Definition: Globals.h:16
Additional math routines (with more includes).
#define INLINE
Macros for conditional compilation based on selected compiler.
Definition: Object.h:31
#define NAMESPACE_SPH_END
Definition: Object.h:12
Wrapper of type value of which may or may not be present.
const NothingType NOTHING
Definition: Optional.h:16
constexpr int N
typename TypeSelector< n, TArgs... >::Type SelectType
Definition: Traits.h:27
constexpr int getTypeIndex
Definition: Traits.h:41
decltype(auto) INLINE forValue(Variant< TArgs... > &variant, TFunctor &&functor)
Definition: Variant.h:428
Stores value of type std::aligned_union, having size and alignment equal to maximum of sizes and alig...
Definition: Variant.h:18
INLINE void destroy()
Destroys value of type T.
Definition: Variant.h:36
INLINE void emplace(Ts &&... args)
Creates value of type T in place.
Definition: Variant.h:27
INLINE const T & get() const
Converts stored value to a reference of type T.
Definition: Variant.h:50
INLINE T & get()
Converts stored value to a reference of type T.
Definition: Variant.h:44
Wrapper of type value of which may or may not be present.
Definition: Optional.h:23
Variant, an implementation of type-safe union, similar to std::variant or boost::variant.
Definition: Variant.h:171
Variant(const ConstructTypeIdxTag, const Size typeIdx)
Default-constructs a type with given type index.
Definition: Variant.h:207
void swap(Variant &other)
Swaps value stored in the variant with value of different variant.
Definition: Variant.h:290
INLINE const T & get() const
Returns the stored value, const version.
Definition: Variant.h:344
INLINE Size getTypeIdx() const
Returns index of type currently stored in variant.
Definition: Variant.h:310
Variant & operator=(T &&value)
Universal copy/move operator with type of rhs being one of stored types.
Definition: Variant.h:236
static constexpr INLINE bool canHold()
Checks if the given type is one of the listed ones.
Definition: Variant.h:325
Optional< T > tryGet() const
Returns the stored value in the variant.
Definition: Variant.h:371
Variant()
Default constructor, constructs the object of type listed first in the type list using its default co...
Definition: Variant.h:188
INLINE bool has() const
Checks if the variant currently hold value of given type.
Definition: Variant.h:316
void emplace(Ts &&... args)
Creates a value of type T in place.
Definition: Variant.h:300
Variant & operator=(const Variant &other)
Assigns another variant into this.
Definition: Variant.h:257
Variant & operator=(Variant &&other)
Moves another variant into this.
Definition: Variant.h:274
Variant(T &&value)
Construct variant from value of stored type.
Definition: Variant.h:196
INLINE T & get()
Returns the stored value.
Definition: Variant.h:335
Variant(Variant &&other)
Definition: Variant.h:220
Variant(const Variant &other)
Definition: Variant.h:214
~Variant()
Definition: Variant.h:226
Helper visitors creating, deleting or modifying Variant.
Definition: Variant.h:56
Overload of std::swap for Sph::Array.
Definition: Array.h:578
void swap(Sph::Array< T, TCounter > &ar1, Sph::Array< T, TCounter > &ar2)
Definition: Array.h:580
static decltype(auto) INLINE action(TVariant &&variant, TFunctor &&functor)
Definition: Variant.h:386
Assigns a value type of which can be stored in variant.
Definition: Variant.h:100
void visit(TOther &&other)
Definition: Variant.h:104
AlignedUnion< TArgs... > & storage
Definition: Variant.h:101
Creates a variant by copying/moving value currently stored in other variant.
Definition: Variant.h:112
AlignedUnion< TArgs... > & storage
Definition: Variant.h:113
void visit(const TOther &other)
Definition: Variant.h:116
void visit(TOther &&other)
Definition: Variant.h:123
Creates a variant by copying/moving value currently stored in other variant.
Definition: Variant.h:82
void visit(const TOther &other)
Definition: Variant.h:86
void visit(TOther &&other)
Definition: Variant.h:93
AlignedUnion< TArgs... > & storage
Definition: Variant.h:83
Creates a variant using default constructor.
Definition: Variant.h:60
AlignedUnion< TArgs... > & storage
Definition: Variant.h:61
Destroys the object currently stored in variant.
Definition: Variant.h:71
AlignedUnion< TArgs... > & storage
Definition: Variant.h:72
Swaps content of two variants.
Definition: Variant.h:130
void visit(TOther &&other)
Definition: Variant.h:134
AlignedUnion< TArgs... > & storage
Definition: Variant.h:131
static void visit(Size idx, TVisitor &&visitor, Ts &&... args)
Definition: Variant.h:159
Iterates through types of variant until idx-th type is found, and executes given TVisitor,...
Definition: Variant.h:144
static void visit(Size idx, TVisitor &&visitor, Ts &&... args)
Definition: Variant.h:146