//============================================================================ // 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 Sandia Corporation. // Copyright 2014 UT-Battelle, LLC. // Copyright 2014 Los Alamos National Security. // // Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, // 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 #include namespace vtkm { namespace cont { namespace detail { /// \brief Base class for PolymorphicArrayHandleContainer /// struct 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 boost::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 /// unkown 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 PolymorphicArrayHandleContainer : public PolymorphicArrayHandleContainerBase { typedef vtkm::cont::ArrayHandle ArrayHandleType; ArrayHandleType Array; VTKM_CONT_EXPORT PolymorphicArrayHandleContainer() : Array() { } VTKM_CONT_EXPORT 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 boost::shared_ptr NewInstance() const { return boost::shared_ptr( new PolymorphicArrayHandleContainer()); } }; } // namespace detail namespace internal { /// Behaves like (and is interchangable with) a DynamicArrayHandle. The /// difference is that the list of types and list of storage to try when /// calling CastAndCall is set to the class template arguments. /// template class DynamicArrayHandleCast; } // namespace internal /// \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. /// class DynamicArrayHandle { public: VTKM_CONT_EXPORT DynamicArrayHandle() { } template VTKM_CONT_EXPORT DynamicArrayHandle(const vtkm::cont::ArrayHandle &array) : ArrayStorage(new vtkm::cont::detail::PolymorphicArrayHandleContainer< Type,Storage>(array)) { } template VTKM_CONT_EXPORT DynamicArrayHandle( const internal::DynamicArrayHandleCast &dynamicArray) : ArrayStorage(dynamicArray.ArrayStorage) { } /// Returns true if this array is of the provided type and uses the provided /// storage. /// template VTKM_CONT_EXPORT bool IsTypeAndStorage(Type = Type(), Storage = Storage()) const { return (this->TryCastStorage() != NULL); } /// Returns this array cast to an ArrayHandle object of the given type and /// storage. Throws ErrorControlBadValue if the cast does not work. Use /// IsTypeAndStorage to check if the cast can happen. /// template VTKM_CONT_EXPORT vtkm::cont::ArrayHandle CastToArrayHandle(Type = Type(), Storage = Storage()) const { vtkm::cont::detail::PolymorphicArrayHandleContainer *container = this->TryCastStorage(); if (container == NULL) { throw vtkm::cont::ErrorControlBadValue("Bad cast of dynamic array."); } return container->Array; } /// 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_EXPORT internal::DynamicArrayHandleCast ResetTypeList(NewTypeList = NewTypeList()) const { VTKM_IS_LIST_TAG(NewTypeList); return internal::DynamicArrayHandleCast< NewTypeList,VTKM_DEFAULT_STORAGE_LIST_TAG>(*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_EXPORT internal::DynamicArrayHandleCast ResetStorageList(NewStorageList = NewStorageList()) const { VTKM_IS_LIST_TAG(NewStorageList); return internal::DynamicArrayHandleCast< VTKM_DEFAULT_TYPE_LIST_TAG,NewStorageList>(*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 /// VTKM_DEFAULT_TYPE_LIST_TAG and VTK_DEFAULT_STORAGE_LIST_TAG, /// respectively, unless they have been changed with a previous call to /// ResetTypeList or ResetStorageList. /// template VTKM_CONT_EXPORT void CastAndCall(const Functor &f) const { this->CastAndCall( f, VTKM_DEFAULT_TYPE_LIST_TAG(), VTKM_DEFAULT_STORAGE_LIST_TAG()); } /// A version of CastAndCall that tries specified lists of types and /// storage types. /// template VTKM_CONT_EXPORT void CastAndCall(const Functor &f, TypeList, StorageList) 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_EXPORT DynamicArrayHandle NewInstance() const { DynamicArrayHandle newArray; newArray.ArrayStorage = this->ArrayStorage->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_EXPORT vtkm::IdComponent GetNumberOfComponents() const { return this->ArrayStorage->GetNumberOfComponents(); } /// \brief Get the number of values in the array. /// VTKM_CONT_EXPORT vtkm::Id GetNumberOfValues() const { return this->ArrayStorage->GetNumberOfValues(); } private: boost::shared_ptr ArrayStorage; template VTKM_CONT_EXPORT vtkm::cont::detail::PolymorphicArrayHandleContainer * TryCastStorage() const { return dynamic_cast< vtkm::cont::detail::PolymorphicArrayHandleContainer *>( this->ArrayStorage.get()); } }; namespace detail { template struct DynamicArrayHandleTryStorage { const DynamicArrayHandle Array; const Functor &Function; bool FoundCast; VTKM_CONT_EXPORT DynamicArrayHandleTryStorage(const DynamicArrayHandle &array, const Functor &f) : Array(array), Function(f), FoundCast(false) { } template VTKM_CONT_EXPORT typename boost::enable_if< typename vtkm::cont::internal::IsValidArrayHandle::type >::type operator()(Storage) { if (!this->FoundCast && this->Array.IsTypeAndStorage(Type(), Storage())) { this->Function(this->Array.CastToArrayHandle(Type(), Storage())); this->FoundCast = true; } } template VTKM_CONT_EXPORT typename boost::disable_if< typename vtkm::cont::internal::IsValidArrayHandle::type >::type operator()(Storage) { // This type of array handle cannot exist, so do nothing. } }; template struct DynamicArrayHandleTryType { const DynamicArrayHandle Array; const Functor &Function; bool FoundCast; VTKM_CONT_EXPORT DynamicArrayHandleTryType(const DynamicArrayHandle &array, const Functor &f) : Array(array), Function(f), FoundCast(false) { } template VTKM_CONT_EXPORT void operator()(Type) { if (this->FoundCast) { return; } typedef DynamicArrayHandleTryStorage TryStorageType; TryStorageType tryStorage = TryStorageType(this->Array, this->Function); vtkm::ListForEach(tryStorage, StorageList()); if (tryStorage.FoundCast) { this->FoundCast = true; } } }; } // namespace detail template VTKM_CONT_EXPORT void DynamicArrayHandle::CastAndCall(const Functor &f, TypeList, StorageList) const { VTKM_IS_LIST_TAG(TypeList); VTKM_IS_LIST_TAG(StorageList); typedef detail::DynamicArrayHandleTryType TryTypeType; TryTypeType tryType = TryTypeType(*this, f); vtkm::ListForEach(tryType, TypeList()); if (!tryType.FoundCast) { throw vtkm::cont::ErrorControlBadValue( "Could not find appropriate cast for array in CastAndCall."); } } namespace internal { template class DynamicArrayHandleCast : public vtkm::cont::DynamicArrayHandle { VTKM_IS_LIST_TAG(TypeList); VTKM_IS_LIST_TAG(StorageList); public: VTKM_CONT_EXPORT DynamicArrayHandleCast() : DynamicArrayHandle() { } VTKM_CONT_EXPORT DynamicArrayHandleCast(const vtkm::cont::DynamicArrayHandle &array) : DynamicArrayHandle(array) { } template VTKM_CONT_EXPORT DynamicArrayHandleCast( const DynamicArrayHandleCast &array) : DynamicArrayHandle(array) { } template VTKM_CONT_EXPORT DynamicArrayHandleCast ResetTypeList(NewTypeList = NewTypeList()) const { VTKM_IS_LIST_TAG(NewTypeList); return DynamicArrayHandleCast(*this); } template VTKM_CONT_EXPORT internal::DynamicArrayHandleCast ResetStorageList(NewStorageList = NewStorageList()) const { VTKM_IS_LIST_TAG(NewStorageList); return internal::DynamicArrayHandleCast(*this); } template VTKM_CONT_EXPORT void CastAndCall(const Functor &f) const { this->CastAndCall(f, TypeList(), StorageList()); } template VTKM_CONT_EXPORT void CastAndCall(const Functor &f, TL, CL) const { this->DynamicArrayHandle::CastAndCall(f, TL(), CL()); } VTKM_CONT_EXPORT DynamicArrayHandleCast NewInstance() const { return DynamicArrayHandleCast( this->DynamicArrayHandle::NewInstance()); } }; template<> struct DynamicTransformTraits { typedef vtkm::cont::internal::DynamicTransformTagCastAndCall DynamicTag; }; template struct DynamicTransformTraits< vtkm::cont::internal::DynamicArrayHandleCast > { typedef vtkm::cont::internal::DynamicTransformTagCastAndCall DynamicTag; }; } // namespace internal } } // namespace vtkm::cont #endif //vtk_m_cont_DynamicArrayHandle_h