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