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 #ifndef vtk_m_cont_UnknownArrayHandle_h
11 #define vtk_m_cont_UnknownArrayHandle_h
12 
13 #include <vtkm/cont/vtkm_cont_export.h>
14 
15 #include <vtkm/cont/ArrayExtractComponent.h>
16 #include <vtkm/cont/ArrayHandle.h>
17 #include <vtkm/cont/ArrayHandleCast.h>
18 #include <vtkm/cont/ArrayHandleMultiplexer.h>
19 #include <vtkm/cont/ArrayHandleRecombineVec.h>
20 #include <vtkm/cont/ArrayHandleStride.h>
21 #include <vtkm/cont/CastAndCall.h>
22 #include <vtkm/cont/DefaultTypes.h>
23 
24 #include <memory>
25 #include <typeindex>
26 
27 namespace vtkm
28 {
29 namespace cont
30 {
31 
32 namespace detail
33 {
34 
35 template <typename T, typename S>
UnknownAHDelete(void * mem)36 static void UnknownAHDelete(void* mem)
37 {
38   using AH = vtkm::cont::ArrayHandle<T, S>;
39   AH* arrayHandle = reinterpret_cast<AH*>(mem);
40   delete arrayHandle;
41 }
42 
43 template <typename T, typename S>
UnknownAHNewInstance()44 static void* UnknownAHNewInstance()
45 {
46   return new vtkm::cont::ArrayHandle<T, S>;
47 }
48 
49 template <typename T, typename S>
UnknownAHNumberOfValues(void * mem)50 static vtkm::Id UnknownAHNumberOfValues(void* mem)
51 {
52   using AH = vtkm::cont::ArrayHandle<T, S>;
53   AH* arrayHandle = reinterpret_cast<AH*>(mem);
54   return arrayHandle->GetNumberOfValues();
55 }
56 
57 template <typename T, typename StaticSize = typename vtkm::VecTraits<T>::IsSizeStatic>
58 struct UnknownAHNumberOfComponentsImpl;
59 template <typename T>
60 struct UnknownAHNumberOfComponentsImpl<T, vtkm::VecTraitsTagSizeStatic>
61 {
62   static constexpr vtkm::IdComponent Value = vtkm::VecTraits<T>::NUM_COMPONENTS;
63 };
64 template <typename T>
65 struct UnknownAHNumberOfComponentsImpl<T, vtkm::VecTraitsTagSizeVariable>
66 {
67   static constexpr vtkm::IdComponent Value = 0;
68 };
69 
70 template <typename T>
71 static vtkm::IdComponent UnknownAHNumberOfComponents()
72 {
73   return UnknownAHNumberOfComponentsImpl<T>::Value;
74 }
75 
76 template <typename T, typename StaticSize = typename vtkm::VecTraits<T>::IsSizeStatic>
77 struct UnknownAHNumberOfComponentsFlatImpl;
78 template <typename T>
79 struct UnknownAHNumberOfComponentsFlatImpl<T, vtkm::VecTraitsTagSizeStatic>
80 {
81   static constexpr vtkm::IdComponent Value = vtkm::VecFlat<T>::NUM_COMPONENTS;
82 };
83 template <typename T>
84 struct UnknownAHNumberOfComponentsFlatImpl<T, vtkm::VecTraitsTagSizeVariable>
85 {
86   static constexpr vtkm::IdComponent Value = 0;
87 };
88 
89 template <typename T>
90 static vtkm::IdComponent UnknownAHNumberOfComponentsFlat()
91 {
92   return UnknownAHNumberOfComponentsFlatImpl<T>::Value;
93 }
94 
95 template <typename T, typename S>
96 static void UnknownAHAllocate(void* mem, vtkm::Id numValues)
97 {
98   using AH = vtkm::cont::ArrayHandle<T, S>;
99   AH* arrayHandle = reinterpret_cast<AH*>(mem);
100   arrayHandle->Allocate(numValues);
101 }
102 
103 template <typename T, typename S>
104 static std::vector<vtkm::cont::internal::Buffer>
105 UnknownAHExtractComponent(void* mem, vtkm::IdComponent componentIndex, vtkm::CopyFlag allowCopy)
106 {
107   using AH = vtkm::cont::ArrayHandle<T, S>;
108   AH* arrayHandle = reinterpret_cast<AH*>(mem);
109   auto componentArray = vtkm::cont::ArrayExtractComponent(*arrayHandle, componentIndex, allowCopy);
110   vtkm::cont::internal::Buffer* buffers = componentArray.GetBuffers();
111   return std::vector<vtkm::cont::internal::Buffer>(buffers, buffers + 2);
112 }
113 
114 template <typename T, typename S>
115 static void UnknownAHReleaseResources(void* mem)
116 {
117   using AH = vtkm::cont::ArrayHandle<T, S>;
118   AH* arrayHandle = reinterpret_cast<AH*>(mem);
119   arrayHandle->ReleaseResources();
120 }
121 
122 template <typename T, typename S>
123 static void UnknownAHReleaseResourcesExecution(void* mem)
124 {
125   using AH = vtkm::cont::ArrayHandle<T, S>;
126   AH* arrayHandle = reinterpret_cast<AH*>(mem);
127   arrayHandle->ReleaseResourcesExecution();
128 }
129 
130 template <typename T, typename S>
131 static void UnknownAHPrintSummary(void* mem, std::ostream& out, bool full)
132 {
133   using AH = vtkm::cont::ArrayHandle<T, S>;
134   AH* arrayHandle = reinterpret_cast<AH*>(mem);
135   vtkm::cont::printSummary_ArrayHandle(*arrayHandle, out, full);
136 }
137 
138 struct VTKM_CONT_EXPORT UnknownAHContainer;
139 
140 struct MakeUnknownAHContainerFunctor
141 {
142   template <typename T, typename S>
143   std::shared_ptr<UnknownAHContainer> operator()(const vtkm::cont::ArrayHandle<T, S>& array) const;
144 };
145 
146 struct VTKM_CONT_EXPORT UnknownAHComponentInfo
147 {
148   std::type_index Type;
149   bool IsIntegral;
150   bool IsFloat;
151   bool IsSigned;
152   std::size_t Size;
153 
154   UnknownAHComponentInfo() = delete;
155 
156   bool operator==(const UnknownAHComponentInfo& rhs);
157 
158   template <typename T>
159   static UnknownAHComponentInfo Make()
160   {
161     return UnknownAHComponentInfo{ typeid(T),
162                                    std::is_integral<T>::value,
163                                    std::is_floating_point<T>::value,
164                                    std::is_signed<T>::value,
165                                    sizeof(T) };
166   }
167 
168 private:
169   UnknownAHComponentInfo(std::type_index&& type,
170                          bool isIntegral,
171                          bool isFloat,
172                          bool isSigned,
173                          std::size_t size)
174     : Type(std::move(type))
175     , IsIntegral(isIntegral)
176     , IsFloat(isFloat)
177     , IsSigned(isSigned)
178     , Size(size)
179   {
180   }
181 };
182 
183 struct VTKM_CONT_EXPORT UnknownAHContainer
184 {
185   void* ArrayHandlePointer;
186 
187   std::type_index ValueType;
188   std::type_index StorageType;
189   UnknownAHComponentInfo BaseComponentType;
190 
191   using DeleteType = void(void*);
192   DeleteType* DeleteFunction;
193 
194   using NewInstanceType = void*();
195   NewInstanceType* NewInstance;
196 
197   using NewInstanceBasicType = std::shared_ptr<UnknownAHContainer>();
198   NewInstanceBasicType* NewInstanceBasic;
199   NewInstanceBasicType* NewInstanceFloatBasic;
200 
201   using NumberOfValuesType = vtkm::Id(void*);
202   NumberOfValuesType* NumberOfValues;
203 
204   using NumberOfComponentsType = vtkm::IdComponent();
205   NumberOfComponentsType* NumberOfComponents;
206   NumberOfComponentsType* NumberOfComponentsFlat;
207 
208   using AllocateType = void(void*, vtkm::Id);
209   AllocateType* Allocate;
210 
211   using ExtractComponentType = std::vector<vtkm::cont::internal::Buffer>(void*,
212                                                                          vtkm::IdComponent,
213                                                                          vtkm::CopyFlag);
214   ExtractComponentType* ExtractComponent;
215 
216   using ReleaseResourcesType = void(void*);
217   ReleaseResourcesType* ReleaseResources;
218   ReleaseResourcesType* ReleaseResourcesExecution;
219 
220   using PrintSummaryType = void(void*, std::ostream&, bool);
221   PrintSummaryType* PrintSummary;
222 
223   void operator=(const UnknownAHContainer&) = delete;
224 
225   ~UnknownAHContainer() { this->DeleteFunction(this->ArrayHandlePointer); }
226 
227   std::shared_ptr<UnknownAHContainer> MakeNewInstance() const;
228 
229   template <typename T, typename S>
230   static std::shared_ptr<UnknownAHContainer> Make(const vtkm::cont::ArrayHandle<T, S>& array)
231   {
232     return std::shared_ptr<UnknownAHContainer>(new UnknownAHContainer(array));
233   }
234 
235   template <typename TargetT, typename SourceT, typename SourceS>
236   static std::shared_ptr<UnknownAHContainer> Make(
237     const vtkm::cont::ArrayHandle<TargetT, vtkm::cont::StorageTagCast<SourceT, SourceS>>& array)
238   {
239     vtkm::cont::ArrayHandleCast<TargetT, vtkm::cont::ArrayHandle<SourceT, SourceS>> castArray =
240       array;
241     return Make(castArray.GetSourceArray());
242   }
243 
244   template <typename T, typename... Ss>
245   static std::shared_ptr<UnknownAHContainer> Make(
246     const vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagMultiplexer<Ss...>>& array)
247   {
248     auto&& variant = vtkm::cont::ArrayHandleMultiplexer<vtkm::cont::ArrayHandle<T, Ss>...>(array)
249                        .GetArrayHandleVariant();
250     if (variant.IsValid())
251     {
252       return variant.CastAndCall(MakeUnknownAHContainerFunctor{});
253     }
254     else
255     {
256       return std::shared_ptr<UnknownAHContainer>{};
257     }
258   }
259 
260 private:
261   UnknownAHContainer(const UnknownAHContainer&) = default;
262 
263   template <typename T, typename S>
264   explicit UnknownAHContainer(const vtkm::cont::ArrayHandle<T, S>& array);
265 };
266 
267 template <typename T>
268 static std::shared_ptr<UnknownAHContainer> UnknownAHNewInstanceBasic(vtkm::VecTraitsTagSizeStatic)
269 {
270   return UnknownAHContainer::Make(vtkm::cont::ArrayHandleBasic<T>{});
271 }
272 template <typename T>
273 static std::shared_ptr<UnknownAHContainer> UnknownAHNewInstanceBasic(vtkm::VecTraitsTagSizeVariable)
274 {
275   throw vtkm::cont::ErrorBadType("Cannot create a basic array container from with ValueType of " +
276                                  vtkm::cont::TypeToString<T>());
277 }
278 template <typename T>
279 static std::shared_ptr<UnknownAHContainer> UnknownAHNewInstanceBasic()
280 {
281   return UnknownAHNewInstanceBasic<T>(typename vtkm::VecTraits<T>::IsSizeStatic{});
282 }
283 
284 template <typename T>
285 static std::shared_ptr<UnknownAHContainer> UnknownAHNewInstanceFloatBasic(
286   vtkm::VecTraitsTagSizeStatic)
287 {
288   using FloatT = typename vtkm::VecTraits<T>::template ReplaceBaseComponentType<vtkm::FloatDefault>;
289   return UnknownAHContainer::Make(vtkm::cont::ArrayHandleBasic<FloatT>{});
290 }
291 template <typename T>
292 static std::shared_ptr<UnknownAHContainer> UnknownAHNewInstanceFloatBasic(
293   vtkm::VecTraitsTagSizeVariable)
294 {
295   throw vtkm::cont::ErrorBadType("Cannot create a basic array container from with ValueType of " +
296                                  vtkm::cont::TypeToString<T>());
297 }
298 template <typename T>
299 static std::shared_ptr<UnknownAHContainer> UnknownAHNewInstanceFloatBasic()
300 {
301   return UnknownAHNewInstanceFloatBasic<T>(typename vtkm::VecTraits<T>::IsSizeStatic{});
302 }
303 
304 template <typename T, typename S>
305 inline UnknownAHContainer::UnknownAHContainer(const vtkm::cont::ArrayHandle<T, S>& array)
306   : ArrayHandlePointer(new vtkm::cont::ArrayHandle<T, S>(array))
307   , ValueType(typeid(T))
308   , StorageType(typeid(S))
309   , BaseComponentType(
310       UnknownAHComponentInfo::Make<typename vtkm::VecTraits<T>::BaseComponentType>())
311   , DeleteFunction(detail::UnknownAHDelete<T, S>)
312   , NewInstance(detail::UnknownAHNewInstance<T, S>)
313   , NewInstanceBasic(detail::UnknownAHNewInstanceBasic<T>)
314   , NewInstanceFloatBasic(detail::UnknownAHNewInstanceFloatBasic<T>)
315   , NumberOfValues(detail::UnknownAHNumberOfValues<T, S>)
316   , NumberOfComponents(detail::UnknownAHNumberOfComponents<T>)
317   , NumberOfComponentsFlat(detail::UnknownAHNumberOfComponentsFlat<T>)
318   , Allocate(detail::UnknownAHAllocate<T, S>)
319   , ExtractComponent(detail::UnknownAHExtractComponent<T, S>)
320   , ReleaseResources(detail::UnknownAHReleaseResources<T, S>)
321   , ReleaseResourcesExecution(detail::UnknownAHReleaseResourcesExecution<T, S>)
322   , PrintSummary(detail::UnknownAHPrintSummary<T, S>)
323 {
324 }
325 
326 template <typename T, typename S>
327 inline std::shared_ptr<UnknownAHContainer> MakeUnknownAHContainerFunctor::operator()(
328   const vtkm::cont::ArrayHandle<T, S>& array) const
329 {
330   return UnknownAHContainer::Make(array);
331 };
332 
333 } // namespace detail
334 
335 // Forward declaration. Include UncertainArrayHandle.h if using this.
336 template <typename ValueTypeList, typename StorageTypeList>
337 class UncertainArrayHandle;
338 
339 /// \brief An ArrayHandle of an unknown value type and storage.
340 ///
341 /// `UnknownArrayHandle` holds an `ArrayHandle` object using runtime polymorphism
342 /// to manage different value and storage types rather than compile-time templates.
343 /// This adds a programming convenience that helps avoid a proliferation of
344 /// templates. It also provides the management necessary to interface VTK-m with
345 /// data sources where types will not be known until runtime and is the storage
346 /// mechanism for classes like `DataSet` and `Field` that can hold numerous
347 /// types.
348 ///
349 /// To interface between the runtime polymorphism and the templated algorithms
350 /// in VTK-m, `UnknownArrayHandle` contains a method named `CastAndCallForTypes`
351 /// that determines the correct type from some known list of value types and
352 /// storage. This mechanism is used internally by VTK-m's worklet invocation
353 /// mechanism to determine the type when running algorithms.
354 ///
355 /// If the `UnknownArrayHandle` is used in a context where the possible array
356 /// types can be whittled down to a finite list (or you have to), you can
357 /// specify lists of value types and storage using the `ResetTypesAndStorage`
358 /// method. This will convert this object to an `UncertainArrayHandle` of the
359 /// given types. In cases where a finite set of types need to specified but
360 /// there is no known subset, `VTKM_DEFAULT_TYPE_LIST` and
361 /// `VTKM_DEFAULT_STORAGE_LIST` can be used.
362 ///
363 /// `ArrayHandleCast` and `ArrayHandleMultiplexer` are treated special. If
364 /// the `UnknownArrayHandle` is set to an `ArrayHandle` of one of these
365 /// types, it will actually store the `ArrayHandle` contained. Likewise,
366 /// if the `ArrayHandle` is retrieved as one of these types, it will
367 /// automatically convert it if possible.
368 ///
369 class VTKM_CONT_EXPORT UnknownArrayHandle
370 {
371   std::shared_ptr<detail::UnknownAHContainer> Container;
372 
373   VTKM_CONT bool IsValueTypeImpl(std::type_index type) const;
374   VTKM_CONT bool IsStorageTypeImpl(std::type_index type) const;
375   VTKM_CONT bool IsBaseComponentTypeImpl(const detail::UnknownAHComponentInfo& type) const;
376 
377 public:
378   VTKM_CONT UnknownArrayHandle() = default;
379   UnknownArrayHandle(const UnknownArrayHandle&) = default;
380 
381   template <typename T, typename S>
382   VTKM_CONT UnknownArrayHandle(const vtkm::cont::ArrayHandle<T, S>& array)
383     : Container(detail::UnknownAHContainer::Make(array))
384   {
385   }
386 
387   UnknownArrayHandle& operator=(const vtkm::cont::UnknownArrayHandle&) = default;
388 
389   /// \brief Returns whether an array is stored in this `UnknownArrayHandle`.
390   ///
391   /// If the `UnknownArrayHandle` is constructed without an `ArrayHandle`, it
392   /// will not have an underlying type, and therefore the operations will be
393   /// invalid. It is still possible to set this `UnknownArrayHandle` to an
394   /// `ArrayHandle`.
395   VTKM_CONT bool IsValid() const;
396 
397   /// \brief Create a new array of the same type as this array.
398   ///
399   /// This method creates a new array that is the same type as this one and
400   /// returns a new `UnknownArrayHandle` for it. This method is convenient when
401   /// creating output arrays that should be the same type as some input array.
402   ///
403   VTKM_CONT UnknownArrayHandle NewInstance() const;
404 
405   /// \brief Create a new `ArrayHandleBasic` with the same `ValueType` as this array.
406   ///
407   /// This method creates a new `ArrayHandleBasic` that has the same `ValueType` as the
408   /// array held by this one and returns a new `UnknownArrayHandle` for it. This method
409   /// is convenient when creating output arrays that should have the same types of values
410   /// of the input, but the input might not be a writable array.
411   ///
412   VTKM_CONT UnknownArrayHandle NewInstanceBasic() const;
413 
414   /// \brief Create a new `ArrayHandleBasic` with the base component of `FloatDefault`
415   ///
416   /// This method creates a new `ArrayHandleBasic` that has a `ValueType` that is similar
417   /// to the array held by this one except that the base component type is replaced with
418   /// `vtkm::FloatDefault`. For example, if the contained array has `vtkm::Int32` value types,
419   /// the returned array will have `vtkm::FloatDefault` value types. If the contained array
420   /// has `vtkm::Id3` value types, the returned array will have `vtkm::Vec3f` value types.
421   /// If the contained array already has `vtkm::FloatDefault` as the base component (e.g.
422   /// `vtkm::FloatDefault`, `vtkm::Vec3f`, `vtkm::Vec<vtkm::Vec2f, 3>`), then the value type
423   /// will be preserved.
424   ///
425   /// The created array is returned in a new `UnknownArrayHandle`.
426   ///
427   /// This method is used to convert an array of an unknown type to an array of an almost
428   /// known type.
429   ///
430   VTKM_CONT UnknownArrayHandle NewInstanceFloatBasic() const;
431 
432   /// \brief Returns the name of the value type stored in the array.
433   ///
434   /// Returns an empty string if no array is stored.
435   VTKM_CONT std::string GetValueTypeName() const;
436 
437   /// \brief Returns the name of the base component of the value type stored in the array.
438   ///
439   /// Returns an empty string if no array is stored.
440   VTKM_CONT std::string GetBaseComponentTypeName() const;
441 
442   /// \brief Returns the name of the storage tag for the array.
443   ///
444   /// Returns an empty string if no array is stored.
445   VTKM_CONT std::string GetStorageTypeName() const;
446 
447   /// Returns true if this array matches the ValueType template argument.
448   ///
449   template <typename ValueType>
450   VTKM_CONT bool IsValueType() const
451   {
452     return this->IsValueTypeImpl(typeid(ValueType));
453   }
454 
455   /// Returns true if this array matches the StorageType template argument.
456   ///
457   template <typename StorageType>
458   VTKM_CONT bool IsStorageType() const
459   {
460     return this->IsStorageTypeImpl(typeid(StorageType));
461   }
462 
463   /// \brief Returns true if this array's `ValueType` has the provided base component type.
464   ///
465   /// The base component type is the recursive component type of any `Vec`-like object. So
466   /// if the array's `ValueType` is `vtkm::Vec<vtkm::Float32, 3>`, then the base component
467   /// type will be `vtkm::Float32`. Likewise, if the `ValueType` is
468   /// `vtkm::Vec<vtkm::Vec<vtkm::Float32, 3>, 2>`, then the base component type is still
469   /// `vtkm::Float32`.
470   ///
471   /// If the `ValueType` is not `Vec`-like type, then the base component type is the same.
472   /// So a `ValueType` of `vtkm::Float32` has a base component type of `vtkm::Float32`.
473   ///
474   template <typename BaseComponentType>
475   VTKM_CONT bool IsBaseComponentType() const
476   {
477     return this->IsBaseComponentTypeImpl(detail::UnknownAHComponentInfo::Make<BaseComponentType>());
478   }
479 
480   /// Returns true if this array matches the ArrayHandleType template argument.
481   ///
482   template <typename ArrayHandleType>
483   VTKM_CONT bool IsType() const
484   {
485     VTKM_IS_ARRAY_HANDLE(ArrayHandleType);
486     return (this->IsValueType<typename ArrayHandleType::ValueType>() &&
487             this->IsStorageType<typename ArrayHandleType::StorageTag>());
488   }
489 
490   /// \brief Assigns potential value and storage types.
491   ///
492   /// Calling this method will return an `UncertainArrayHandle` with the provided
493   /// value and storage type lists. The returned object will hold the same
494   /// `ArrayHandle`, but `CastAndCall`s on the returned object will be constrained
495   /// to the given types.
496   ///
497   // Defined in UncertainArrayHandle.h
498   template <typename NewValueTypeList, typename NewStorageTypeList>
499   VTKM_CONT vtkm::cont::UncertainArrayHandle<NewValueTypeList, NewStorageTypeList> ResetTypes(
500     NewValueTypeList = NewValueTypeList{},
501     NewStorageTypeList = NewStorageTypeList{}) const;
502 
503   template <typename NewValueTypeList>
504   VTKM_DEPRECATED(1.6, "Specify both value types and storage types.")
505   VTKM_CONT
506     vtkm::cont::UncertainArrayHandle<NewValueTypeList, VTKM_DEFAULT_STORAGE_LIST> ResetTypes(
507       NewValueTypeList = NewValueTypeList{}) const
508   {
509     return this->ResetTypes<NewValueTypeList, VTKM_DEFAULT_STORAGE_LIST>();
510   }
511 
512   /// \brief Returns the number of values in the array.
513   ///
514   VTKM_CONT vtkm::Id GetNumberOfValues() const;
515 
516   /// \brief Returns the number of components for each value in the array.
517   ///
518   /// If the array holds `vtkm::Vec` objects, this will return the number of components
519   /// in each value. If the array holds a basic C type (such as `float`), this will return 1.
520   /// If the array holds `Vec`-like objects that have the number of components that can vary
521   /// at runtime, this method will return 0 (because there is no consistent answer).
522   ///
523   VTKM_CONT vtkm::IdComponent GetNumberOfComponents() const;
524 
525   /// \brief Returns the total number of components for each value in the array.
526   ///
527   /// If the array holds `vtkm::Vec` objects, this will return the total number of components
528   /// in each value assuming the object is flattened out to one level of `Vec` objects.
529   /// If the array holds a basic C type (such as `float`), this will return 1.
530   /// If the array holds a simple `Vec` (such as `vtkm::Vec3f`), this will return the number
531   /// of components (in this case 3).
532   /// If the array holds a hierarchy of `Vec`s (such as `vtkm::Vec<vtkm::Vec3f, 2>`), this will
533   /// return the total number of vecs (in this case 6).
534   /// If the array holds `Vec`-like objects that have the number of components that can vary
535   /// at runtime, this method will return 0 (because there is no consistent answer).
536   ///
537   VTKM_CONT vtkm::IdComponent GetNumberOfComponentsFlat() const;
538 
539   /// \brief Reallocate the data in the array.
540   ///
541   VTKM_CONT void Allocate(vtkm::Id numValues) const;
542 
543   /// \brief Determine if the contained array can be passed to the given array type.
544   ///
545   /// This method will return true if calling `AsArrayHandle` of the given type will
546   /// succeed. The result is similar to `IsType`, and if `IsType` returns true, then
547   /// this will return true. However, this method will also return true for other
548   /// types such as an `ArrayHandleMultiplexer` that can contain the array.
549   ///
550   template <typename ArrayHandleType>
551   VTKM_CONT bool CanConvert() const;
552 
553   // MSVC will issue deprecation warnings here if this template is instantiated with
554   // a deprecated class even if the template is used from a section of code where
555   // deprecation warnings are suppressed. This is annoying behavior since this template
556   // has no control over what class it is used with. To get around it, we have to
557   // suppress all deprecation warnings here.
558 #ifdef VTKM_MSVC
559   VTKM_DEPRECATED_SUPPRESS_BEGIN
560 #endif
561   ///@{
562   /// Returns this array cast appropriately and stored in the given `ArrayHandle` type.
563   /// Throws an `ErrorBadType` if the stored array cannot be stored in the given array type.
564   /// Use the `IsType` method to determine if the array can be returned with the given type.
565   ///
566   template <typename T, typename S>
567   VTKM_CONT void AsArrayHandle(vtkm::cont::ArrayHandle<T, S>& array) const
568   {
569     using ArrayType = vtkm::cont::ArrayHandle<T, S>;
570     if (!this->IsType<ArrayType>())
571     {
572       VTKM_LOG_CAST_FAIL(*this, decltype(array));
573       throwFailedDynamicCast(vtkm::cont::TypeToString(*this), vtkm::cont::TypeToString(array));
574     }
575 
576     array = *reinterpret_cast<ArrayType*>(this->Container->ArrayHandlePointer);
577   }
578 
579   template <typename T, typename... Ss>
580   VTKM_CONT void AsArrayHandle(
581     vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagMultiplexer<Ss...>>& array) const;
582 
583   template <typename TargetT, typename SourceT, typename SourceS>
584   VTKM_CONT void AsArrayHandle(
585     vtkm::cont::ArrayHandle<TargetT, vtkm::cont::StorageTagCast<SourceT, SourceS>>& array) const
586   {
587     using ContainedArrayType = vtkm::cont::ArrayHandle<SourceT, SourceS>;
588     array = vtkm::cont::ArrayHandleCast<TargetT, ContainedArrayType>(
589       this->AsArrayHandle<ContainedArrayType>());
590   }
591 
592   template <typename ArrayType>
593   VTKM_CONT ArrayType AsArrayHandle() const
594   {
595     VTKM_IS_ARRAY_HANDLE(ArrayType);
596     ArrayType array;
597     this->AsArrayHandle(array);
598     return array;
599   }
600   ///@}
601 #ifdef VTKM_MSVC
602   VTKM_DEPRECATED_SUPPRESS_END
603 #endif
604 
605   // For code still expecting a VariantArrayHandle
606   template <typename ArrayHandleType>
607   VTKM_DEPRECATED(1.6, "Use AsArrayHandle.")
608   VTKM_CONT ArrayHandleType Cast() const
609   {
610     return this->AsArrayHandle<ArrayHandleType>();
611   }
612   template <typename ArrayHandleType>
613   VTKM_DEPRECATED(1.6, "Use AsArrayHandle.")
614   VTKM_CONT void CopyTo(ArrayHandleType& array) const
615   {
616     this->AsArrayHandle(array);
617   }
618   template <typename MultiplexerType>
619   VTKM_DEPRECATED(1.6, "Use AsArrayHandle.")
620   VTKM_CONT MultiplexerType AsMultiplexer() const
621   {
622     return this->AsArrayHandle<MultiplexerType>();
623   }
624   template <typename MultiplexerType>
625   VTKM_DEPRECATED(1.6, "Use AsArrayHandle.")
626   VTKM_CONT void AsMultiplexer(MultiplexerType& result) const
627   {
628     result = this->AsArrayHandle<MultiplexerType>();
629   }
630 
631   /// \brief Extract a component of the array.
632   ///
633   /// This method returns an array that holds the data for a given flat component of the data.
634   /// The `BaseComponentType` has to be specified and must match the contained array (i.e.
635   /// the result of `IsBaseComponentType` must succeed for the given type).
636   ///
637   /// This method treats each value in the array as a flat `Vec` even if it is a `Vec` of
638   /// `Vec`s. For example, if the array actually holds values of type `Vec<Vec<T, 3>, 2>`,
639   /// it is treated as if it holds a `Vec<T, 6>`. See `vtkm::VecFlat` for details on how
640   /// vectors are flattened.
641   ///
642   /// The point of using `ExtractComponent` over `AsArrayHandle` is that it drastically reduces
643   /// the amount of types you have to try. Most of the type the base component type is one of
644   /// the basic C types (i.e. `int`, `long`, `float`, etc.). You do not need to know what shape
645   /// the containing `Vec` is in, nor do you need to know the actual storage of the array.
646   ///
647   /// Note that the type of the array returned is `ArrayHandleStride`. Using this type of
648   /// array handle has a slight overhead over basic arrays like `ArrayHandleBasic` and
649   /// `ArrayHandleSOA`.
650   ///
651   /// When extracting a component of an array, a shallow pointer to the data is returned
652   /// whenever possible. However, in some circumstances it is impossible to conform the
653   /// array. In these cases, the data are by default copied. If copying the data would
654   /// cause problems (for example, you are writing into the array), you can select the
655   /// optional `allowCopy` flag to `vtkm::CopyFlag::Off`. In this case, an exception
656   /// will be thrown if the result cannot be represented by a shallow copy.
657   ///
658   template <typename BaseComponentType>
659   VTKM_CONT vtkm::cont::ArrayHandleStride<BaseComponentType> ExtractComponent(
660     vtkm::IdComponent componentIndex,
661     vtkm::CopyFlag allowCopy = vtkm::CopyFlag::On) const
662   {
663     using ComponentArrayType = vtkm::cont::ArrayHandleStride<BaseComponentType>;
664     if (!this->IsBaseComponentType<BaseComponentType>())
665     {
666       VTKM_LOG_CAST_FAIL(*this, ComponentArrayType);
667       throwFailedDynamicCast(vtkm::cont::TypeToString(*this),
668                              "component array of " + vtkm::cont::TypeToString<BaseComponentType>());
669     }
670 
671     auto buffers = this->Container->ExtractComponent(
672       this->Container->ArrayHandlePointer, componentIndex, allowCopy);
673     return ComponentArrayType(buffers);
674   }
675 
676   /// \brief Extract the array knowing only the component type of the array.
677   ///
678   /// This method returns an `ArrayHandle` that points to the data in the array. This method
679   /// differs from `AsArrayHandle` because you do not need to know the exact `ValueType` and
680   /// `StorageTag` of the array. Instead, you only need to know the base component type.
681   ///
682   /// `ExtractArrayFromComponents` works by calling the `ExtractComponent` method and then
683   /// combining them together in a fancy `ArrayHandle`. This allows you to ignore the storage
684   /// type of the underlying array as well as any `Vec` structure of the value type. However,
685   /// it also places some limitations on how the data can be pulled from the data.
686   ///
687   /// First, you have to specify the base component type. This must match the data in the
688   /// underlying array (as reported by `IsBaseComponentType`).
689   ///
690   /// Second, the array returned will have the `Vec`s flattened. For example, if the underlying
691   /// array has a `ValueType` of `Vec<Vec<T, 3>, 3>`, then this method will tread the data as
692   /// if it was `Vec<T, 9>`. There is no way to get an array with `Vec` of `Vec` values.
693   ///
694   /// Third, because the `Vec` length of the values in the returned `ArrayHandle` must be
695   /// determined at runtime, that can break many assumptions of using `Vec` objects. The
696   /// type is not going to be a `Vec<T,N>` type but rather an internal class that is intended
697   /// to behave like that. The type should behave mostly like a `Vec`, but will have some
698   /// differences that can lead to unexpected behavior. For example, this `Vec`-like object
699   /// will not have a `NUM_COMPONENTS` constant static expression because it is not known
700   /// at compile time. (Use the `GetNumberOfComponents` method instead.) And for the same
701   /// reason you will not be able to pass these objects to classes overloaded or templated
702   /// on the `Vec` type. Also, these `Vec`-like objects cannot be created as new instances.
703   /// Thus, you will likely have to iterate over all components rather than do operations on
704   /// the whole `Vec`.
705   ///
706   /// Fourth, because `ExtractArrayFromComponents` uses `ExtractComponent` to pull data from
707   /// the array (which in turn uses `ArrayExtractComponent`), there are some `ArrayHandle` types
708   /// that will require copying data to a new array. This could be problematic in cases where
709   /// you want to write to the array. To prevent data from being copied, set the optional
710   ///  `allowCopy` to `vtkm::CopyFlag::Off`. This will cause an exception to be thrown if
711   /// the resulting array cannot reference the memory held in this `UnknownArrayHandle`.
712   ///
713   /// Fifth, component arrays are extracted using `ArrayHandleStride` as the representation
714   /// for each component. This array adds a slight overhead for each lookup as it performs the
715   /// arithmetic for finding the index of each component.
716   ///
717   template <typename BaseComponentType>
718   VTKM_CONT vtkm::cont::ArrayHandleRecombineVec<BaseComponentType> ExtractArrayFromComponents(
719     vtkm::CopyFlag allowCopy = vtkm::CopyFlag::On) const
720   {
721     vtkm::cont::ArrayHandleRecombineVec<BaseComponentType> result;
722     vtkm::IdComponent numComponents = this->GetNumberOfComponentsFlat();
723     for (vtkm::IdComponent cIndex = 0; cIndex < numComponents; ++cIndex)
724     {
725       result.AppendComponentArray(this->ExtractComponent<BaseComponentType>(cIndex, allowCopy));
726     }
727     return result;
728   }
729 
730   /// \brief Call a functor using the underlying array type.
731   ///
732   /// `CastAndCallForTypes` attempts to cast the held array to a specific value type,
733   /// and then calls the given functor with the cast array. You must specify
734   /// the `TypeList` and `StorageList` as template arguments.
735   ///
736   /// After the functor argument you may add any number of arguments that will be
737   /// passed to the functor after the converted `ArrayHandle`.
738   ///
739   template <typename TypeList, typename StorageList, typename Functor, typename... Args>
740   VTKM_CONT void CastAndCallForTypes(Functor&& functor, Args&&... args) const;
741 
742   /// \brief Call a functor on an array extracted from the components.
743   ///
744   /// `CastAndCallWithExtractedArray` behaves similarly to `CastAndCallForTypes`.
745   /// It converts the contained data to an `ArrayHandle` and calls a functor with
746   /// that `ArrayHandle` (and any number of optionally specified arguments).
747   ///
748   /// The advantage of `CastAndCallWithExtractedArray` is that you do not need to
749   /// specify any `TypeList` or `StorageList`. Instead, it internally uses
750   /// `ExtractArrayFromComponents` to work with most `ArrayHandle` types with only
751   /// about 10 instances of the functor. In contrast, calling `CastAndCallForTypes`
752   /// with, for example, `VTKM_DEFAULT_TYPE_LIST` and `VTKM_DEFAULT_STORAGE_LIST`
753   /// results in many more instances of the functor but handling many fewer types
754   /// of `ArrayHandle`.
755   ///
756   /// There are, however, costs to using this method. Details of these costs are
757   /// documented for the `ExtractArrayFromComponents` method, but briefly they
758   /// are that `Vec` types get flattened, the resulting array has a strange `Vec`-like
759   /// value type that has many limitations on its use, there is an overhead for
760   /// retrieving each value from the array, and there is a potential that data
761   /// must be copied.
762   ///
763   template <typename Functor, typename... Args>
764   VTKM_CONT void CastAndCallWithExtractedArray(Functor&& functor, Args&&... args) const;
765 
766   template <typename FunctorOrStorageList, typename... Args>
767   VTKM_CONT VTKM_DEPRECATED(1.6, "Use CastAndCallForTypes.") void CastAndCall(
768     FunctorOrStorageList&& functorOrStorageList,
769     Args&&... args) const
770   {
771     this->CastAndCallImpl(vtkm::internal::IsList<FunctorOrStorageList>{},
772                           std::forward<FunctorOrStorageList>(functorOrStorageList),
773                           std::forward<Args>(args)...);
774   }
775 
776   /// Releases any resources being used in the execution environment (that are
777   /// not being shared by the control environment).
778   ///
779   VTKM_CONT void ReleaseResourcesExecution() const;
780 
781   /// Releases all resources in both the control and execution environments.
782   ///
783   VTKM_CONT void ReleaseResources() const;
784 
785   VTKM_CONT void PrintSummary(std::ostream& out, bool full = false) const;
786 
787 private:
788   // Remove this when deprecated CastAndCall is removed.
789   template <typename... Args>
790   VTKM_CONT void CastAndCallImpl(std::false_type, Args&&... args) const
791   {
792     this->CastAndCallForTypes<VTKM_DEFAULT_TYPE_LIST, VTKM_DEFAULT_STORAGE_LIST>(
793       std::forward<Args>(args)...);
794   }
795   template <typename StorageList, typename... Args>
796   VTKM_CONT void CastAndCallImpl(std::true_type, StorageList, Args&&... args) const
797   {
798     this->CastAndCallForTypes<VTKM_DEFAULT_TYPE_LIST, StorageList>(std::forward<Args>(args)...);
799   }
800 };
801 
802 //=============================================================================
803 // Out of class implementations
804 
805 namespace detail
806 {
807 
808 template <typename T, typename S>
809 struct UnknownArrayHandleCanConvert
810 {
811   VTKM_CONT bool operator()(const vtkm::cont::UnknownArrayHandle& array) const
812   {
813     return array.IsType<vtkm::cont::ArrayHandle<T, S>>();
814   }
815 };
816 
817 template <typename TargetT, typename SourceT, typename SourceS>
818 struct UnknownArrayHandleCanConvert<TargetT, vtkm::cont::StorageTagCast<SourceT, SourceS>>
819 {
820   VTKM_CONT bool operator()(const vtkm::cont::UnknownArrayHandle& array) const
821   {
822     return UnknownArrayHandleCanConvert<SourceT, SourceS>{}(array);
823   }
824 };
825 
826 template <typename T>
827 struct UnknownArrayHandleCanConvertTry
828 {
829   template <typename S>
830   VTKM_CONT void operator()(S, const vtkm::cont::UnknownArrayHandle& array, bool& canConvert) const
831   {
832     canConvert |= UnknownArrayHandleCanConvert<T, S>{}(array);
833   }
834 };
835 
836 template <typename T, typename... Ss>
837 struct UnknownArrayHandleCanConvert<T, vtkm::cont::StorageTagMultiplexer<Ss...>>
838 {
839   VTKM_CONT bool operator()(const vtkm::cont::UnknownArrayHandle& array) const
840   {
841     bool canConvert = false;
842     vtkm::ListForEach(UnknownArrayHandleCanConvertTry<T>{}, vtkm::List<Ss...>{}, array, canConvert);
843     return canConvert;
844   }
845 };
846 
847 } // namespace detail
848 
849 template <typename ArrayHandleType>
850 VTKM_CONT bool UnknownArrayHandle::CanConvert() const
851 {
852   VTKM_IS_ARRAY_HANDLE(ArrayHandleType);
853 
854   return detail::UnknownArrayHandleCanConvert<typename ArrayHandleType::ValueType,
855                                               typename ArrayHandleType::StorageTag>{}(*this);
856 }
857 
858 namespace detail
859 {
860 
861 struct UnknownArrayHandleMultplexerCastTry
862 {
863   template <typename T, typename S, typename... Ss>
864   VTKM_CONT void operator()(
865     S,
866     const vtkm::cont::UnknownArrayHandle& unknownArray,
867     vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagMultiplexer<Ss...>>& outputArray,
868     bool& converted) const
869   {
870     using ArrayType = vtkm::cont::ArrayHandle<T, S>;
871     if (unknownArray.CanConvert<ArrayType>())
872     {
873       if (converted && !unknownArray.IsType<ArrayType>())
874       {
875         // The array has already been converted and pushed in the multiplexer. It is
876         // possible that multiple array types can be put in the ArrayHandleMultiplexer
877         // (for example, and ArrayHandle or an ArrayHandle that has been cast). Exact
878         // matches will override other matches (hence, the second part of the condition),
879         // but at this point we have already found a better array to put inside.
880         return;
881       }
882       outputArray = vtkm::cont::ArrayHandleMultiplexer<vtkm::cont::ArrayHandle<T, Ss>...>(
883         unknownArray.AsArrayHandle<ArrayType>());
884       converted = true;
885     }
886   }
887 };
888 
889 } // namespace detail
890 
891 template <typename T, typename... Ss>
892 void UnknownArrayHandle::AsArrayHandle(
893   vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagMultiplexer<Ss...>>& array) const
894 {
895   bool converted = false;
896   vtkm::ListForEach(
897     detail::UnknownArrayHandleMultplexerCastTry{}, vtkm::List<Ss...>{}, *this, array, converted);
898 
899   if (!converted)
900   {
901     VTKM_LOG_CAST_FAIL(*this, decltype(array));
902     throwFailedDynamicCast(vtkm::cont::TypeToString(*this), vtkm::cont::TypeToString(array));
903   }
904 }
905 
906 namespace detail
907 {
908 
909 struct UnknownArrayHandleTry
910 {
911   template <typename T, typename S, typename Functor, typename... Args>
912   void operator()(vtkm::List<T, S>,
913                   Functor&& f,
914                   bool& called,
915                   const vtkm::cont::UnknownArrayHandle& unknownArray,
916                   Args&&... args) const
917   {
918     using DerivedArrayType = vtkm::cont::ArrayHandle<T, S>;
919     if (!called && unknownArray.CanConvert<DerivedArrayType>())
920     {
921       called = true;
922       DerivedArrayType derivedArray;
923       unknownArray.AsArrayHandle(derivedArray);
924       VTKM_LOG_CAST_SUCC(unknownArray, derivedArray);
925 
926       // If you get a compile error here, it means that you have called CastAndCall for a
927       // vtkm::cont::UnknownArrayHandle and the arguments of the functor do not match those
928       // being passed. This is often because it is calling the functor with an ArrayHandle
929       // type that was not expected. Either add overloads to the functor to accept all
930       // possible array types or constrain the types tried for the CastAndCall. Note that
931       // the functor will be called with an array of type vtkm::cont::ArrayHandle<T, S>.
932       // Directly using a subclass of ArrayHandle (e.g. vtkm::cont::ArrayHandleConstant<T>)
933       // might not work.
934       f(derivedArray, std::forward<Args>(args)...);
935     }
936   }
937 };
938 
939 template <typename T>
940 struct IsUndefinedArrayType
941 {
942 };
943 template <typename T, typename S>
944 struct IsUndefinedArrayType<vtkm::List<T, S>> : vtkm::cont::internal::IsInvalidArrayHandle<T, S>
945 {
946 };
947 
948 template <typename ValueTypeList, typename StorageTypeList>
949 using ListAllArrayTypes =
950   vtkm::ListRemoveIf<vtkm::ListCross<ValueTypeList, StorageTypeList>, IsUndefinedArrayType>;
951 
952 
953 VTKM_CONT_EXPORT void ThrowCastAndCallException(const vtkm::cont::UnknownArrayHandle&,
954                                                 const std::type_info&);
955 
956 } // namespace detail
957 
958 template <typename TypeList, typename StorageTagList, typename Functor, typename... Args>
959 inline void UnknownArrayHandle::CastAndCallForTypes(Functor&& f, Args&&... args) const
960 {
961   using crossProduct = detail::ListAllArrayTypes<TypeList, StorageTagList>;
962 
963   bool called = false;
964   vtkm::ListForEach(detail::UnknownArrayHandleTry{},
965                     crossProduct{},
966                     std::forward<Functor>(f),
967                     called,
968                     *this,
969                     std::forward<Args>(args)...);
970   if (!called)
971   {
972     // throw an exception
973     VTKM_LOG_CAST_FAIL(*this, TypeList);
974     detail::ThrowCastAndCallException(*this, typeid(TypeList));
975   }
976 }
977 
978 
979 //=============================================================================
980 // Free function casting helpers
981 
982 /// Returns true if \c variant matches the type of ArrayHandleType.
983 ///
984 template <typename ArrayHandleType>
985 VTKM_CONT inline bool IsType(const vtkm::cont::UnknownArrayHandle& array)
986 {
987   return array.template IsType<ArrayHandleType>();
988 }
989 
990 /// Returns \c variant cast to the given \c ArrayHandle type. Throws \c
991 /// ErrorBadType if the cast does not work. Use \c IsType
992 /// to check if the cast can happen.
993 ///
994 template <typename ArrayHandleType>
995 VTKM_CONT inline ArrayHandleType Cast(const vtkm::cont::UnknownArrayHandle& array)
996 {
997   return array.template AsArrayHandle<ArrayHandleType>();
998 }
999 
1000 template <typename Functor, typename... Args>
1001 void CastAndCall(const UnknownArrayHandle& handle, Functor&& f, Args&&... args)
1002 {
1003   handle.CastAndCallForTypes<VTKM_DEFAULT_TYPE_LIST, VTKM_DEFAULT_STORAGE_LIST>(
1004     std::forward<Functor>(f), std::forward<Args>(args)...);
1005 }
1006 
1007 namespace detail
1008 {
1009 
1010 struct UnknownArrayHandleTryExtract
1011 {
1012   template <typename T, typename Functor, typename... Args>
1013   void operator()(T,
1014                   Functor&& f,
1015                   bool& called,
1016                   const vtkm::cont::UnknownArrayHandle& unknownArray,
1017                   Args&&... args) const
1018   {
1019     if (!called && unknownArray.IsBaseComponentType<T>())
1020     {
1021       called = true;
1022       auto extractedArray = unknownArray.ExtractArrayFromComponents<T>();
1023       VTKM_LOG_CAST_SUCC(unknownArray, extractedArray);
1024 
1025       // If you get a compile error here, it means that you have called
1026       // CastAndCallWithExtractedArray for a vtkm::cont::UnknownArrayHandle and the arguments of
1027       // the functor do not match those being passed. This is often because it is calling the
1028       // functor with an ArrayHandle type that was not expected. Add overloads to the functor to
1029       // accept all possible array types or constrain the types tried for the CastAndCall. Note
1030       // that the functor will be called with an array of type that is different than the actual
1031       // type of the `ArrayHandle` stored in the `UnknownArrayHandle`.
1032       f(extractedArray, std::forward<Args>(args)...);
1033     }
1034   }
1035 };
1036 
1037 } // namespace detail
1038 
1039 template <typename Functor, typename... Args>
1040 inline void UnknownArrayHandle::CastAndCallWithExtractedArray(Functor&& functor,
1041                                                               Args&&... args) const
1042 {
1043   bool called = false;
1044   vtkm::ListForEach(detail::UnknownArrayHandleTryExtract{},
1045                     vtkm::TypeListScalarAll{},
1046                     std::forward<Functor>(functor),
1047                     called,
1048                     *this,
1049                     std::forward<Args>(args)...);
1050   if (!called)
1051   {
1052     // Throw an exception.
1053     // The message will be a little wonky because the types are just the value types, not the
1054     // full type to cast to.
1055     VTKM_LOG_CAST_FAIL(*this, vtkm::TypeListScalarAll);
1056     detail::ThrowCastAndCallException(*this, typeid(vtkm::TypeListScalarAll));
1057   }
1058 }
1059 
1060 namespace internal
1061 {
1062 
1063 template <>
1064 struct DynamicTransformTraits<vtkm::cont::UnknownArrayHandle>
1065 {
1066   using DynamicTag = vtkm::cont::internal::DynamicTransformTagCastAndCall;
1067 };
1068 
1069 } // namespace internal
1070 
1071 }
1072 } // namespace vtkm::cont
1073 
1074 //=============================================================================
1075 // Specializations of serialization related classes
1076 /// @cond SERIALIZATION
1077 
1078 namespace vtkm
1079 {
1080 namespace cont
1081 {
1082 
1083 template <>
1084 struct VTKM_CONT_EXPORT SerializableTypeString<vtkm::cont::UnknownArrayHandle>
1085 {
1086   static VTKM_CONT std::string Get();
1087 };
1088 }
1089 } // namespace vtkm::cont
1090 
1091 namespace mangled_diy_namespace
1092 {
1093 
1094 template <>
1095 struct VTKM_CONT_EXPORT Serialization<vtkm::cont::UnknownArrayHandle>
1096 {
1097 public:
1098   static VTKM_CONT void save(BinaryBuffer& bb, const vtkm::cont::UnknownArrayHandle& obj);
1099   static VTKM_CONT void load(BinaryBuffer& bb, vtkm::cont::UnknownArrayHandle& obj);
1100 };
1101 
1102 } // namespace mangled_diy_namespace
1103 
1104 /// @endcond SERIALIZATION
1105 
1106 #endif //vtk_m_cont_UnknownArrayHandle_h
1107