Serac  0.1
Serac is an implicit thermal strucural mechanics simulation code.
variant.hpp
Go to the documentation of this file.
1 // Copyright (c) 2019-2024, Lawrence Livermore National Security, LLC and
2 // other Serac Project Developers. See the top-level LICENSE file for
3 // details.
4 //
5 // SPDX-License-Identifier: (BSD-3-Clause)
6 
18 #pragma once
19 
20 #include <memory>
21 #include <type_traits>
22 
23 namespace serac {
24 
25 namespace detail {
26 
33 template <typename T0, typename T1, typename SFINAE = void>
38  int index_ = 0;
39  union {
40  T0 t0_;
41  T1 t1_;
42  };
43 
49  constexpr variant_storage(const variant_storage& other) : index_(other.index_)
50  {
51  switch (index_) {
52  case 0: {
53  new (&t0_) T0(other.t0_);
54  break;
55  }
56  case 1: {
57  new (&t1_) T1(other.t1_);
58  break;
59  }
60  }
61  };
62 
68  constexpr variant_storage(variant_storage&& other) : index_(other.index_)
69  {
70  switch (index_) {
71  case 0: {
72  new (&t0_) T0(std::move(other.t0_));
73  break;
74  }
75  case 1: {
76  new (&t1_) T1(std::move(other.t1_));
77  break;
78  }
79  }
80  };
81 
86  constexpr void clear()
87  {
88  switch (index_) {
89  case 0: {
90  t0_.~T0();
91  break;
92  }
93  case 1: {
94  t1_.~T1();
95  break;
96  }
97  }
98  }
99 
104  constexpr variant_storage() : index_{0}, t0_{} {}
105 
110 };
111 
115 template <typename T0, typename T1>
116 struct variant_storage<T0, T1,
117  std::enable_if_t<std::is_trivially_destructible_v<T0> && std::is_trivially_destructible_v<T1>>> {
121  int index_ = 0;
122  union {
126  T0 t0_;
130  T1 t1_;
131  };
132 
137  constexpr variant_storage() : index_{0}, t0_{} {}
138 
142  constexpr void clear() {}
143 };
144 
151 template <typename T, typename T0, typename T1>
156  constexpr static bool value = std::is_same_v<std::decay_t<T>, T0> || std::is_assignable_v<T0, T> ||
157  std::is_same_v<std::decay_t<T>, T1> || std::is_assignable_v<T1, T>;
158 };
159 
160 } // namespace detail
161 
168 template <int I, typename T0, typename T1>
170 
176 template <typename T0, typename T1>
177 struct variant_alternative<0, T0, T1> {
181  using type = T0;
182 };
183 
189 template <typename T0, typename T1>
190 struct variant_alternative<1, T0, T1> {
194  using type = T1;
195 };
196 
206 template <typename T0, typename T1>
207 struct variant {
212 
216  constexpr variant() = default;
217 
222  constexpr variant(const variant&) = default;
223 
227  constexpr variant(variant&&) = default;
228 
237  template <typename T, typename SFINAE = std::enable_if_t<detail::is_variant_assignable<T, T0, T1>::value>>
238  constexpr variant(T&& t)
239  {
240  if constexpr (std::is_same_v<std::decay_t<T>, T0> || std::is_assignable_v<T0, T>) {
241  storage_.index_ = 0;
242  new (&storage_.t0_) T0(std::forward<T>(t));
243  } else if constexpr (std::is_same_v<std::decay_t<T>, T1> || std::is_assignable_v<T1, T>) {
244  storage_.index_ = 1;
245  new (&storage_.t1_) T1(std::forward<T>(t));
246  } else {
247  static_assert(sizeof(T) < 0, "Type not supported");
248  }
249  }
250 
257  constexpr variant& operator=(const variant&) = default;
258 
264  constexpr variant& operator=(variant&&) = default;
265 
272  template <typename T, typename SFINAE = std::enable_if_t<detail::is_variant_assignable<T, T0, T1>::value>>
273  constexpr variant& operator=(T&& t)
274  {
275  if constexpr (std::is_same_v<std::decay_t<T>, T0> || std::is_assignable_v<T0, T>) {
276  if (storage_.index_ != 0) {
277  storage_.clear();
278  }
279  storage_.t0_ = std::forward<T>(t);
280  storage_.index_ = 0;
281  } else if constexpr (std::is_same_v<std::decay_t<T>, T1> || std::is_assignable_v<T1, T>) {
282  if (storage_.index_ != 1) {
283  storage_.clear();
284  }
285  storage_.t1_ = std::forward<T>(t);
286  storage_.index_ = 1;
287  } else {
288  static_assert(sizeof(T) < 0, "Type not supported");
289  }
290  return *this;
291  }
292 
296  constexpr int index() const { return storage_.index_; }
297 
303  template <int I>
304  friend constexpr typename variant_alternative<I, T0, T1>::type& get(variant& v)
305  {
306  if constexpr (I == 0) {
307  return v.storage_.t0_;
308  }
309  if constexpr (I == 1) {
310  return v.storage_.t1_;
311  }
312  }
313 
315  template <int I>
316  friend constexpr const typename variant_alternative<I, T0, T1>::type& get(const variant& v)
317  {
318  if constexpr (I == 0) {
319  return v.storage_.t0_;
320  }
321  if constexpr (I == 1) {
322  return v.storage_.t1_;
323  }
324  }
325 };
326 
337 template <typename T, typename T0, typename T1>
338 constexpr T& get(variant<T0, T1>& v)
339 {
340  if constexpr (std::is_same_v<T, T0>) {
341  return get<0>(v);
342  } else if constexpr (std::is_same_v<T, T1>) {
343  return get<1>(v);
344  }
345 }
346 
348 template <typename T, typename T0, typename T1>
349 constexpr const T& get(const variant<T0, T1>& v)
350 {
351  if constexpr (std::is_same_v<T, T0>) {
352  return get<0>(v);
353  } else if constexpr (std::is_same_v<T, T1>) {
354  return get<1>(v);
355  }
356 }
357 
364 template <typename Visitor, typename Variant>
365 constexpr decltype(auto) visit(Visitor visitor, Variant&& v)
366 {
367  if (v.index() == 0) {
368  return visitor(get<0>(v));
369  } else {
370  return visitor(get<1>(v));
371  }
372 }
373 
380 template <typename T, typename T0, typename T1>
382 {
383  if constexpr (std::is_same_v<T, T0>) {
384  return v.index() == 0;
385  } else if constexpr (std::is_same_v<T, T1>) {
386  return v.index() == 1;
387  }
388  return false;
389 }
390 
397 template <typename T, typename T0, typename T1>
399 {
400  if constexpr (std::is_same_v<T, T0>) {
401  return (v->index() == 0) ? &get<0>(*v) : nullptr;
402  } else if constexpr (std::is_same_v<T, T1>) {
403  return (v->index() == 1) ? &get<1>(*v) : nullptr;
404  }
405  return nullptr;
406 }
407 
409 template <typename T, typename T0, typename T1>
410 const T* get_if(const variant<T0, T1>* v)
411 {
412  if constexpr (std::is_same_v<T, T0>) {
413  return (v->index() == 0) ? &get<0>(*v) : nullptr;
414  } else if constexpr (std::is_same_v<T, T1>) {
415  return (v->index() == 1) ? &get<1>(*v) : nullptr;
416  }
417  return nullptr;
418 }
419 
420 namespace detail {
421 
435 template <typename T>
437 
442 template <typename T>
443 static T& retrieve(MaybeOwningPointer<T>& obj)
444 {
445  return visit([](auto&& ptr) -> T& { return *ptr; }, obj);
446 }
448 template <typename T>
449 static const T& retrieve(const MaybeOwningPointer<T>& obj)
450 {
451  return visit([](auto&& ptr) -> const T& { return *ptr; }, obj);
452 }
453 
454 } // namespace detail
455 } // namespace serac
Accelerator functionality.
Definition: serac.cpp:38
constexpr T & get(variant< T0, T1 > &v)
Returns the variant member of specified type.
Definition: variant.hpp:338
T * get_if(variant< T0, T1 > *v)
Returns the member of requested type if it's active, otherwise nullptr.
Definition: variant.hpp:398
bool holds_alternative(const variant< T0, T1 > &v)
Checks whether a variant's active member is of a certain type.
Definition: variant.hpp:381
constexpr decltype(auto) visit(Visitor visitor, Variant &&v)
Applies a functor to the active variant element.
Definition: variant.hpp:365
Determines if T can be assigned to a variant<T0, T1>
Definition: variant.hpp:152
constexpr static bool value
If T can be assigned to the variant type.
Definition: variant.hpp:156
Storage abstraction to provide trivial destructor when both variant types are trivially destructible.
Definition: variant.hpp:34
constexpr variant_storage(const variant_storage &other)
Copy constructor for nontrivial types Placement-new is required to correctly initialize the union.
Definition: variant.hpp:49
constexpr variant_storage()
Default constructor Default initializes the first member of the variant.
Definition: variant.hpp:104
constexpr variant_storage(variant_storage &&other)
Move constructor for nontrivial types Placement-new is required to correctly initialize the union.
Definition: variant.hpp:68
~variant_storage()
Destroys the variant by calling the destructor of the active member.
Definition: variant.hpp:109
constexpr void clear()
Resets the union by destroying the active member.
Definition: variant.hpp:86
int index_
The index of the active member.
Definition: variant.hpp:38
T0 type
The type of the first member.
Definition: variant.hpp:181
T1 type
The type of the second member.
Definition: variant.hpp:194
Obtains the type at index I of a variant<T0, T1>
Definition: variant.hpp:169
A simple variant type that supports only two elements.
Definition: variant.hpp:207
constexpr variant()=default
Default constructor - will default-initialize first member of the variant.
constexpr int index() const
Returns the index of the active variant member.
Definition: variant.hpp:296
constexpr friend variant_alternative< I, T0, T1 >::type & get(variant &v)
Returns the variant member at the provided index.
Definition: variant.hpp:304
constexpr variant(T &&t)
"Parameterized" constructor with which a value can be assigned
Definition: variant.hpp:238
constexpr variant & operator=(variant &&)=default
Default assignment operator.
constexpr friend const variant_alternative< I, T0, T1 >::type & get(const variant &v)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition: variant.hpp:316
constexpr variant & operator=(const variant &)=default
Default assignment operator.
constexpr variant(variant &&)=default
Default constructor.
detail::variant_storage< T0, T1 > storage_
Storage abstraction used to provide constexpr functionality when applicable.
Definition: variant.hpp:211
constexpr variant & operator=(T &&t)
Definition: variant.hpp:273
constexpr variant(const variant &)=default
Default constructor.