//============================================================================ // Copyright (c) Kitware, Inc. // All rights reserved. // See LICENSE.txt for details. // This software is distributed WITHOUT ANY WARRANTY; without even // the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // PURPOSE. See the above copyright notice for more information. // // Copyright 2014 National Technology & Engineering Solutions of Sandia, LLC (NTESS). // Copyright 2014 UT-Battelle, LLC. // Copyright 2014 Los Alamos National Security. // // Under the terms of Contract DE-NA0003525 with NTESS, // the U.S. Government retains certain rights in this software. // // Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National // Laboratory (LANL), the U.S. Government retains certain rights in // this software. //============================================================================ #ifndef vtk_m_cont_DynamicArrayHandle_h #define vtk_m_cont_DynamicArrayHandle_h #include #include #include #include #include #include #include namespace vtkm { template struct ListCrossProduct; namespace cont { // Forward declaration template class DynamicArrayHandleBase; namespace detail { /// \brief Base class for PolymorphicArrayHandleContainer /// struct VTKM_CONT_EXPORT PolymorphicArrayHandleContainerBase { PolymorphicArrayHandleContainerBase(); // This must exist so that subclasses are destroyed correctly. virtual ~PolymorphicArrayHandleContainerBase(); virtual vtkm::IdComponent GetNumberOfComponents() const = 0; virtual vtkm::Id GetNumberOfValues() const = 0; virtual void PrintSummary(std::ostream& out) const = 0; virtual std::shared_ptr NewInstance() const = 0; }; /// \brief ArrayHandle container that can use C++ run-time type information. /// /// The \c PolymorphicArrayHandleContainer is similar to the /// \c SimplePolymorphicContainer in that it can contain an object of an /// unknown type. However, this class specifically holds ArrayHandle objects /// (with different template parameters) so that it can polymorphically answer /// simple questions about the object. /// template struct VTKM_ALWAYS_EXPORT PolymorphicArrayHandleContainer : public PolymorphicArrayHandleContainerBase { using ArrayHandleType = vtkm::cont::ArrayHandle; ArrayHandleType Array; VTKM_CONT PolymorphicArrayHandleContainer() : Array() { } VTKM_CONT PolymorphicArrayHandleContainer(const ArrayHandleType& array) : Array(array) { } virtual vtkm::IdComponent GetNumberOfComponents() const { return vtkm::VecTraits::NUM_COMPONENTS; } virtual vtkm::Id GetNumberOfValues() const { return this->Array.GetNumberOfValues(); } virtual void PrintSummary(std::ostream& out) const { vtkm::cont::printSummary_ArrayHandle(this->Array, out); } virtual std::shared_ptr NewInstance() const { return std::shared_ptr( new PolymorphicArrayHandleContainer()); } }; // One instance of a template class cannot access the private members of // another instance of a template class. However, I want to be able to copy // construct a DynamicArrayHandle from another DynamicArrayHandle of any other // type. Since you cannot partially specialize friendship, use this accessor // class to get at the internals for the copy constructor. struct DynamicArrayHandleCopyHelper { template VTKM_CONT static const std::shared_ptr& GetArrayHandleContainer(const vtkm::cont::DynamicArrayHandleBase& src) { return src.ArrayContainer; } }; // A simple function to downcast an ArrayHandle encapsulated in a // PolymorphicArrayHandleContainerBase to the given type of ArrayHandle. If the // conversion cannot be done, nullptr is returned. template VTKM_CONT vtkm::cont::ArrayHandle* DynamicArrayHandleTryCast( vtkm::cont::detail::PolymorphicArrayHandleContainerBase* arrayContainer) { vtkm::cont::detail::PolymorphicArrayHandleContainer* downcastContainer = nullptr; downcastContainer = dynamic_cast(arrayContainer); if (downcastContainer != nullptr) { return &downcastContainer->Array; } else { return nullptr; } } template VTKM_CONT vtkm::cont::ArrayHandle* DynamicArrayHandleTryCast( const std::shared_ptr& arrayContainer) { return detail::DynamicArrayHandleTryCast(arrayContainer.get()); } } // namespace detail /// \brief Holds an array handle without having to specify template parameters. /// /// \c DynamicArrayHandle holds an \c ArrayHandle object using runtime /// polymorphism to manage different value types and storage rather than /// compile-time templates. This adds a programming convenience that helps /// avoid a proliferation of templates. It also provides the management /// necessary to interface VTK-m with data sources where types will not be /// known until runtime. /// /// To interface between the runtime polymorphism and the templated algorithms /// in VTK-m, \c DynamicArrayHandle contains a method named \c CastAndCall that /// will determine the correct type from some known list of types and storage /// objects. This mechanism is used internally by VTK-m's worklet invocation /// mechanism to determine the type when running algorithms. /// /// By default, \c DynamicArrayHandle will assume that the value type in the /// array matches one of the types specified by \c VTKM_DEFAULT_TYPE_LIST_TAG /// and the storage matches one of the tags specified by \c /// VTKM_DEFAULT_STORAGE_LIST_TAG. These lists can be changed by using the \c /// ResetTypeList and \c ResetStorageList methods, respectively. It is /// worthwhile to match these lists closely to the possible types that might be /// used. If a type is missing you will get a runtime error. If there are more /// types than necessary, then the template mechanism will create a lot of /// object code that is never used, and keep in mind that the number of /// combinations grows exponentially when using multiple \c DynamicArrayHandle /// objects. /// /// The actual implementation of \c DynamicArrayHandle is in a templated class /// named \c DynamicArrayHandleBase, which is templated on the list of /// component types and storage types. \c DynamicArrayHandle is really just a /// typedef of \c DynamicArrayHandleBase with the default type and storage /// lists. /// template class VTKM_ALWAYS_EXPORT DynamicArrayHandleBase { public: VTKM_CONT DynamicArrayHandleBase() {} template VTKM_CONT DynamicArrayHandleBase(const vtkm::cont::ArrayHandle& array) : ArrayContainer(new vtkm::cont::detail::PolymorphicArrayHandleContainer(array)) { } VTKM_CONT DynamicArrayHandleBase(const DynamicArrayHandleBase& src) : ArrayContainer(src.ArrayContainer) { } template VTKM_CONT explicit DynamicArrayHandleBase( const DynamicArrayHandleBase& src) : ArrayContainer(detail::DynamicArrayHandleCopyHelper::GetArrayHandleContainer(src)) { } VTKM_CONT ~DynamicArrayHandleBase() {} VTKM_CONT vtkm::cont::DynamicArrayHandleBase& operator=( const vtkm::cont::DynamicArrayHandleBase& src) { this->ArrayContainer = src.ArrayContainer; return *this; } /// Returns true if this array is of the provided type and uses the provided /// storage. /// template VTKM_CONT bool IsTypeAndStorage() const { return (detail::DynamicArrayHandleTryCast(this->ArrayContainer) != nullptr); } /// Returns true if this array matches the array handle type passed in. /// template VTKM_CONT bool IsType() { VTKM_IS_ARRAY_HANDLE(ArrayHandleType); using ValueType = typename ArrayHandleType::ValueType; using StorageTag = typename ArrayHandleType::StorageTag; return this->IsTypeAndStorage(); } /// Returns true if the array held in this object is the same (or equivalent) /// type as the object given. /// template VTKM_CONT bool IsSameType(const ArrayHandleType&) { VTKM_IS_ARRAY_HANDLE(ArrayHandleType); return this->IsType(); } /// Returns this array cast to an ArrayHandle object of the given type and /// storage. Throws \c ErrorBadType if the cast does not work. Use /// \c IsTypeAndStorage to check if the cast can happen. /// /// template VTKM_CONT vtkm::cont::ArrayHandle CastToTypeStorage() const { vtkm::cont::ArrayHandle* downcastArray = detail::DynamicArrayHandleTryCast(this->ArrayContainer); if (downcastArray == nullptr) { throw vtkm::cont::ErrorBadType("Bad cast of dynamic array."); } // Technically, this method returns a copy of the \c ArrayHandle. But // because \c ArrayHandle acts like a shared pointer, it is valid to // do the copy. return *downcastArray; } /// Returns this array cast to the given \c ArrayHandle type. Throws \c /// ErrorBadType if the cast does not work. Use \c IsType /// to check if the cast can happen. /// template VTKM_CONT ArrayHandleType Cast() const { VTKM_IS_ARRAY_HANDLE(ArrayHandleType); using ValueType = typename ArrayHandleType::ValueType; using StorageTag = typename ArrayHandleType::StorageTag; // Technically, this method returns a copy of the \c ArrayHandle. But // because \c ArrayHandle acts like a shared pointer, it is valid to // do the copy. return this->CastToTypeStorage(); } /// Given a references to an ArrayHandle object, casts this array to the /// ArrayHandle's type and sets the given ArrayHandle to this array. Throws /// \c ErrorBadType if the cast does not work. Use \c /// ArrayHandleType to check if the cast can happen. /// /// Note that this is a shallow copy. The data are not copied and a change /// in the data in one array will be reflected in the other. /// template VTKM_CONT void CopyTo(ArrayHandleType& array) const { VTKM_IS_ARRAY_HANDLE(ArrayHandleType); array = this->Cast(); } /// Changes the types to try casting to when resolving this dynamic array, /// which is specified with a list tag like those in TypeListTag.h. Since C++ /// does not allow you to actually change the template arguments, this method /// returns a new dynamic array object. This method is particularly useful to /// narrow down (or expand) the types when using an array of particular /// constraints. /// template VTKM_CONT DynamicArrayHandleBase ResetTypeList( NewTypeList = NewTypeList()) const { VTKM_IS_LIST_TAG(NewTypeList); return DynamicArrayHandleBase(*this); } /// Changes the array storage types to try casting to when resolving this /// dynamic array, which is specified with a list tag like those in /// StorageListTag.h. Since C++ does not allow you to actually change the /// template arguments, this method returns a new dynamic array object. This /// method is particularly useful to narrow down (or expand) the types when /// using an array of particular constraints. /// template VTKM_CONT DynamicArrayHandleBase ResetStorageList( NewStorageList = NewStorageList()) const { VTKM_IS_LIST_TAG(NewStorageList); return DynamicArrayHandleBase(*this); } /// Changes the value, and array storage types to try casting to when /// resolving this dynamic array. Since C++ does not allow you to actually /// change the template arguments, this method returns a new dynamic array /// object. This method is particularly useful when you have both custom /// types and traits. /// template VTKM_CONT DynamicArrayHandleBase ResetTypeAndStorageLists( NewTypeList = NewTypeList(), NewStorageList = NewStorageList()) const { VTKM_IS_LIST_TAG(NewTypeList); VTKM_IS_LIST_TAG(NewStorageList); return DynamicArrayHandleBase(*this); } /// Attempts to cast the held array to a specific value type and storage, /// then call the given functor with the cast array. The types and storage /// tried in the cast are those in the lists defined by the TypeList and /// StorageList, respectively. The default \c DynamicArrayHandle sets these /// two lists to VTKM_DEFAULT_TYPE_LIST_TAG and VTK_DEFAULT_STORAGE_LIST_TAG, /// respectively. /// template VTKM_CONT void CastAndCall(Functor&& f, Args&&...) const; /// \brief Create a new array of the same type as this array. /// /// This method creates a new array that is the same type as this one and /// returns a new dynamic array handle for it. This method is convenient when /// creating output arrays that should be the same type as some input array. /// VTKM_CONT DynamicArrayHandleBase NewInstance() const { DynamicArrayHandleBase newArray; newArray.ArrayContainer = this->ArrayContainer->NewInstance(); return newArray; } /// \brief Get the number of components in each array value. /// /// This method will query the array type for the number of components in /// each value of the array. The number of components is determined by /// the \c VecTraits::NUM_COMPONENTS trait class. /// VTKM_CONT vtkm::IdComponent GetNumberOfComponents() const { return this->ArrayContainer->GetNumberOfComponents(); } /// \brief Get the number of values in the array. /// VTKM_CONT vtkm::Id GetNumberOfValues() const { return this->ArrayContainer->GetNumberOfValues(); } VTKM_CONT void PrintSummary(std::ostream& out) const { this->ArrayContainer->PrintSummary(out); } private: std::shared_ptr ArrayContainer; friend struct detail::DynamicArrayHandleCopyHelper; }; using DynamicArrayHandle = vtkm::cont::DynamicArrayHandleBase; namespace detail { struct DynamicArrayHandleTry { DynamicArrayHandleTry(const PolymorphicArrayHandleContainerBase* const c) : Container(c) { } template void operator()(brigand::list, Args&&... args) const { using storage = vtkm::cont::internal::Storage; using invalid = typename std::is_base_of::type; this->run(invalid{}, args...); } template void run(std::false_type, Functor&& f, bool& called, Args&&... args) const { if (!called) { using downcastType = const vtkm::cont::detail::PolymorphicArrayHandleContainer* const; downcastType downcastContainer = dynamic_cast(this->Container); if (downcastContainer) { f(downcastContainer->Array, std::forward(args)...); called = true; } } } template void run(std::true_type, Args&&...) const { } const PolymorphicArrayHandleContainerBase* const Container; }; VTKM_CONT_EXPORT void ThrowCastAndCallException(PolymorphicArrayHandleContainerBase*, const std::type_info*, const std::type_info*); } // namespace detail template template VTKM_CONT void DynamicArrayHandleBase::CastAndCall(Functor&& f, Args&&... args) const { //For optimizations we should compile once the cross product for the default types //and make it extern using crossProduct = typename vtkm::ListCrossProduct; bool called = false; auto* ptr = this->ArrayContainer.get(); vtkm::ListForEach(detail::DynamicArrayHandleTry(ptr), crossProduct{}, std::forward(f), called, std::forward(args)...); if (!called) { // throw an exception detail::ThrowCastAndCallException(ptr, &typeid(TypeList), &typeid(StorageList)); } } namespace internal { template struct DynamicTransformTraits> { using DynamicTag = vtkm::cont::internal::DynamicTransformTagCastAndCall; }; } // namespace internal } } // namespace vtkm::cont #endif //vtk_m_cont_DynamicArrayHandle_h