VTK-m  2.2
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/List.h>
21 
22 #include <vtkm/internal/Assume.h>
23 
24 namespace vtkm
25 {
26 namespace VTK_M_NAMESPACE
27 {
28 
29 // Forward declaration
30 template <typename... Ts>
31 class Variant;
32 
33 namespace detail
34 {
35 
36 // --------------------------------------------------------------------------------
37 // Helper classes for Variant
38 
39 template <typename UnionType>
40 struct VariantUnionToListImpl;
41 template <typename... Ts>
42 struct VariantUnionToListImpl<detail::VariantUnionTD<Ts...>>
43 {
44  using type = vtkm::List<Ts...>;
45 };
46 template <typename... Ts>
47 struct VariantUnionToListImpl<detail::VariantUnionNTD<Ts...>>
48 {
49  using type = vtkm::List<Ts...>;
50 };
51 
52 template <typename UnionType>
53 using VariantUnionToList =
54  typename VariantUnionToListImpl<typename std::decay<UnionType>::type>::type;
55 
56 struct VariantCopyConstructFunctor
57 {
58  template <typename T, typename UnionType>
59  VTK_M_DEVICE void operator()(const T& src, UnionType& destUnion) const noexcept
60  {
62  // If we are using this functor, we can assume the union does not hold a valid type.
63  new (&VariantUnionGet<Index>(destUnion)) T(src);
64  }
65 };
66 
67 struct VariantCopyFunctor
68 {
69  template <typename T, typename UnionType>
70  VTK_M_DEVICE void operator()(const T& src, UnionType& destUnion) const noexcept
71  {
73  // If we are using this functor, we can assume the union holds type T.
74  this->DoCopy(
75  src, VariantUnionGet<Index>(destUnion), typename std::is_copy_assignable<T>::type{});
76  }
77 
78  template <typename T>
79  VTK_M_DEVICE void DoCopy(const T& src, T& dest, std::true_type) const noexcept
80  {
81  dest = src;
82  }
83 
84  template <typename T>
85  VTK_M_DEVICE void DoCopy(const T& src, T& dest, std::false_type) const noexcept
86  {
87  if (&src != &dest)
88  {
89  // Do not have an assignment operator, so destroy the old object and create a new one.
90  dest.~T();
91  new (&dest) T(src);
92  }
93  else
94  {
95  // Objects are already the same.
96  }
97  }
98 };
99 
100 struct VariantDestroyFunctor
101 {
102  template <typename T>
103  VTK_M_DEVICE void operator()(T& src) const noexcept
104  {
105  src.~T();
106  }
107 };
108 
109 template <typename T>
110 struct VariantCheckType
111 {
112  // We are currently not allowing reference types (e.g. FooType&) or pointer types (e.g. FooType*)
113  // in Variant objects. References and pointers can fail badly when things are passed around
114  // devices. If you get a compiler error here, consider removing the reference or using something
115  // like std::decay to remove qualifiers on the type. (We may decide to do that automatically in
116  // the future.)
117  VTKM_STATIC_ASSERT_MSG(!std::is_reference<T>::value,
118  "References are not allowed in VTK-m Variant.");
119  VTKM_STATIC_ASSERT_MSG(!std::is_pointer<T>::value, "Pointers are not allowed in VTK-m Variant.");
120 };
121 
122 template <typename VariantType>
123 struct VariantTriviallyCopyable;
124 
125 template <typename... Ts>
126 struct VariantTriviallyCopyable<vtkm::VTK_M_NAMESPACE::Variant<Ts...>> : AllTriviallyCopyable<Ts...>
127 {
128 };
129 
130 template <typename VariantType>
131 struct VariantTriviallyConstructible;
132 
133 template <typename... Ts>
134 struct VariantTriviallyConstructible<vtkm::VTK_M_NAMESPACE::Variant<Ts...>>
135  : AllTriviallyConstructible<Ts...>
136 {
137 };
138 
139 // --------------------------------------------------------------------------------
140 // Variant superclass that defines its storage
141 template <typename... Ts>
142 struct VariantStorageImpl
143 {
144  VariantUnion<Ts...> Storage;
146 
147  VariantStorageImpl() = default;
148 
149  VTK_M_DEVICE VariantStorageImpl(vtkm::internal::NullType dummy)
150  : Storage({ dummy })
151  {
152  }
153 
154  template <vtkm::IdComponent Index>
155  using TypeAt = typename vtkm::ListAt<vtkm::List<Ts...>, Index>;
156 
157  VTK_M_DEVICE vtkm::IdComponent GetIndex() const noexcept { return this->Index; }
158  VTK_M_DEVICE bool IsValid() const noexcept
159  {
160  return (this->Index >= 0) && (this->Index < static_cast<vtkm::IdComponent>(sizeof...(Ts)));
161  }
162 
163  VTK_M_DEVICE void Reset() noexcept
164  {
165  if (this->IsValid())
166  {
167  this->CastAndCall(detail::VariantDestroyFunctor{});
168  this->Index = -1;
169  }
170  }
171 
172  template <typename Functor, typename... Args>
173  VTK_M_DEVICE auto CastAndCall(Functor&& f, Args&&... args) const
174  noexcept(noexcept(f(std::declval<const TypeAt<0>&>(), args...)))
175  -> decltype(f(std::declval<const TypeAt<0>&>(), args...))
176  {
177  VTKM_ASSERT(this->IsValid());
178  return detail::VariantCastAndCallImpl<sizeof...(Ts)>(
179  this->GetIndex(), std::forward<Functor>(f), this->Storage, std::forward<Args>(args)...);
180  }
181 
182  template <typename Functor, typename... Args>
183  VTK_M_DEVICE auto CastAndCall(Functor&& f, Args&&... args) noexcept(
184  noexcept(f(std::declval<const TypeAt<0>&>(), args...)))
185  -> decltype(f(std::declval<TypeAt<0>&>(), args...))
186  {
187  VTKM_ASSERT(this->IsValid());
188  return detail::VariantCastAndCallImpl<sizeof...(Ts)>(
189  this->GetIndex(), std::forward<Functor>(f), this->Storage, std::forward<Args>(args)...);
190  }
191 };
192 
193 // --------------------------------------------------------------------------------
194 // Variant superclass that helps preserve trivially copyable and trivially constructable
195 // properties where possible.
196 template <typename VariantType,
197  typename TriviallyConstructible =
198  typename VariantTriviallyConstructible<VariantType>::type,
199  typename TriviallyCopyable = typename VariantTriviallyCopyable<VariantType>::type>
200 struct VariantConstructorImpl;
201 
202 // Can trivially construct, deconstruct, and copy all data. (Probably all trivial classes.)
203 template <typename... Ts>
204 struct VariantConstructorImpl<vtkm::VTK_M_NAMESPACE::Variant<Ts...>, std::true_type, std::true_type>
205  : VariantStorageImpl<Ts...>
206 {
207  VariantConstructorImpl() = default;
208  ~VariantConstructorImpl() = default;
209 
210  VariantConstructorImpl(const VariantConstructorImpl&) = default;
211  VariantConstructorImpl(VariantConstructorImpl&&) = default;
212  VariantConstructorImpl& operator=(const VariantConstructorImpl&) = default;
213  VariantConstructorImpl& operator=(VariantConstructorImpl&&) = default;
214 };
215 
216 // Can trivially copy, but cannot trivially construct. Common if a class is simple but
217 // initializes itself.
218 template <typename... Ts>
219 struct VariantConstructorImpl<vtkm::VTK_M_NAMESPACE::Variant<Ts...>,
220  std::false_type,
221  std::true_type> : VariantStorageImpl<Ts...>
222 {
223  VTK_M_DEVICE VariantConstructorImpl()
224  : VariantStorageImpl<Ts...>(vtkm::internal::NullType{})
225  {
226  this->Index = -1;
227  }
228 
229  // Any trivially copyable class is trivially destructable.
230  ~VariantConstructorImpl() = default;
231 
232  VariantConstructorImpl(const VariantConstructorImpl&) = default;
233  VariantConstructorImpl(VariantConstructorImpl&&) = default;
234  VariantConstructorImpl& operator=(const VariantConstructorImpl&) = default;
235  VariantConstructorImpl& operator=(VariantConstructorImpl&&) = default;
236 };
237 
238 // Cannot trivially copy. We assume we cannot trivially construct/destruct.
239 template <typename construct_type, typename... Ts>
240 struct VariantConstructorImpl<vtkm::VTK_M_NAMESPACE::Variant<Ts...>,
241  construct_type,
242  std::false_type> : VariantStorageImpl<Ts...>
243 {
244  VTK_M_DEVICE VariantConstructorImpl()
245  : VariantStorageImpl<Ts...>(vtkm::internal::NullType{})
246  {
247  this->Index = -1;
248  }
249  VTK_M_DEVICE ~VariantConstructorImpl() { this->Reset(); }
250 
251  VTK_M_DEVICE VariantConstructorImpl(const VariantConstructorImpl& src) noexcept
252  : VariantStorageImpl<Ts...>(vtkm::internal::NullType{})
253  {
254  if (src.IsValid())
255  {
256  src.CastAndCall(VariantCopyConstructFunctor{}, this->Storage);
257  }
258  this->Index = src.Index;
259  }
260 
261  VTK_M_DEVICE VariantConstructorImpl& operator=(const VariantConstructorImpl& src) noexcept
262  {
263  if (src.IsValid())
264  {
265  if (this->GetIndex() == src.GetIndex())
266  {
267  src.CastAndCall(detail::VariantCopyFunctor{}, this->Storage);
268  }
269  else
270  {
271  this->Reset();
272  src.CastAndCall(detail::VariantCopyConstructFunctor{}, this->Storage);
273  this->Index = src.Index;
274  }
275  }
276  else
277  {
278  this->Reset();
279  }
280  return *this;
281  }
282 };
283 
284 } // namespace detail
285 
286 template <typename... Ts>
287 class Variant : detail::VariantConstructorImpl<Variant<Ts...>>
288 {
289  using Superclass = detail::VariantConstructorImpl<Variant<Ts...>>;
290 
291  // Type not used, but has the compiler check all the types for validity.
293 
294 public:
297  template <typename T>
299 
302  template <typename T>
304  {
305  return IndexOf<T>::value;
306  }
307 
310  template <vtkm::IdComponent Index>
311  using TypeAt = typename vtkm::ListAt<vtkm::List<Ts...>, Index>;
312 
318  template <typename T>
319  using CanStore = std::integral_constant<bool, (IndexOf<T>::value >= 0)>;
320 
323  template <typename T>
324  VTK_M_DEVICE static constexpr bool GetCanStore()
325  {
326  return CanStore<T>::value;
327  }
328 
331  static constexpr vtkm::IdComponent NumberOfTypes = vtkm::IdComponent{ sizeof...(Ts) };
332 
336  VTK_M_DEVICE vtkm::IdComponent GetIndex() const noexcept { return this->Index; }
337 
345  VTK_M_DEVICE bool IsValid() const noexcept
346  {
347  return (this->Index >= 0) && (this->Index < NumberOfTypes);
348  }
349 
352  template <typename T>
353  VTK_M_DEVICE bool IsType() const
354  {
355  return (this->GetIndex() == this->GetIndexOf<T>());
356  }
357 
358  Variant() = default;
359  ~Variant() = default;
360  Variant(const Variant&) = default;
361  Variant(Variant&&) = default;
362  Variant& operator=(const Variant&) = default;
363  Variant& operator=(Variant&&) = default;
364 
365  template <typename T>
366  VTK_M_DEVICE Variant(const T& src) noexcept
367  {
368  constexpr vtkm::IdComponent index = GetIndexOf<T>();
369  // Might be a way to use an enable_if to enforce a proper type.
370  VTKM_STATIC_ASSERT_MSG(index >= 0, "Attempting to put invalid type into a Variant");
371 
372  this->Index = index;
373  new (&this->Get<index>()) T(src);
374  }
375 
376  template <typename T>
378  {
379  if (this->IsType<T>())
380  {
381  this->Get<T>() = src;
382  }
383  else
384  {
385  this->Emplace<T>(src);
386  }
387  return *this;
388  }
389 
390  template <typename T, typename... Args>
391  VTK_M_DEVICE T& Emplace(Args&&... args)
392  {
393  constexpr vtkm::IdComponent I = GetIndexOf<T>();
394  VTKM_STATIC_ASSERT_MSG(I >= 0, "Variant::Emplace called with invalid type.");
395  return this->EmplaceImpl<T, I>(std::forward<Args>(args)...);
396  }
397 
398  template <typename T, typename U, typename... Args>
399  VTK_M_DEVICE T& Emplace(std::initializer_list<U> il, Args&&... args)
400  {
401  constexpr vtkm::IdComponent I = GetIndexOf<T>();
402  VTKM_STATIC_ASSERT_MSG(I >= 0, "Variant::Emplace called with invalid type.");
403  return this->EmplaceImpl<T, I>(il, std::forward<Args>(args)...);
404  }
405 
406  template <vtkm::IdComponent I, typename... Args>
407  VTK_M_DEVICE TypeAt<I>& Emplace(Args&&... args)
408  {
409  VTKM_STATIC_ASSERT_MSG((I >= 0) && (I < NumberOfTypes),
410  "Variant::Emplace called with invalid index");
411  return this->EmplaceImpl<TypeAt<I>, I>(std::forward<Args>(args)...);
412  }
413 
414  template <vtkm::IdComponent I, typename U, typename... Args>
415  VTK_M_DEVICE TypeAt<I>& Emplace(std::initializer_list<U> il, Args&&... args)
416  {
417  VTKM_STATIC_ASSERT_MSG((I >= 0) && (I < NumberOfTypes),
418  "Variant::Emplace called with invalid index");
419  return this->EmplaceImpl<TypeAt<I>, I>(il, std::forward<Args>(args)...);
420  }
421 
422 private:
423  template <typename T, vtkm::IdComponent I, typename... Args>
424  VTK_M_DEVICE T& EmplaceImpl(Args&&... args)
425  {
426  this->Reset();
427  this->Index = I;
428  return *(new (&this->Get<I>()) T{ args... });
429  }
430 
431  template <typename T, vtkm::IdComponent I, typename U, typename... Args>
432  VTK_M_DEVICE T& EmplaceImpl(std::initializer_list<U> il, Args&&... args)
433  {
434  this->Reset();
435  this->Index = I;
436  return *(new (&this->Get<I>()) T(il, args...));
437  }
438 
439 public:
444  template <vtkm::IdComponent I>
446  {
447  VTKM_ASSERT(I == this->GetIndex());
448  return detail::VariantUnionGet<I>(this->Storage);
449  }
450 
451  template <vtkm::IdComponent I>
452  VTK_M_DEVICE const TypeAt<I>& Get() const noexcept
453  {
454  VTKM_ASSERT(I == this->GetIndex());
455  return detail::VariantUnionGet<I>(this->Storage);
456  }
458 
463  template <typename T>
464  VTK_M_DEVICE T& Get() noexcept
465  {
466  return this->GetImpl<T>(CanStore<T>{});
467  }
468 
469  template <typename T>
470  VTK_M_DEVICE const T& Get() const noexcept
471  {
472  return this->GetImpl<T>(CanStore<T>{});
473  }
475 
476 private:
477  template <typename T>
478  VTK_M_DEVICE T& GetImpl(std::true_type)
479  {
480  VTKM_ASSERT(this->IsType<T>());
481  return detail::VariantUnionGet<IndexOf<T>::value>(this->Storage);
482  }
483 
484  template <typename T>
485  VTK_M_DEVICE const T& GetImpl(std::true_type) const
486  {
487  VTKM_ASSERT(this->IsType<T>());
488  return detail::VariantUnionGet<IndexOf<T>::value>(this->Storage);
489  }
490 
491  // This function overload only gets created if you attempt to pull a type from a
492  // variant that does not exist. Perhaps this should be a compile error, but there
493  // are cases where you might create templated code that has a path that could call
494  // this but never does. To make this case easier, do a runtime error (when asserts
495  // are active) instead.
496  template <typename T>
497  VTK_M_DEVICE T& GetImpl(std::false_type) const
498  {
499  VTKM_ASSERT(false &&
500  "Attempted to get a type from a variant that the variant does not contain.");
501  // This will cause some _really_ nasty issues if you actually try to use the returned type.
502  return *reinterpret_cast<T*>(0);
503  }
504 
505 public:
513  template <typename Functor, typename... Args>
514  VTK_M_DEVICE auto CastAndCall(Functor&& f, Args&&... args) const
515  noexcept(noexcept(f(std::declval<const TypeAt<0>&>(), args...)))
516  {
517  VTKM_ASSERT(this->IsValid());
518  return detail::VariantCastAndCallImpl<sizeof...(Ts)>(
519  this->GetIndex(), std::forward<Functor>(f), this->Storage, std::forward<Args>(args)...);
520  }
521 
522  template <typename Functor, typename... Args>
523  VTK_M_DEVICE auto CastAndCall(Functor&& f,
524  Args&&... args) noexcept(noexcept(f(std::declval<TypeAt<0>&>(),
525  args...)))
526  {
527  VTKM_ASSERT(this->IsValid());
528  return detail::VariantCastAndCallImpl<sizeof...(Ts)>(
529  this->GetIndex(), std::forward<Functor>(f), this->Storage, std::forward<Args>(args)...);
530  }
531 
535  VTK_M_DEVICE void Reset() noexcept
536  {
537  if (this->IsValid())
538  {
539  this->CastAndCall(detail::VariantDestroyFunctor{});
540  this->Index = -1;
541  }
542  }
543 };
544 
547 template <typename List>
549 }
550 } // namespace vtkm::VTK_M_NAMESPACE
551 
552 #undef VTK_M_DEVICE
553 #undef VTK_M_NAMESPACE
vtkm::tmp::Variant::Get
TypeAt< I > & Get() noexcept
Definition: VariantImpl.h:445
vtkm::tmp::Variant::operator=
Variant & operator=(const T &src)
Definition: VariantImpl.h:377
vtkm
Groups connected points that have the same field value.
Definition: Atomic.h:19
VTK_M_DEVICE
#define VTK_M_DEVICE
Definition: VariantImpl.h:14
VTKM_ASSERT
#define VTKM_ASSERT(condition)
Definition: Assert.h:43
vtkm::ListIndexOf
typename detail::ListIndexOfImpl< List, T >::type ListIndexOf
Finds the index of a given type.
Definition: List.h:553
vtkm::IdComponent
vtkm::Int32 IdComponent
Base type to use to index small lists.
Definition: Types.h:194
VTK_M_NAMESPACE
#define VTK_M_NAMESPACE
Definition: VariantImpl.h:15
vtkm::ListApply
typename detail::ListApplyImpl< List, Target >::type ListApply
Applies the list of types to a template.
Definition: List.h:138
vtkm::tmp::Variant::Emplace
TypeAt< I > & Emplace(std::initializer_list< U > il, Args &&... args)
Definition: VariantImpl.h:415
vtkm::tmp::Variant::Get
T & Get() noexcept
Definition: VariantImpl.h:464
vtkm::tmp::Variant::CanStore
std::integral_constant< bool,(IndexOf< T >::value >=0)> CanStore
Type that indicates whether another type can be stored in this Variant.
Definition: VariantImpl.h:319
vtkm::cont::CastAndCall
void CastAndCall(const DynamicObject &dynamicObject, Functor &&f, Args &&... args)
A Generic interface to CastAndCall.
Definition: CastAndCall.h:47
vtkm::tmp::Variant::EmplaceImpl
T & EmplaceImpl(Args &&... args)
Definition: VariantImpl.h:424
vtkm::ListAt
typename detail::ListAtImpl< List, Index >::type ListAt
Finds the type at the given index.
Definition: List.h:350
vtkm::tmp::Variant::Emplace
T & Emplace(Args &&... args)
Definition: VariantImpl.h:391
vtkm::tmp::Variant::Emplace
TypeAt< I > & Emplace(Args &&... args)
Definition: VariantImpl.h:407
Assume.h
vtkm::tmp::Variant::GetIndexOf
static constexpr vtkm::IdComponent GetIndexOf()
Returns the index for the given type (or -1 if that type is not in the list).
Definition: VariantImpl.h:303
vtkm::tmp::Variant::IndexOf
vtkm::ListIndexOf< vtkm::List< Ts... >, T > IndexOf
Type that converts to a std::integral_constant containing the index of the given type (or -1 if that ...
Definition: VariantImpl.h:298
vtkm::tmp::Variant::GetImpl
const T & GetImpl(std::true_type) const
Definition: VariantImpl.h:485
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::tmp::Variant::GetCanStore
static constexpr bool GetCanStore()
Returns whether the given type can be respresented in this Variant.
Definition: VariantImpl.h:324
vtkm::tmp::Variant::Get
const T & Get() const noexcept
Definition: VariantImpl.h:470
vtkm::tmp::Variant::Variant
Variant(const T &src) noexcept
Definition: VariantImpl.h:366
vtkm::tmp::Variant::CastAndCall
auto CastAndCall(Functor &&f, Args &&... args) noexcept(noexcept(f(std::declval< TypeAt< 0 > & >(), args...)))
Definition: VariantImpl.h:523
vtkm::tmp::Variant::GetIndex
vtkm::IdComponent GetIndex() const noexcept
Returns the index of the type of object this variant is storing.
Definition: VariantImpl.h:336
vtkm::tmp::Variant::GetImpl
T & GetImpl(std::true_type)
Definition: VariantImpl.h:478
vtkm::tmp::Variant::Reset
void Reset() noexcept
Destroys any object the Variant is holding and sets the Variant to an invalid state.
Definition: VariantImpl.h:535
vtkm::tmp::Variant::IsValid
bool IsValid() const noexcept
Returns true if this Variant is storing an object from one of the types in the template list,...
Definition: VariantImpl.h:345
vtkm::tmp::Variant::GetImpl
T & GetImpl(std::false_type) const
Definition: VariantImpl.h:497
vtkm::tmp::Variant::CastAndCall
auto CastAndCall(Functor &&f, Args &&... args) const noexcept(noexcept(f(std::declval< const TypeAt< 0 > & >(), args...)))
Definition: VariantImpl.h:514
vtkm::tmp::ListAsVariant
vtkm::ListApply< List, vtkm::tmp ::Variant > ListAsVariant
Convert a List to a Variant.
Definition: VariantImpl.h:548
vtkm::List
A template used to hold a list of types.
Definition: List.h:39
vtkm::tmp::Variant::TypeAt
typename vtkm::ListAt< vtkm::List< Ts... >, Index > TypeAt
Type that converts to the type at the given index.
Definition: VariantImpl.h:311
vtkm::tmp::Variant::Superclass
detail::VariantConstructorImpl< Variant< Ts... > > Superclass
Definition: VariantImpl.h:289
vtkm::tmp::Variant::EmplaceImpl
T & EmplaceImpl(std::initializer_list< U > il, Args &&... args)
Definition: VariantImpl.h:432
vtkm::tmp::Variant::IsType
bool IsType() const
Returns true if this Variant stores the given type.
Definition: VariantImpl.h:353
vtkm::tmp::Variant
Definition: VariantImpl.h:31
vtkm::tmp::Variant::Emplace
T & Emplace(std::initializer_list< U > il, Args &&... args)
Definition: VariantImpl.h:399
vtkm::tmp::Variant::Get
const TypeAt< I > & Get() const noexcept
Definition: VariantImpl.h:452
List.h