VTK-m  1.9
VariantImpl.h
Go to the documentation of this file.
1 //============================================================================
2 // Copyright (c) Kitware, Inc.
3 // All rights reserved.
4 // See LICENSE.txt for details.
5 //
6 // This software is distributed WITHOUT ANY WARRANTY; without even
7 // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
8 // PURPOSE. See the above copyright notice for more information.
9 //============================================================================
10 
11 #if !defined(VTK_M_DEVICE) || !defined(VTK_M_NAMESPACE)
12 #error VariantImpl.h must be included from Variant.h
13 // Some defines to make my IDE happy.
14 #define VTK_M_DEVICE
15 #define VTK_M_NAMESPACE tmp
16 #endif
17 
19 
20 #include <vtkm/Deprecated.h>
21 #include <vtkm/List.h>
22 
23 #include <vtkm/internal/Assume.h>
24 
25 namespace vtkm
26 {
27 namespace VTK_M_NAMESPACE
28 {
29 namespace internal
30 {
31 
32 // Forward declaration
33 template <typename... Ts>
34 class Variant;
35 
36 namespace detail
37 {
38 
39 // --------------------------------------------------------------------------------
40 // Helper classes for Variant
41 
42 template <typename UnionType>
43 struct VariantUnionToListImpl;
44 template <typename... Ts>
45 struct VariantUnionToListImpl<detail::VariantUnionTD<Ts...>>
46 {
47  using type = vtkm::List<Ts...>;
48 };
49 template <typename... Ts>
50 struct VariantUnionToListImpl<detail::VariantUnionNTD<Ts...>>
51 {
52  using type = vtkm::List<Ts...>;
53 };
54 
55 template <typename UnionType>
56 using VariantUnionToList =
57  typename VariantUnionToListImpl<typename std::decay<UnionType>::type>::type;
58 
59 struct VariantCopyConstructFunctor
60 {
61  template <typename T, typename UnionType>
62  VTK_M_DEVICE void operator()(const T& src, UnionType& destUnion) const noexcept
63  {
65  // If we are using this functor, we can assume the union does not hold a valid type.
66  new (&VariantUnionGet<Index>(destUnion)) T(src);
67  }
68 };
69 
70 struct VariantCopyFunctor
71 {
72  template <typename T, typename UnionType>
73  VTK_M_DEVICE void operator()(const T& src, UnionType& destUnion) const noexcept
74  {
76  // If we are using this functor, we can assume the union holds type T.
77  this->DoCopy(
78  src, VariantUnionGet<Index>(destUnion), typename std::is_copy_assignable<T>::type{});
79  }
80 
81  template <typename T>
82  VTK_M_DEVICE void DoCopy(const T& src, T& dest, std::true_type) const noexcept
83  {
84  dest = src;
85  }
86 
87  template <typename T>
88  VTK_M_DEVICE void DoCopy(const T& src, T& dest, std::false_type) const noexcept
89  {
90  if (&src != &dest)
91  {
92  // Do not have an assignment operator, so destroy the old object and create a new one.
93  dest.~T();
94  new (&dest) T(src);
95  }
96  else
97  {
98  // Objects are already the same.
99  }
100  }
101 };
102 
103 struct VariantDestroyFunctor
104 {
105  template <typename T>
106  VTK_M_DEVICE void operator()(T& src) const noexcept
107  {
108  src.~T();
109  }
110 };
111 
112 template <typename T>
113 struct VariantCheckType
114 {
115  // We are currently not allowing reference types (e.g. FooType&) or pointer types (e.g. FooType*)
116  // in Variant objects. References and pointers can fail badly when things are passed around
117  // devices. If you get a compiler error here, consider removing the reference or using something
118  // like std::decay to remove qualifiers on the type. (We may decide to do that automatically in
119  // the future.)
120  VTKM_STATIC_ASSERT_MSG(!std::is_reference<T>::value,
121  "References are not allowed in VTK-m Variant.");
122  VTKM_STATIC_ASSERT_MSG(!std::is_pointer<T>::value, "Pointers are not allowed in VTK-m Variant.");
123 };
124 
125 template <typename VariantType>
126 struct VariantTriviallyCopyable;
127 
128 template <typename... Ts>
129 struct VariantTriviallyCopyable<vtkm::VTK_M_NAMESPACE::internal::Variant<Ts...>>
130  : AllTriviallyCopyable<Ts...>
131 {
132 };
133 
134 template <typename VariantType>
135 struct VariantTriviallyConstructible;
136 
137 template <typename... Ts>
138 struct VariantTriviallyConstructible<vtkm::VTK_M_NAMESPACE::internal::Variant<Ts...>>
139  : AllTriviallyConstructible<Ts...>
140 {
141 };
142 
143 // --------------------------------------------------------------------------------
144 // Variant superclass that defines its storage
145 template <typename... Ts>
146 struct VariantStorageImpl
147 {
148  VariantUnion<Ts...> Storage;
150 
151  VariantStorageImpl() = default;
152 
153  VTK_M_DEVICE VariantStorageImpl(vtkm::internal::NullType dummy)
154  : Storage({ dummy })
155  {
156  }
157 
158  template <vtkm::IdComponent Index>
159  using TypeAt = typename vtkm::ListAt<vtkm::List<Ts...>, Index>;
160 
161  VTK_M_DEVICE vtkm::IdComponent GetIndex() const noexcept { return this->Index; }
162  VTK_M_DEVICE bool IsValid() const noexcept
163  {
164  return (this->Index >= 0) && (this->Index < static_cast<vtkm::IdComponent>(sizeof...(Ts)));
165  }
166 
167  VTK_M_DEVICE void Reset() noexcept
168  {
169  if (this->IsValid())
170  {
171  this->CastAndCall(detail::VariantDestroyFunctor{});
172  this->Index = -1;
173  }
174  }
175 
176  template <typename Functor, typename... Args>
177  VTK_M_DEVICE auto CastAndCall(Functor&& f, Args&&... args) const
178  noexcept(noexcept(f(std::declval<const TypeAt<0>&>(), args...)))
179  -> decltype(f(std::declval<const TypeAt<0>&>(), args...))
180  {
181  VTKM_ASSERT(this->IsValid());
182  return detail::VariantCastAndCallImpl<sizeof...(Ts)>(
183  this->GetIndex(), std::forward<Functor>(f), this->Storage, std::forward<Args>(args)...);
184  }
185 
186  template <typename Functor, typename... Args>
187  VTK_M_DEVICE auto CastAndCall(Functor&& f, Args&&... args) noexcept(
188  noexcept(f(std::declval<const TypeAt<0>&>(), args...)))
189  -> decltype(f(std::declval<TypeAt<0>&>(), args...))
190  {
191  VTKM_ASSERT(this->IsValid());
192  return detail::VariantCastAndCallImpl<sizeof...(Ts)>(
193  this->GetIndex(), std::forward<Functor>(f), this->Storage, std::forward<Args>(args)...);
194  }
195 };
196 
197 // --------------------------------------------------------------------------------
198 // Variant superclass that helps preserve trivially copyable and trivially constructable
199 // properties where possible.
200 template <typename VariantType,
201  typename TriviallyConstructible =
202  typename VariantTriviallyConstructible<VariantType>::type,
203  typename TriviallyCopyable = typename VariantTriviallyCopyable<VariantType>::type>
204 struct VariantConstructorImpl;
205 
206 // Can trivially construct, deconstruct, and copy all data. (Probably all trivial classes.)
207 template <typename... Ts>
208 struct VariantConstructorImpl<vtkm::VTK_M_NAMESPACE::internal::Variant<Ts...>,
209  std::true_type,
210  std::true_type> : VariantStorageImpl<Ts...>
211 {
212  VariantConstructorImpl() = default;
213  ~VariantConstructorImpl() = default;
214 
215  VariantConstructorImpl(const VariantConstructorImpl&) = default;
216  VariantConstructorImpl(VariantConstructorImpl&&) = default;
217  VariantConstructorImpl& operator=(const VariantConstructorImpl&) = default;
218  VariantConstructorImpl& operator=(VariantConstructorImpl&&) = default;
219 };
220 
221 // Can trivially copy, but cannot trivially construct. Common if a class is simple but
222 // initializes itself.
223 template <typename... Ts>
224 struct VariantConstructorImpl<vtkm::VTK_M_NAMESPACE::internal::Variant<Ts...>,
225  std::false_type,
226  std::true_type> : VariantStorageImpl<Ts...>
227 {
228  VTK_M_DEVICE VariantConstructorImpl()
229  : VariantStorageImpl<Ts...>(vtkm::internal::NullType{})
230  {
231  this->Index = -1;
232  }
233 
234  // Any trivially copyable class is trivially destructable.
235  ~VariantConstructorImpl() = default;
236 
237  VariantConstructorImpl(const VariantConstructorImpl&) = default;
238  VariantConstructorImpl(VariantConstructorImpl&&) = default;
239  VariantConstructorImpl& operator=(const VariantConstructorImpl&) = default;
240  VariantConstructorImpl& operator=(VariantConstructorImpl&&) = default;
241 };
242 
243 // Cannot trivially copy. We assume we cannot trivially construct/destruct.
244 template <typename construct_type, typename... Ts>
245 struct VariantConstructorImpl<vtkm::VTK_M_NAMESPACE::internal::Variant<Ts...>,
246  construct_type,
247  std::false_type> : VariantStorageImpl<Ts...>
248 {
249  VTK_M_DEVICE VariantConstructorImpl()
250  : VariantStorageImpl<Ts...>(vtkm::internal::NullType{})
251  {
252  this->Index = -1;
253  }
254  VTK_M_DEVICE ~VariantConstructorImpl() { this->Reset(); }
255 
256  VTK_M_DEVICE VariantConstructorImpl(const VariantConstructorImpl& src) noexcept
257  : VariantStorageImpl<Ts...>(vtkm::internal::NullType{})
258  {
259  if (src.IsValid())
260  {
261  src.CastAndCall(VariantCopyConstructFunctor{}, this->Storage);
262  }
263  this->Index = src.Index;
264  }
265 
266  VTK_M_DEVICE VariantConstructorImpl& operator=(const VariantConstructorImpl& src) noexcept
267  {
268  if (src.IsValid())
269  {
270  if (this->GetIndex() == src.GetIndex())
271  {
272  src.CastAndCall(detail::VariantCopyFunctor{}, this->Storage);
273  }
274  else
275  {
276  this->Reset();
277  src.CastAndCall(detail::VariantCopyConstructFunctor{}, this->Storage);
278  this->Index = src.Index;
279  }
280  }
281  else
282  {
283  this->Reset();
284  }
285  return *this;
286  }
287 };
288 
289 } // namespace detail
290 
291 template <typename... Ts>
292 class Variant : detail::VariantConstructorImpl<Variant<Ts...>>
293 {
294  using Superclass = detail::VariantConstructorImpl<Variant<Ts...>>;
295 
296  // Type not used, but has the compiler check all the types for validity.
297  using CheckTypes = vtkm::List<detail::VariantCheckType<Ts>...>;
298 
299 public:
302  template <typename T>
303  using IndexOf = vtkm::ListIndexOf<vtkm::List<Ts...>, T>;
304 
307  template <typename T>
308  VTK_M_DEVICE static constexpr vtkm::IdComponent GetIndexOf()
309  {
310  return IndexOf<T>::value;
311  }
312 
315  template <vtkm::IdComponent Index>
316  using TypeAt = typename vtkm::ListAt<vtkm::List<Ts...>, Index>;
317 
323  template <typename T>
324  using CanStore = std::integral_constant<bool, (IndexOf<T>::value >= 0)>;
325 
328  template <typename T>
329  VTK_M_DEVICE static constexpr bool GetCanStore()
330  {
331  return CanStore<T>::value;
332  }
333 
336  static constexpr vtkm::IdComponent NumberOfTypes = vtkm::IdComponent{ sizeof...(Ts) };
337 
341  VTK_M_DEVICE vtkm::IdComponent GetIndex() const noexcept { return this->Index; }
342 
350  VTK_M_DEVICE bool IsValid() const noexcept
351  {
352  return (this->Index >= 0) && (this->Index < NumberOfTypes);
353  }
354 
357  template <typename T>
358  VTK_M_DEVICE bool IsType() const
359  {
360  return (this->GetIndex() == this->GetIndexOf<T>());
361  }
362 
363  Variant() = default;
364  ~Variant() = default;
365  Variant(const Variant&) = default;
366  Variant(Variant&&) = default;
367  Variant& operator=(const Variant&) = default;
368  Variant& operator=(Variant&&) = default;
369 
370  template <typename T>
371  VTK_M_DEVICE Variant(const T& src) noexcept
372  {
373  constexpr vtkm::IdComponent index = GetIndexOf<T>();
374  // Might be a way to use an enable_if to enforce a proper type.
375  VTKM_STATIC_ASSERT_MSG(index >= 0, "Attempting to put invalid type into a Variant");
376 
377  this->Index = index;
378  new (&this->Get<index>()) T(src);
379  }
380 
381  template <typename T>
382  VTK_M_DEVICE Variant& operator=(const T& src)
383  {
384  if (this->IsType<T>())
385  {
386  this->Get<T>() = src;
387  }
388  else
389  {
390  this->Emplace<T>(src);
391  }
392  return *this;
393  }
394 
395  template <typename T, typename... Args>
396  VTK_M_DEVICE T& Emplace(Args&&... args)
397  {
398  constexpr vtkm::IdComponent I = GetIndexOf<T>();
399  VTKM_STATIC_ASSERT_MSG(I >= 0, "Variant::Emplace called with invalid type.");
400  return this->EmplaceImpl<T, I>(std::forward<Args>(args)...);
401  }
402 
403  template <typename T, typename U, typename... Args>
404  VTK_M_DEVICE T& Emplace(std::initializer_list<U> il, Args&&... args)
405  {
406  constexpr vtkm::IdComponent I = GetIndexOf<T>();
407  VTKM_STATIC_ASSERT_MSG(I >= 0, "Variant::Emplace called with invalid type.");
408  return this->EmplaceImpl<T, I>(il, std::forward<Args>(args)...);
409  }
410 
411  template <vtkm::IdComponent I, typename... Args>
412  VTK_M_DEVICE TypeAt<I>& Emplace(Args&&... args)
413  {
414  VTKM_STATIC_ASSERT_MSG((I >= 0) && (I < NumberOfTypes),
415  "Variant::Emplace called with invalid index");
416  return this->EmplaceImpl<TypeAt<I>, I>(std::forward<Args>(args)...);
417  }
418 
419  template <vtkm::IdComponent I, typename U, typename... Args>
420  VTK_M_DEVICE TypeAt<I>& Emplace(std::initializer_list<U> il, Args&&... args)
421  {
422  VTKM_STATIC_ASSERT_MSG((I >= 0) && (I < NumberOfTypes),
423  "Variant::Emplace called with invalid index");
424  return this->EmplaceImpl<TypeAt<I>, I>(il, std::forward<Args>(args)...);
425  }
426 
427 private:
428  template <typename T, vtkm::IdComponent I, typename... Args>
429  VTK_M_DEVICE T& EmplaceImpl(Args&&... args)
430  {
431  this->Reset();
432  this->Index = I;
433  return *(new (&this->Get<I>()) T{ args... });
434  }
435 
436  template <typename T, vtkm::IdComponent I, typename U, typename... Args>
437  VTK_M_DEVICE T& EmplaceImpl(std::initializer_list<U> il, Args&&... args)
438  {
439  this->Reset();
440  this->Index = I;
441  return *(new (&this->Get<I>()) T(il, args...));
442  }
443 
444 public:
449  template <vtkm::IdComponent I>
450  VTK_M_DEVICE TypeAt<I>& Get() noexcept
451  {
452  VTKM_ASSERT(I == this->GetIndex());
453  return detail::VariantUnionGet<I>(this->Storage);
454  }
455 
456  template <vtkm::IdComponent I>
457  VTK_M_DEVICE const TypeAt<I>& Get() const noexcept
458  {
459  VTKM_ASSERT(I == this->GetIndex());
460  return detail::VariantUnionGet<I>(this->Storage);
461  }
463 
468  template <typename T>
469  VTK_M_DEVICE T& Get() noexcept
470  {
471  return this->GetImpl<T>(CanStore<T>{});
472  }
473 
474  template <typename T>
475  VTK_M_DEVICE const T& Get() const noexcept
476  {
477  return this->GetImpl<T>(CanStore<T>{});
478  }
480 
481 private:
482  template <typename T>
483  VTK_M_DEVICE T& GetImpl(std::true_type)
484  {
485  VTKM_ASSERT(this->IsType<T>());
486  return detail::VariantUnionGet<IndexOf<T>::value>(this->Storage);
487  }
488 
489  template <typename T>
490  VTK_M_DEVICE const T& GetImpl(std::true_type) const
491  {
492  VTKM_ASSERT(this->IsType<T>());
493  return detail::VariantUnionGet<IndexOf<T>::value>(this->Storage);
494  }
495 
496  // This function overload only gets created if you attempt to pull a type from a
497  // variant that does not exist. Perhaps this should be a compile error, but there
498  // are cases where you might create templated code that has a path that could call
499  // this but never does. To make this case easier, do a runtime error (when asserts
500  // are active) instead.
501  template <typename T>
502  VTK_M_DEVICE T& GetImpl(std::false_type) const
503  {
504  VTKM_ASSERT(false &&
505  "Attempted to get a type from a variant that the variant does not contain.");
506  // This will cause some _really_ nasty issues if you actually try to use the returned type.
507  return *reinterpret_cast<T*>(0);
508  }
509 
510 public:
518  template <typename Functor, typename... Args>
519  VTK_M_DEVICE auto CastAndCall(Functor&& f, Args&&... args) const
520  noexcept(noexcept(f(std::declval<const TypeAt<0>&>(), args...)))
521  {
522  VTKM_ASSERT(this->IsValid());
523  return detail::VariantCastAndCallImpl<sizeof...(Ts)>(
524  this->GetIndex(), std::forward<Functor>(f), this->Storage, std::forward<Args>(args)...);
525  }
526 
527  template <typename Functor, typename... Args>
528  VTK_M_DEVICE auto CastAndCall(Functor&& f,
529  Args&&... args) noexcept(noexcept(f(std::declval<TypeAt<0>&>(),
530  args...)))
531  {
532  VTKM_ASSERT(this->IsValid());
533  return detail::VariantCastAndCallImpl<sizeof...(Ts)>(
534  this->GetIndex(), std::forward<Functor>(f), this->Storage, std::forward<Args>(args)...);
535  }
536 
540  VTK_M_DEVICE void Reset() noexcept
541  {
542  if (this->IsValid())
543  {
544  this->CastAndCall(detail::VariantDestroyFunctor{});
545  this->Index = -1;
546  }
547  }
548 };
549 
554 template <typename ListTag>
555 using ListTagAsVariant VTKM_DEPRECATED(
556  1.6,
557  "vtkm::ListTag is no longer supported. Use vtkm::List instead.") =
559 
562 template <typename List>
564 }
565 }
566 } // namespace vtkm::VTK_M_NAMESPACE::internal
567 
568 #undef VTK_M_DEVICE
569 #undef VTK_M_NAMESPACE
vtkm
VTKM_NO_DEPRECATED_VIRTUAL.
Definition: Algorithms.h:18
VTK_M_DEVICE
#define VTK_M_DEVICE
Definition: VariantImpl.h:14
VTKM_ASSERT
#define VTKM_ASSERT(condition)
Definition: Assert.h:43
vtkm::Get
VTKM_SUPPRESS_EXEC_WARNINGS VTKM_EXEC_CONT auto Get(const vtkm::Tuple< Ts... > &tuple) -> decltype(tuple.template Get< Index >())
Retrieve the object from a vtkm::Tuple at the given index.
Definition: Tuple.h:83
vtkm::IdComponent
vtkm::Int32 IdComponent
Represents a component ID (index of component in a vector).
Definition: Types.h:168
VTK_M_NAMESPACE
#define VTK_M_NAMESPACE
Definition: VariantImpl.h:15
vtkm::VTKM_DEPRECATED
struct VTKM_DEPRECATED(1.8, "IntegrateOver is no longer supported") IntegrateOver
Definition: mesh_info/CellMeasures.h:79
Assume.h
vtkm::ListApply
typename detail::ListApplyImpl< internal::AsList< List >, Target >::type ListApply
Applies the list of types to a template.
Definition: List.h:161
VTKM_STATIC_ASSERT_MSG
#define VTKM_STATIC_ASSERT_MSG(condition, message)
Definition: StaticAssert.h:18
Index
int Index
Definition: ChooseCudaDevice.h:87
VariantImplDetail.h
vtkm::ListAt
typename detail::ListAtImpl< internal::AsList< List >, Index >::type ListAt
Finds the type at the given index.
Definition: List.h:373
vtkm::ListIndexOf
typename detail::ListIndexOfImpl< internal::AsList< List >, T >::type ListIndexOf
Finds the index of a given type.
Definition: List.h:576
Deprecated.h
vtkm::List
Definition: List.h:34
vtkm::cont::IsType
VTKM_CONT bool IsType(const vtkm::cont::UnknownArrayHandle &array)
Returns true if variant matches the type of ArrayHandleType.
Definition: UnknownArrayHandle.h:1147
vtkm::cont::CastAndCall
VTKM_DEPRECATED_SUPPRESS_BEGIN class VTKM_ALWAYS_EXPORT VTKM_DEPRECATED(1.6, "Virtual ArrayHandles are being phased out.") ArrayHandleVirtualCoordinates final void CastAndCall(const vtkm::cont::ArrayHandleVirtualCoordinates &coords, Functor &&f, Args &&... args)
ArrayHandleVirtualCoordinates is a specialization of ArrayHandle.
Definition: ArrayHandleVirtualCoordinates.h:52
List.h