Viskores  1.0
VariantImplDetail.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 // **** DO NOT EDIT THIS FILE!!! ****
19 // This file is automatically generated by VariantDetail.h.in
20 
21 #if !defined(VISKORES_DEVICE) || !defined(VISKORES_NAMESPACE)
22 #error VarianImplDetail.h must be included from VariantImpl.h
23 // Some defines to make my IDE happy.
24 #define VISKORES_DEVICE
25 #define VISKORES_NAMESPACE tmp
26 #endif
27 
28 #include <viskores/List.h>
29 #include <viskores/Types.h>
30 
32 
33 #include <viskoresstd/is_trivial.h>
34 
35 #include <algorithm>
36 #include <cstddef>
37 #include <type_traits>
38 
39 
40 
41 namespace viskores
42 {
43 namespace VISKORES_NAMESPACE
44 {
45 namespace detail
46 {
47 
48 // --------------------------------------------------------------------------------
49 // Helper classes to determine if all Variant types are trivial.
50 template <typename... Ts>
51 using AllTriviallyCopyable = viskores::ListAll<viskores::List<Ts...>, viskoresstd::is_trivially_copyable>;
52 
53 // Single argument version of is_trivially_constructible
54 template <typename T>
55 using Constructible = viskoresstd::is_trivially_constructible<T>;
56 
57 template <typename... Ts>
58 using AllTriviallyConstructible = viskores::ListAll<viskores::List<Ts...>, Constructible>;
59 
60 template <typename... Ts>
61 using AllTriviallyDestructible =
62  viskores::ListAll<viskores::List<Ts...>, viskoresstd::is_trivially_destructible>;
63 
64 // --------------------------------------------------------------------------------
65 // Helper functions to determine the maximum type size.
66 #if defined(VISKORES_GCC) && (__GNUC__ == 5)
67 // GCC5 gives an error with `sizeof(Ts)...` for an unexpanded parameter pack.
68 template <typename T0>
69 constexpr std::size_t MaxSizeOf()
70 {
71  return sizeof(T0);
72 }
73 template <typename T0, typename T1, typename... Ts>
74 constexpr std::size_t MaxSizeOf()
75 {
76  return std::max(sizeof(T0), MaxSizeOf<T1, Ts...>());
77 }
78 #else
79 template <typename... Ts>
80 constexpr std::size_t MaxSizeOf()
81 {
82  return std::max({ sizeof(Ts)... });
83 }
84 #endif
85 
86 // --------------------------------------------------------------------------------
87 // Helper functions to determine the maximum alignment size.
88 template <typename... Ts>
89 constexpr std::size_t MaxAlignmentOf()
90 {
91  return std::max({ std::alignment_of<Ts>::value... });
92 }
93 
94 // --------------------------------------------------------------------------------
95 // Placeholder for a fully used structure of the given type.
96 // This placeholder is used for compilers that do not correctly copy `struct`s
97 // in `union`s where some of the `struct`s have padding. This is added to the
98 // front of the `union` for the compiler to pick up and use.
99 //
100 // It is normally sufficient to have a full `struct`, but we have also encountered
101 // compilers that only use it if the alignment is at least as large. But we
102 // don't want the alignment too large because it can add unwanted padding
103 // elsewhere. Also, adding `alignas` did not work to resolve this problem for
104 // the compiler.
105 
106 template <std::size_t Alignment>
107 struct TypeForAlignmentImpl;
108 template <>
109 struct TypeForAlignmentImpl<8>
110 {
111  using type = viskores::Int64;
112 };
113 template <>
114 struct TypeForAlignmentImpl<4>
115 {
116  using type = viskores::Int32;
117 };
118 template <>
119 struct TypeForAlignmentImpl<2>
120 {
121  using type = viskores::Int16;
122 };
123 template <>
124 struct TypeForAlignmentImpl<1>
125 {
126  using type = viskores::Int8;
127 };
128 template <std::size_t Alignment>
129 using TypeForAlignment = typename TypeForAlignmentImpl<Alignment>::type;
130 
131 template <std::size_t Size, typename Word, bool = (Size >= 4)>
132 struct SizedPlaceholderImpl;
133 
134 template <std::size_t Size, typename Word>
135 struct SizedPlaceholderImpl<Size, Word, true>
136 {
137  Word A;
138  Word B;
139  Word C;
140  Word D;
141  SizedPlaceholderImpl<Size - 4, Word> E;
142 };
143 template <typename Word>
144 struct SizedPlaceholderImpl<4, Word, true>
145 {
146  Word A;
147  Word B;
148  Word C;
149  Word D;
150 };
151 
152 template <std::size_t Size, typename Word>
153 struct SizedPlaceholderImpl<Size, Word, false>
154 {
155  Word A;
156  SizedPlaceholderImpl<Size - 1, Word> B;
157 };
158 template <typename Word>
159 struct SizedPlaceholderImpl<1, Word, false>
160 {
161  Word A;
162 };
163 
164 template <typename... Ts>
165 struct SizedPlaceholder
166  : SizedPlaceholderImpl<(MaxSizeOf<Ts...>() / MaxAlignmentOf<Ts...>()),
167  TypeForAlignment<MaxAlignmentOf<Ts...>()>>
168 {
169 };
170 
171 // clang-format off
172 
173 // --------------------------------------------------------------------------------
174 // Union type used inside of Variant
175 //
176 // You may be asking yourself, why not just use an std::aligned_union rather than a real union
177 // type? That was our first implementation, but the problem is that the std::aligned_union
178 // reference needs to be recast to the actual type. Typically you would do that with
179 // reinterpret_cast. However, doing that leads to undefined behavior. The C++ compiler assumes that
180 // 2 pointers of different types point to different memory (even if it is clear that they are set
181 // to the same address). That means optimizers can remove code because it "knows" that data in one
182 // type cannot affect data in another type. (See Shafik Yaghmour's excellent writeup at
183 // https://gist.github.com/shafik/848ae25ee209f698763cffee272a58f8 for more details.) To safely
184 // change the type of an std::aligned_union, you really have to do an std::memcpy. This is
185 // problematic for types that cannot be trivially copied. Another problem is that we found that
186 // device compilers do not optimize the memcpy as well as most CPU compilers. Likely, memcpy is
187 // used much less frequently on GPU devices.
188 //
189 // Part of the trickiness of the union implementation is trying to preserve when the type is
190 // trivially constructible and copyable. The trick is that if members of the union are not trivial,
191 // then the default constructors are deleted. To get around that, a non-default constructor is
192 // added, which we can use to construct the union for non-trivial types. Working with types with
193 // non-trivial destructors are particularly tricky. Again, if any member of the union has a
194 // non-trivial destructor, the destructor is deleted. Unlike a constructor, you cannot just say to
195 // use a different destructor. Thus, we have to define our own destructor for the union.
196 // Technically, the destructor here does not do anything, but the actual destruction should be
197 // handled by the Variant class that contains this VariantUnion. We actually need two separate
198 // implementations of our union, one that defines a destructor and one that use the default
199 // destructor. If you define your own destructor, you can lose the trivial constructor and trivial
200 // copy properties.
201 //
202 
203 // TD = trivially deconstructible
204 template <typename T0, typename... Ts>
205 union VariantUnionTD;
206 
207 // NTD = non-trivially deconstructible
208 template <typename T0, typename... Ts>
209 union VariantUnionNTD;
210 
211 template <typename T0>
212 union VariantUnionTD<T0>
213 {
214  // Work around issue where some compilers miss initializing some struct members if another entry
215  // in the varient has a struct with padding. Place an item that requires everthing to be copied.
216  SizedPlaceholder<T0> Placeholder;
217 
218  T0 V0;
219  VISKORES_DEVICE VariantUnionTD(viskores::internal::NullType) { }
220  VariantUnionTD() = default;
221 };
222 template <typename T0>
223 union VariantUnionNTD<T0>
224 {
225  // Work around issue where some compilers miss initializing some struct members if another entry
226  // in the varient has a struct with padding. Place an item that requires everthing to be copied.
227  SizedPlaceholder<T0> Placeholder;
228 
229  T0 V0;
230  VISKORES_DEVICE VariantUnionNTD(viskores::internal::NullType) { }
231  VariantUnionNTD() = default;
232  VISKORES_DEVICE ~VariantUnionNTD() { }
233 };
234 
235 template <typename T0, typename T1>
236 union VariantUnionTD<T0, T1>
237 {
238  // Work around issue where some compilers miss initializing some struct members if another entry
239  // in the varient has a struct with padding. Place an item that requires everthing to be copied.
240  SizedPlaceholder<T0, T1> Placeholder;
241 
242  T0 V0;
243  T1 V1;
244  VISKORES_DEVICE VariantUnionTD(viskores::internal::NullType) { }
245  VariantUnionTD() = default;
246 };
247 template <typename T0, typename T1>
248 union VariantUnionNTD<T0, T1>
249 {
250  // Work around issue where some compilers miss initializing some struct members if another entry
251  // in the varient has a struct with padding. Place an item that requires everthing to be copied.
252  SizedPlaceholder<T0, T1> Placeholder;
253 
254  T0 V0;
255  T1 V1;
256  VISKORES_DEVICE VariantUnionNTD(viskores::internal::NullType) { }
257  VariantUnionNTD() = default;
258  VISKORES_DEVICE ~VariantUnionNTD() { }
259 };
260 
261 template <typename T0, typename T1, typename T2>
262 union VariantUnionTD<T0, T1, T2>
263 {
264  // Work around issue where some compilers miss initializing some struct members if another entry
265  // in the varient has a struct with padding. Place an item that requires everthing to be copied.
266  SizedPlaceholder<T0, T1, T2> Placeholder;
267 
268  T0 V0;
269  T1 V1;
270  T2 V2;
271  VISKORES_DEVICE VariantUnionTD(viskores::internal::NullType) { }
272  VariantUnionTD() = default;
273 };
274 template <typename T0, typename T1, typename T2>
275 union VariantUnionNTD<T0, T1, T2>
276 {
277  // Work around issue where some compilers miss initializing some struct members if another entry
278  // in the varient has a struct with padding. Place an item that requires everthing to be copied.
279  SizedPlaceholder<T0, T1, T2> Placeholder;
280 
281  T0 V0;
282  T1 V1;
283  T2 V2;
284  VISKORES_DEVICE VariantUnionNTD(viskores::internal::NullType) { }
285  VariantUnionNTD() = default;
286  VISKORES_DEVICE ~VariantUnionNTD() { }
287 };
288 
289 template <typename T0, typename T1, typename T2, typename T3>
290 union VariantUnionTD<T0, T1, T2, T3>
291 {
292  // Work around issue where some compilers miss initializing some struct members if another entry
293  // in the varient has a struct with padding. Place an item that requires everthing to be copied.
294  SizedPlaceholder<T0, T1, T2, T3> Placeholder;
295 
296  T0 V0;
297  T1 V1;
298  T2 V2;
299  T3 V3;
300  VISKORES_DEVICE VariantUnionTD(viskores::internal::NullType) { }
301  VariantUnionTD() = default;
302 };
303 template <typename T0, typename T1, typename T2, typename T3>
304 union VariantUnionNTD<T0, T1, T2, T3>
305 {
306  // Work around issue where some compilers miss initializing some struct members if another entry
307  // in the varient has a struct with padding. Place an item that requires everthing to be copied.
308  SizedPlaceholder<T0, T1, T2, T3> Placeholder;
309 
310  T0 V0;
311  T1 V1;
312  T2 V2;
313  T3 V3;
314  VISKORES_DEVICE VariantUnionNTD(viskores::internal::NullType) { }
315  VariantUnionNTD() = default;
316  VISKORES_DEVICE ~VariantUnionNTD() { }
317 };
318 
319 template <typename T0, typename T1, typename T2, typename T3, typename T4>
320 union VariantUnionTD<T0, T1, T2, T3, T4>
321 {
322  // Work around issue where some compilers miss initializing some struct members if another entry
323  // in the varient has a struct with padding. Place an item that requires everthing to be copied.
324  SizedPlaceholder<T0, T1, T2, T3, T4> Placeholder;
325 
326  T0 V0;
327  T1 V1;
328  T2 V2;
329  T3 V3;
330  T4 V4;
331  VISKORES_DEVICE VariantUnionTD(viskores::internal::NullType) { }
332  VariantUnionTD() = default;
333 };
334 template <typename T0, typename T1, typename T2, typename T3, typename T4>
335 union VariantUnionNTD<T0, T1, T2, T3, T4>
336 {
337  // Work around issue where some compilers miss initializing some struct members if another entry
338  // in the varient has a struct with padding. Place an item that requires everthing to be copied.
339  SizedPlaceholder<T0, T1, T2, T3, T4> Placeholder;
340 
341  T0 V0;
342  T1 V1;
343  T2 V2;
344  T3 V3;
345  T4 V4;
346  VISKORES_DEVICE VariantUnionNTD(viskores::internal::NullType) { }
347  VariantUnionNTD() = default;
348  VISKORES_DEVICE ~VariantUnionNTD() { }
349 };
350 
351 template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5>
352 union VariantUnionTD<T0, T1, T2, T3, T4, T5>
353 {
354  // Work around issue where some compilers miss initializing some struct members if another entry
355  // in the varient has a struct with padding. Place an item that requires everthing to be copied.
356  SizedPlaceholder<T0, T1, T2, T3, T4, T5> Placeholder;
357 
358  T0 V0;
359  T1 V1;
360  T2 V2;
361  T3 V3;
362  T4 V4;
363  T5 V5;
364  VISKORES_DEVICE VariantUnionTD(viskores::internal::NullType) { }
365  VariantUnionTD() = default;
366 };
367 template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5>
368 union VariantUnionNTD<T0, T1, T2, T3, T4, T5>
369 {
370  // Work around issue where some compilers miss initializing some struct members if another entry
371  // in the varient has a struct with padding. Place an item that requires everthing to be copied.
372  SizedPlaceholder<T0, T1, T2, T3, T4, T5> Placeholder;
373 
374  T0 V0;
375  T1 V1;
376  T2 V2;
377  T3 V3;
378  T4 V4;
379  T5 V5;
380  VISKORES_DEVICE VariantUnionNTD(viskores::internal::NullType) { }
381  VariantUnionNTD() = default;
382  VISKORES_DEVICE ~VariantUnionNTD() { }
383 };
384 
385 template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
386 union VariantUnionTD<T0, T1, T2, T3, T4, T5, T6>
387 {
388  // Work around issue where some compilers miss initializing some struct members if another entry
389  // in the varient has a struct with padding. Place an item that requires everthing to be copied.
390  SizedPlaceholder<T0, T1, T2, T3, T4, T5, T6> Placeholder;
391 
392  T0 V0;
393  T1 V1;
394  T2 V2;
395  T3 V3;
396  T4 V4;
397  T5 V5;
398  T6 V6;
399  VISKORES_DEVICE VariantUnionTD(viskores::internal::NullType) { }
400  VariantUnionTD() = default;
401 };
402 template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
403 union VariantUnionNTD<T0, T1, T2, T3, T4, T5, T6>
404 {
405  // Work around issue where some compilers miss initializing some struct members if another entry
406  // in the varient has a struct with padding. Place an item that requires everthing to be copied.
407  SizedPlaceholder<T0, T1, T2, T3, T4, T5, T6> Placeholder;
408 
409  T0 V0;
410  T1 V1;
411  T2 V2;
412  T3 V3;
413  T4 V4;
414  T5 V5;
415  T6 V6;
416  VISKORES_DEVICE VariantUnionNTD(viskores::internal::NullType) { }
417  VariantUnionNTD() = default;
418  VISKORES_DEVICE ~VariantUnionNTD() { }
419 };
420 
421 template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
422 union VariantUnionTD<T0, T1, T2, T3, T4, T5, T6, T7>
423 {
424  // Work around issue where some compilers miss initializing some struct members if another entry
425  // in the varient has a struct with padding. Place an item that requires everthing to be copied.
426  SizedPlaceholder<T0, T1, T2, T3, T4, T5, T6, T7> Placeholder;
427 
428  T0 V0;
429  T1 V1;
430  T2 V2;
431  T3 V3;
432  T4 V4;
433  T5 V5;
434  T6 V6;
435  T7 V7;
436  VISKORES_DEVICE VariantUnionTD(viskores::internal::NullType) { }
437  VariantUnionTD() = default;
438 };
439 template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
440 union VariantUnionNTD<T0, T1, T2, T3, T4, T5, T6, T7>
441 {
442  // Work around issue where some compilers miss initializing some struct members if another entry
443  // in the varient has a struct with padding. Place an item that requires everthing to be copied.
444  SizedPlaceholder<T0, T1, T2, T3, T4, T5, T6, T7> Placeholder;
445 
446  T0 V0;
447  T1 V1;
448  T2 V2;
449  T3 V3;
450  T4 V4;
451  T5 V5;
452  T6 V6;
453  T7 V7;
454  VISKORES_DEVICE VariantUnionNTD(viskores::internal::NullType) { }
455  VariantUnionNTD() = default;
456  VISKORES_DEVICE ~VariantUnionNTD() { }
457 };
458 
459 
460 template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename... Ts>
461 union VariantUnionTD<T0, T1, T2, T3, T4, T5, T6, T7, T8, Ts...>
462 {
463  // Work around issue where CUDA sometimes seems to miss initializing some struct members
464  // if another entry in the varient has a struct with padding. Place an item that requires
465  // everthing to be copied.
466  SizedPlaceholder<T0, T1, T2, T3, T4, T5, T6, T7, T8, Ts...> Placeholder;
467 
468  T0 V0;
469  T1 V1;
470  T2 V2;
471  T3 V3;
472  T4 V4;
473  T5 V5;
474  T6 V6;
475  T7 V7;
476  VariantUnionTD<T8, Ts...> Remaining;
477 
478  VISKORES_DEVICE VariantUnionTD(viskores::internal::NullType) { }
479  VariantUnionTD() = default;
480 };
481 
482 template <typename T0, typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8, typename... Ts>
483 union VariantUnionNTD<T0, T1, T2, T3, T4, T5, T6, T7, T8, Ts...>
484 {
485  // Work around issue where CUDA sometimes seems to miss initializing some struct members
486  // if another entry in the varient has a struct with padding. Place an item that requires
487  // everthing to be copied.
488  SizedPlaceholder<T0, T1, T2, T3, T4, T5, T6, T7, T8, Ts...> Placeholder;
489 
490  T0 V0;
491  T1 V1;
492  T2 V2;
493  T3 V3;
494  T4 V4;
495  T5 V5;
496  T6 V6;
497  T7 V7;
498  VariantUnionNTD<T8, Ts...> Remaining;
499 
500  VISKORES_DEVICE VariantUnionNTD(viskores::internal::NullType) { }
501  VariantUnionNTD() = default;
502  VISKORES_DEVICE ~VariantUnionNTD() { }
503 };
504 
505 //clang-format on
506 
507 template <bool TrivialConstructor, typename... Ts>
508 struct VariantUnionFinder;
509 
510 template <typename... Ts>
511 struct VariantUnionFinder<true, Ts...>
512 {
513  using type = VariantUnionTD<Ts...>;
514 };
515 template <typename... Ts>
516 struct VariantUnionFinder<false, Ts...>
517 {
518  using type = VariantUnionNTD<Ts...>;
519 };
520 
521 template <typename... Ts>
522 using VariantUnion =
523  typename VariantUnionFinder<AllTriviallyDestructible<Ts...>::value, Ts...>::type;
524 
525 // --------------------------------------------------------------------------------
526 // Methods to get values out of the variant union
527 template <viskores::IdComponent I, typename UnionType>
528 struct VariantUnionGetImpl;
529 
530 template <typename UnionType>
531 struct VariantUnionGetImpl<0, UnionType>
532 {
533  using ReturnType = decltype(std::declval<UnionType>().V0);
534  VISKORES_DEVICE static ReturnType& Get(UnionType& storage) noexcept
535  {
536  return storage.V0;
537  }
538  VISKORES_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
539  {
540  return storage.V0;
541  }
542 };
543 
544 template <typename UnionType>
545 struct VariantUnionGetImpl<1, UnionType>
546 {
547  using ReturnType = decltype(std::declval<UnionType>().V1);
548  VISKORES_DEVICE static ReturnType& Get(UnionType& storage) noexcept
549  {
550  return storage.V1;
551  }
552  VISKORES_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
553  {
554  return storage.V1;
555  }
556 };
557 
558 template <typename UnionType>
559 struct VariantUnionGetImpl<2, UnionType>
560 {
561  using ReturnType = decltype(std::declval<UnionType>().V2);
562  VISKORES_DEVICE static ReturnType& Get(UnionType& storage) noexcept
563  {
564  return storage.V2;
565  }
566  VISKORES_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
567  {
568  return storage.V2;
569  }
570 };
571 
572 template <typename UnionType>
573 struct VariantUnionGetImpl<3, UnionType>
574 {
575  using ReturnType = decltype(std::declval<UnionType>().V3);
576  VISKORES_DEVICE static ReturnType& Get(UnionType& storage) noexcept
577  {
578  return storage.V3;
579  }
580  VISKORES_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
581  {
582  return storage.V3;
583  }
584 };
585 
586 template <typename UnionType>
587 struct VariantUnionGetImpl<4, UnionType>
588 {
589  using ReturnType = decltype(std::declval<UnionType>().V4);
590  VISKORES_DEVICE static ReturnType& Get(UnionType& storage) noexcept
591  {
592  return storage.V4;
593  }
594  VISKORES_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
595  {
596  return storage.V4;
597  }
598 };
599 
600 template <typename UnionType>
601 struct VariantUnionGetImpl<5, UnionType>
602 {
603  using ReturnType = decltype(std::declval<UnionType>().V5);
604  VISKORES_DEVICE static ReturnType& Get(UnionType& storage) noexcept
605  {
606  return storage.V5;
607  }
608  VISKORES_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
609  {
610  return storage.V5;
611  }
612 };
613 
614 template <typename UnionType>
615 struct VariantUnionGetImpl<6, UnionType>
616 {
617  using ReturnType = decltype(std::declval<UnionType>().V6);
618  VISKORES_DEVICE static ReturnType& Get(UnionType& storage) noexcept
619  {
620  return storage.V6;
621  }
622  VISKORES_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
623  {
624  return storage.V6;
625  }
626 };
627 
628 template <typename UnionType>
629 struct VariantUnionGetImpl<7, UnionType>
630 {
631  using ReturnType = decltype(std::declval<UnionType>().V7);
632  VISKORES_DEVICE static ReturnType& Get(UnionType& storage) noexcept
633  {
634  return storage.V7;
635  }
636  VISKORES_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
637  {
638  return storage.V7;
639  }
640 };
641 
642 
643 template <viskores::IdComponent I, typename UnionType>
644 struct VariantUnionGetImpl
645 {
646  VISKORES_STATIC_ASSERT(I >= 8);
647  using RecursiveGet = VariantUnionGetImpl<I - 8, decltype(std::declval<UnionType&>().Remaining)>;
648  using ReturnType = typename RecursiveGet::ReturnType;
649  VISKORES_DEVICE static ReturnType& Get(UnionType& storage) noexcept
650  {
651  return RecursiveGet::Get(storage.Remaining);
652  }
653  VISKORES_DEVICE static const ReturnType& Get(const UnionType& storage) noexcept
654  {
655  return RecursiveGet::Get(storage.Remaining);
656  }
657 };
658 
659 template <viskores::IdComponent I, typename UnionType>
660 VISKORES_DEVICE auto VariantUnionGet(UnionType& storage) noexcept
661  -> decltype(VariantUnionGetImpl<I, typename std::decay<UnionType>::type>::Get(storage))&
662 {
663  return VariantUnionGetImpl<I, typename std::decay<UnionType>::type>::Get(storage);
664 }
665 
666 // --------------------------------------------------------------------------------
667 // Internal implementation of CastAndCall for Variant
668 template <std::size_t NumCases>
669 struct VariantCases
670 {
671  template <typename Functor, typename UnionType, typename... Args>
672  VISKORES_DEVICE static
673 #ifdef VISKORES_HIP
674  // this is a temporary solution to improve Kokkos/HIP compile times for
675  // ConnectivityTracer in Rendering.
676  //
677  // This function currently gets inlined many times, which dramatically increases
678  // both compile time and the size of the resulting code-object
679  __attribute__((noinline))
680 #else
681  inline
682 #endif
683  auto CastAndCall(
684  viskores::IdComponent index,
685  Functor&& f,
686  UnionType& storage,
687  Args&&... args) noexcept(noexcept(f(storage.V0, args...)))
688  -> decltype(f(storage.V0, args...))
689  {
690  VISKORES_ASSERT((index >= 0) && (index < static_cast<viskores::IdComponent>(NumCases)));
691  switch (index)
692  {
693  case 0:
694  // If you get a compile error here, it probably means that you have called
695  // Variant::CastAndCall with a functor that does not accept one of the types in the
696  // Variant. The functor you provide must be callable with all types in the Variant, not
697  // just the one that it currently holds.
698  return f(storage.V0, std::forward<Args>(args)...);
699  case 1:
700  // If you get a compile error here, it probably means that you have called
701  // Variant::CastAndCall with a functor that does not accept one of the types in the
702  // Variant. The functor you provide must be callable with all types in the Variant, not
703  // just the one that it currently holds.
704  return f(storage.V1, std::forward<Args>(args)...);
705  case 2:
706  // If you get a compile error here, it probably means that you have called
707  // Variant::CastAndCall with a functor that does not accept one of the types in the
708  // Variant. The functor you provide must be callable with all types in the Variant, not
709  // just the one that it currently holds.
710  return f(storage.V2, std::forward<Args>(args)...);
711  case 3:
712  // If you get a compile error here, it probably means that you have called
713  // Variant::CastAndCall with a functor that does not accept one of the types in the
714  // Variant. The functor you provide must be callable with all types in the Variant, not
715  // just the one that it currently holds.
716  return f(storage.V3, std::forward<Args>(args)...);
717  case 4:
718  // If you get a compile error here, it probably means that you have called
719  // Variant::CastAndCall with a functor that does not accept one of the types in the
720  // Variant. The functor you provide must be callable with all types in the Variant, not
721  // just the one that it currently holds.
722  return f(storage.V4, std::forward<Args>(args)...);
723  case 5:
724  // If you get a compile error here, it probably means that you have called
725  // Variant::CastAndCall with a functor that does not accept one of the types in the
726  // Variant. The functor you provide must be callable with all types in the Variant, not
727  // just the one that it currently holds.
728  return f(storage.V5, std::forward<Args>(args)...);
729  case 6:
730  // If you get a compile error here, it probably means that you have called
731  // Variant::CastAndCall with a functor that does not accept one of the types in the
732  // Variant. The functor you provide must be callable with all types in the Variant, not
733  // just the one that it currently holds.
734  return f(storage.V6, std::forward<Args>(args)...);
735  case 7:
736  // If you get a compile error here, it probably means that you have called
737  // Variant::CastAndCall with a functor that does not accept one of the types in the
738  // Variant. The functor you provide must be callable with all types in the Variant, not
739  // just the one that it currently holds.
740  return f(storage.V7, std::forward<Args>(args)...);
741  default:
742  return VariantCases<NumCases - 8>::template CastAndCall<>(
743  index - 8, std::forward<Functor>(f), storage.Remaining, std::forward<Args>(args)...);
744  }
745  }
746 };
747 
748 template<>
749 struct VariantCases<1>
750 {
751  template <typename Functor, typename UnionType, typename... Args>
752  VISKORES_DEVICE static inline auto CastAndCall(
753  viskores::IdComponent index,
754  Functor&& f,
755  UnionType& storage,
756  Args&&... args) noexcept(noexcept(f(storage.V0, args...)))
757  -> decltype(f(storage.V0, args...))
758  {
759  // Assume index is 0. Saves us some conditionals.
760  VISKORES_ASSERT(index == 0);
761  (void)index;
762  return f(storage.V0, std::forward<Args>(args)...);
763  }
764 };
765 
766 template<>
767 struct VariantCases<2>
768 {
769  template <typename Functor, typename UnionType, typename... Args>
770  VISKORES_DEVICE static
771 #ifdef VISKORES_HIP
772  // this is a temporary solution to improve Kokkos/HIP compile times for
773  // ConnectivityTracer in Rendering.
774  //
775  // This function currently gets inlined many times, which dramatically increases
776  // both compile time and the size of the resulting code-object
777  __attribute__((noinline))
778 #else
779  inline
780 #endif
781  auto CastAndCall(
782  viskores::IdComponent index,
783  Functor&& f,
784  UnionType& storage,
785  Args&&... args) noexcept(noexcept(f(storage.V0, args...)))
786  -> decltype(f(storage.V0, args...))
787  {
788  // Assume index is 0. Saves us some conditionals.
789  VISKORES_ASSERT((index >= 0) && (index < 2));
790  switch (index)
791  {
792  default:
793  case 0:
794  // If you get a compile error here, it probably means that you have called
795  // Variant::CastAndCall with a functor that does not accept one of the types in the
796  // Variant. The functor you provide must be callable with all types in the Variant, not
797  // just the one that it currently holds.
798  return f(storage.V0, std::forward<Args>(args)...);
799  case 1:
800  // If you get a compile error here, it probably means that you have called
801  // Variant::CastAndCall with a functor that does not accept one of the types in the
802  // Variant. The functor you provide must be callable with all types in the Variant, not
803  // just the one that it currently holds.
804  return f(storage.V1, std::forward<Args>(args)...);
805  }
806  }
807 };
808 template<>
809 struct VariantCases<3>
810 {
811  template <typename Functor, typename UnionType, typename... Args>
812  VISKORES_DEVICE static
813 #ifdef VISKORES_HIP
814  // this is a temporary solution to improve Kokkos/HIP compile times for
815  // ConnectivityTracer in Rendering.
816  //
817  // This function currently gets inlined many times, which dramatically increases
818  // both compile time and the size of the resulting code-object
819  __attribute__((noinline))
820 #else
821  inline
822 #endif
823  auto CastAndCall(
824  viskores::IdComponent index,
825  Functor&& f,
826  UnionType& storage,
827  Args&&... args) noexcept(noexcept(f(storage.V0, args...)))
828  -> decltype(f(storage.V0, args...))
829  {
830  // Assume index is 0. Saves us some conditionals.
831  VISKORES_ASSERT((index >= 0) && (index < 3));
832  switch (index)
833  {
834  default:
835  case 0:
836  // If you get a compile error here, it probably means that you have called
837  // Variant::CastAndCall with a functor that does not accept one of the types in the
838  // Variant. The functor you provide must be callable with all types in the Variant, not
839  // just the one that it currently holds.
840  return f(storage.V0, std::forward<Args>(args)...);
841  case 1:
842  // If you get a compile error here, it probably means that you have called
843  // Variant::CastAndCall with a functor that does not accept one of the types in the
844  // Variant. The functor you provide must be callable with all types in the Variant, not
845  // just the one that it currently holds.
846  return f(storage.V1, std::forward<Args>(args)...);
847  case 2:
848  // If you get a compile error here, it probably means that you have called
849  // Variant::CastAndCall with a functor that does not accept one of the types in the
850  // Variant. The functor you provide must be callable with all types in the Variant, not
851  // just the one that it currently holds.
852  return f(storage.V2, std::forward<Args>(args)...);
853  }
854  }
855 };
856 template<>
857 struct VariantCases<4>
858 {
859  template <typename Functor, typename UnionType, typename... Args>
860  VISKORES_DEVICE static
861 #ifdef VISKORES_HIP
862  // this is a temporary solution to improve Kokkos/HIP compile times for
863  // ConnectivityTracer in Rendering.
864  //
865  // This function currently gets inlined many times, which dramatically increases
866  // both compile time and the size of the resulting code-object
867  __attribute__((noinline))
868 #else
869  inline
870 #endif
871  auto CastAndCall(
872  viskores::IdComponent index,
873  Functor&& f,
874  UnionType& storage,
875  Args&&... args) noexcept(noexcept(f(storage.V0, args...)))
876  -> decltype(f(storage.V0, args...))
877  {
878  // Assume index is 0. Saves us some conditionals.
879  VISKORES_ASSERT((index >= 0) && (index < 4));
880  switch (index)
881  {
882  default:
883  case 0:
884  // If you get a compile error here, it probably means that you have called
885  // Variant::CastAndCall with a functor that does not accept one of the types in the
886  // Variant. The functor you provide must be callable with all types in the Variant, not
887  // just the one that it currently holds.
888  return f(storage.V0, std::forward<Args>(args)...);
889  case 1:
890  // If you get a compile error here, it probably means that you have called
891  // Variant::CastAndCall with a functor that does not accept one of the types in the
892  // Variant. The functor you provide must be callable with all types in the Variant, not
893  // just the one that it currently holds.
894  return f(storage.V1, std::forward<Args>(args)...);
895  case 2:
896  // If you get a compile error here, it probably means that you have called
897  // Variant::CastAndCall with a functor that does not accept one of the types in the
898  // Variant. The functor you provide must be callable with all types in the Variant, not
899  // just the one that it currently holds.
900  return f(storage.V2, std::forward<Args>(args)...);
901  case 3:
902  // If you get a compile error here, it probably means that you have called
903  // Variant::CastAndCall with a functor that does not accept one of the types in the
904  // Variant. The functor you provide must be callable with all types in the Variant, not
905  // just the one that it currently holds.
906  return f(storage.V3, std::forward<Args>(args)...);
907  }
908  }
909 };
910 template<>
911 struct VariantCases<5>
912 {
913  template <typename Functor, typename UnionType, typename... Args>
914  VISKORES_DEVICE static
915 #ifdef VISKORES_HIP
916  // this is a temporary solution to improve Kokkos/HIP compile times for
917  // ConnectivityTracer in Rendering.
918  //
919  // This function currently gets inlined many times, which dramatically increases
920  // both compile time and the size of the resulting code-object
921  __attribute__((noinline))
922 #else
923  inline
924 #endif
925  auto CastAndCall(
926  viskores::IdComponent index,
927  Functor&& f,
928  UnionType& storage,
929  Args&&... args) noexcept(noexcept(f(storage.V0, args...)))
930  -> decltype(f(storage.V0, args...))
931  {
932  // Assume index is 0. Saves us some conditionals.
933  VISKORES_ASSERT((index >= 0) && (index < 5));
934  switch (index)
935  {
936  default:
937  case 0:
938  // If you get a compile error here, it probably means that you have called
939  // Variant::CastAndCall with a functor that does not accept one of the types in the
940  // Variant. The functor you provide must be callable with all types in the Variant, not
941  // just the one that it currently holds.
942  return f(storage.V0, std::forward<Args>(args)...);
943  case 1:
944  // If you get a compile error here, it probably means that you have called
945  // Variant::CastAndCall with a functor that does not accept one of the types in the
946  // Variant. The functor you provide must be callable with all types in the Variant, not
947  // just the one that it currently holds.
948  return f(storage.V1, std::forward<Args>(args)...);
949  case 2:
950  // If you get a compile error here, it probably means that you have called
951  // Variant::CastAndCall with a functor that does not accept one of the types in the
952  // Variant. The functor you provide must be callable with all types in the Variant, not
953  // just the one that it currently holds.
954  return f(storage.V2, std::forward<Args>(args)...);
955  case 3:
956  // If you get a compile error here, it probably means that you have called
957  // Variant::CastAndCall with a functor that does not accept one of the types in the
958  // Variant. The functor you provide must be callable with all types in the Variant, not
959  // just the one that it currently holds.
960  return f(storage.V3, std::forward<Args>(args)...);
961  case 4:
962  // If you get a compile error here, it probably means that you have called
963  // Variant::CastAndCall with a functor that does not accept one of the types in the
964  // Variant. The functor you provide must be callable with all types in the Variant, not
965  // just the one that it currently holds.
966  return f(storage.V4, std::forward<Args>(args)...);
967  }
968  }
969 };
970 template<>
971 struct VariantCases<6>
972 {
973  template <typename Functor, typename UnionType, typename... Args>
974  VISKORES_DEVICE static
975 #ifdef VISKORES_HIP
976  // this is a temporary solution to improve Kokkos/HIP compile times for
977  // ConnectivityTracer in Rendering.
978  //
979  // This function currently gets inlined many times, which dramatically increases
980  // both compile time and the size of the resulting code-object
981  __attribute__((noinline))
982 #else
983  inline
984 #endif
985  auto CastAndCall(
986  viskores::IdComponent index,
987  Functor&& f,
988  UnionType& storage,
989  Args&&... args) noexcept(noexcept(f(storage.V0, args...)))
990  -> decltype(f(storage.V0, args...))
991  {
992  // Assume index is 0. Saves us some conditionals.
993  VISKORES_ASSERT((index >= 0) && (index < 6));
994  switch (index)
995  {
996  default:
997  case 0:
998  // If you get a compile error here, it probably means that you have called
999  // Variant::CastAndCall with a functor that does not accept one of the types in the
1000  // Variant. The functor you provide must be callable with all types in the Variant, not
1001  // just the one that it currently holds.
1002  return f(storage.V0, std::forward<Args>(args)...);
1003  case 1:
1004  // If you get a compile error here, it probably means that you have called
1005  // Variant::CastAndCall with a functor that does not accept one of the types in the
1006  // Variant. The functor you provide must be callable with all types in the Variant, not
1007  // just the one that it currently holds.
1008  return f(storage.V1, std::forward<Args>(args)...);
1009  case 2:
1010  // If you get a compile error here, it probably means that you have called
1011  // Variant::CastAndCall with a functor that does not accept one of the types in the
1012  // Variant. The functor you provide must be callable with all types in the Variant, not
1013  // just the one that it currently holds.
1014  return f(storage.V2, std::forward<Args>(args)...);
1015  case 3:
1016  // If you get a compile error here, it probably means that you have called
1017  // Variant::CastAndCall with a functor that does not accept one of the types in the
1018  // Variant. The functor you provide must be callable with all types in the Variant, not
1019  // just the one that it currently holds.
1020  return f(storage.V3, std::forward<Args>(args)...);
1021  case 4:
1022  // If you get a compile error here, it probably means that you have called
1023  // Variant::CastAndCall with a functor that does not accept one of the types in the
1024  // Variant. The functor you provide must be callable with all types in the Variant, not
1025  // just the one that it currently holds.
1026  return f(storage.V4, std::forward<Args>(args)...);
1027  case 5:
1028  // If you get a compile error here, it probably means that you have called
1029  // Variant::CastAndCall with a functor that does not accept one of the types in the
1030  // Variant. The functor you provide must be callable with all types in the Variant, not
1031  // just the one that it currently holds.
1032  return f(storage.V5, std::forward<Args>(args)...);
1033  }
1034  }
1035 };
1036 template<>
1037 struct VariantCases<7>
1038 {
1039  template <typename Functor, typename UnionType, typename... Args>
1040  VISKORES_DEVICE static
1041 #ifdef VISKORES_HIP
1042  // this is a temporary solution to improve Kokkos/HIP compile times for
1043  // ConnectivityTracer in Rendering.
1044  //
1045  // This function currently gets inlined many times, which dramatically increases
1046  // both compile time and the size of the resulting code-object
1047  __attribute__((noinline))
1048 #else
1049  inline
1050 #endif
1051  auto CastAndCall(
1052  viskores::IdComponent index,
1053  Functor&& f,
1054  UnionType& storage,
1055  Args&&... args) noexcept(noexcept(f(storage.V0, args...)))
1056  -> decltype(f(storage.V0, args...))
1057  {
1058  // Assume index is 0. Saves us some conditionals.
1059  VISKORES_ASSERT((index >= 0) && (index < 7));
1060  switch (index)
1061  {
1062  default:
1063  case 0:
1064  // If you get a compile error here, it probably means that you have called
1065  // Variant::CastAndCall with a functor that does not accept one of the types in the
1066  // Variant. The functor you provide must be callable with all types in the Variant, not
1067  // just the one that it currently holds.
1068  return f(storage.V0, std::forward<Args>(args)...);
1069  case 1:
1070  // If you get a compile error here, it probably means that you have called
1071  // Variant::CastAndCall with a functor that does not accept one of the types in the
1072  // Variant. The functor you provide must be callable with all types in the Variant, not
1073  // just the one that it currently holds.
1074  return f(storage.V1, std::forward<Args>(args)...);
1075  case 2:
1076  // If you get a compile error here, it probably means that you have called
1077  // Variant::CastAndCall with a functor that does not accept one of the types in the
1078  // Variant. The functor you provide must be callable with all types in the Variant, not
1079  // just the one that it currently holds.
1080  return f(storage.V2, std::forward<Args>(args)...);
1081  case 3:
1082  // If you get a compile error here, it probably means that you have called
1083  // Variant::CastAndCall with a functor that does not accept one of the types in the
1084  // Variant. The functor you provide must be callable with all types in the Variant, not
1085  // just the one that it currently holds.
1086  return f(storage.V3, std::forward<Args>(args)...);
1087  case 4:
1088  // If you get a compile error here, it probably means that you have called
1089  // Variant::CastAndCall with a functor that does not accept one of the types in the
1090  // Variant. The functor you provide must be callable with all types in the Variant, not
1091  // just the one that it currently holds.
1092  return f(storage.V4, std::forward<Args>(args)...);
1093  case 5:
1094  // If you get a compile error here, it probably means that you have called
1095  // Variant::CastAndCall with a functor that does not accept one of the types in the
1096  // Variant. The functor you provide must be callable with all types in the Variant, not
1097  // just the one that it currently holds.
1098  return f(storage.V5, std::forward<Args>(args)...);
1099  case 6:
1100  // If you get a compile error here, it probably means that you have called
1101  // Variant::CastAndCall with a functor that does not accept one of the types in the
1102  // Variant. The functor you provide must be callable with all types in the Variant, not
1103  // just the one that it currently holds.
1104  return f(storage.V6, std::forward<Args>(args)...);
1105  }
1106  }
1107 };
1108 template<>
1109 struct VariantCases<8>
1110 {
1111  template <typename Functor, typename UnionType, typename... Args>
1112  VISKORES_DEVICE static
1113 #ifdef VISKORES_HIP
1114  // this is a temporary solution to improve Kokkos/HIP compile times for
1115  // ConnectivityTracer in Rendering.
1116  //
1117  // This function currently gets inlined many times, which dramatically increases
1118  // both compile time and the size of the resulting code-object
1119  __attribute__((noinline))
1120 #else
1121  inline
1122 #endif
1123  auto CastAndCall(
1124  viskores::IdComponent index,
1125  Functor&& f,
1126  UnionType& storage,
1127  Args&&... args) noexcept(noexcept(f(storage.V0, args...)))
1128  -> decltype(f(storage.V0, args...))
1129  {
1130  // Assume index is 0. Saves us some conditionals.
1131  VISKORES_ASSERT((index >= 0) && (index < 8));
1132  switch (index)
1133  {
1134  default:
1135  case 0:
1136  // If you get a compile error here, it probably means that you have called
1137  // Variant::CastAndCall with a functor that does not accept one of the types in the
1138  // Variant. The functor you provide must be callable with all types in the Variant, not
1139  // just the one that it currently holds.
1140  return f(storage.V0, std::forward<Args>(args)...);
1141  case 1:
1142  // If you get a compile error here, it probably means that you have called
1143  // Variant::CastAndCall with a functor that does not accept one of the types in the
1144  // Variant. The functor you provide must be callable with all types in the Variant, not
1145  // just the one that it currently holds.
1146  return f(storage.V1, std::forward<Args>(args)...);
1147  case 2:
1148  // If you get a compile error here, it probably means that you have called
1149  // Variant::CastAndCall with a functor that does not accept one of the types in the
1150  // Variant. The functor you provide must be callable with all types in the Variant, not
1151  // just the one that it currently holds.
1152  return f(storage.V2, std::forward<Args>(args)...);
1153  case 3:
1154  // If you get a compile error here, it probably means that you have called
1155  // Variant::CastAndCall with a functor that does not accept one of the types in the
1156  // Variant. The functor you provide must be callable with all types in the Variant, not
1157  // just the one that it currently holds.
1158  return f(storage.V3, std::forward<Args>(args)...);
1159  case 4:
1160  // If you get a compile error here, it probably means that you have called
1161  // Variant::CastAndCall with a functor that does not accept one of the types in the
1162  // Variant. The functor you provide must be callable with all types in the Variant, not
1163  // just the one that it currently holds.
1164  return f(storage.V4, std::forward<Args>(args)...);
1165  case 5:
1166  // If you get a compile error here, it probably means that you have called
1167  // Variant::CastAndCall with a functor that does not accept one of the types in the
1168  // Variant. The functor you provide must be callable with all types in the Variant, not
1169  // just the one that it currently holds.
1170  return f(storage.V5, std::forward<Args>(args)...);
1171  case 6:
1172  // If you get a compile error here, it probably means that you have called
1173  // Variant::CastAndCall with a functor that does not accept one of the types in the
1174  // Variant. The functor you provide must be callable with all types in the Variant, not
1175  // just the one that it currently holds.
1176  return f(storage.V6, std::forward<Args>(args)...);
1177  case 7:
1178  // If you get a compile error here, it probably means that you have called
1179  // Variant::CastAndCall with a functor that does not accept one of the types in the
1180  // Variant. The functor you provide must be callable with all types in the Variant, not
1181  // just the one that it currently holds.
1182  return f(storage.V7, std::forward<Args>(args)...);
1183  }
1184  }
1185 };
1186 
1187 
1188 template <std::size_t UnionSize, typename Functor, typename UnionType, typename... Args>
1189 VISKORES_DEVICE inline auto VariantCastAndCallImpl(
1190  viskores::IdComponent index,
1191  Functor&& f,
1192  UnionType& storage,
1193  Args&&... args) noexcept(noexcept(f(storage.V0, args...)))
1194  -> decltype(f(storage.V0, args...))
1195 {
1197  index, std::forward<Functor>(f), storage, std::forward<Args>(args)...);
1198 }
1199 
1200 }
1201 }
1202 } // viskores::VISKORES_NAMESPACE::detail
viskores::Int16
int16_t Int16
Base type to use for 16-bit signed integer numbers.
Definition: Types.h:181
Types.h
viskores::cont::CastAndCall
void CastAndCall(const DynamicObject &dynamicObject, Functor &&f, Args &&... args)
A Generic interface to CastAndCall.
Definition: CastAndCall.h:55
viskores::Int8
int8_t Int8
Base type to use for 8-bit signed integer numbers.
Definition: Types.h:173
viskores::IdComponent
viskores::Int32 IdComponent
Base type to use to index small lists.
Definition: Types.h:202
viskores::List
A template used to hold a list of types.
Definition: List.h:47
viskores::Int64
signed long long Int64
Base type to use for 64-bit signed integer numbers.
Definition: Types.h:212
Assume.h
viskores
Groups connected points that have the same field value.
Definition: Atomic.h:27
viskores::ListAll
viskores::ListReduce< viskores::ListTransform< List, Predicate >, viskores::internal::meta::And, std::true_type > ListAll
Determines whether all the types in the list are "true.".
Definition: List.h:865
VISKORES_ASSERT
#define VISKORES_ASSERT(condition)
Definition: Assert.h:51
VISKORES_NAMESPACE
#define VISKORES_NAMESPACE
Definition: VariantImplDetail.h:25
viskores::Int32
int32_t Int32
Base type to use for 32-bit signed integer numbers.
Definition: Types.h:189
viskores::Get
auto Get(const viskores::Tuple< Ts... > &tuple)
Retrieve the object from a viskores::Tuple at the given index.
Definition: Tuple.h:89
VISKORES_STATIC_ASSERT
#define VISKORES_STATIC_ASSERT(condition)
Definition: StaticAssert.h:24
VISKORES_DEVICE
#define VISKORES_DEVICE
Definition: VariantImplDetail.h:24
List.h