//============================================================================ // 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 internal { /// Behaves like (and is interchangable with) a DynamicArrayHandle. The /// difference is that the list of types and list of containers 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 convienience 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 /// containers. 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 container matches one of the tags specified by \c /// VTKM_DEFAULT_CONTAINER_LIST_TAG. These lists can be changed by using the \c /// ResetTypeList and \c ResetContainerList 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 exponentally when using multiple \c DynamicArrayHandle /// objects. /// class DynamicArrayHandle { public: VTKM_CONT_EXPORT DynamicArrayHandle() { } template VTKM_CONT_EXPORT DynamicArrayHandle(const vtkm::cont::ArrayHandle &array) : ArrayContainer(new vtkm::cont::internal::SimplePolymorphicContainer< vtkm::cont::ArrayHandle >(array)) { } template VTKM_CONT_EXPORT DynamicArrayHandle( const internal::DynamicArrayHandleCast &dynamicArray) : ArrayContainer(dynamicArray.ArrayContainer) { } /// Returns true if this array is of the provided type and uses the provided /// container. /// template VTKM_CONT_EXPORT bool IsTypeAndContainer(Type = Type(), Container = Container()) const { return (this->TryCastArrayContainer() != NULL); } /// Returns this array cast to an ArrayHandle object of the given type and /// container. Throws ErrorControlBadValue if the cast does not work. Use /// IsTypeAndContainer to check if the cast can happen. /// template VTKM_CONT_EXPORT vtkm::cont::ArrayHandle CastToArrayHandle(Type = Type(), Container = Container()) const { vtkm::cont::internal::SimplePolymorphicContainer< vtkm::cont::ArrayHandle > *container = this->TryCastArrayContainer(); if (container == NULL) { throw vtkm::cont::ErrorControlBadValue("Bad cast of dynamic array."); } return container->Item; } /// 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 { return internal::DynamicArrayHandleCast< NewTypeList,VTKM_DEFAULT_CONTAINER_LIST_TAG>(*this); } /// Changes the array containers to try casting to when resolving this /// dynamic array, which is specified with a list tag like those in /// ContainerListTag.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 ResetContainerList(NewContainerList = NewContainerList()) const { return internal::DynamicArrayHandleCast< VTKM_DEFAULT_TYPE_LIST_TAG,NewContainerList>(*this); } /// Attempts to cast the held array to a specific value type and container, /// then call the given functor with the cast array. The types and containers /// tried in the cast are those in the lists defined by /// VTKM_DEFAULT_TYPE_LIST_TAG and VTKM_DEFAULT_CONTAINER_LIST_TAG, /// respectively, unless they have been changed with a previous call to /// ResetTypeList or ResetContainerList. /// template VTKM_CONT_EXPORT void CastAndCall(const Functor &f) const { this->CastAndCall( f, VTKM_DEFAULT_TYPE_LIST_TAG(), VTKM_DEFAULT_CONTAINER_LIST_TAG()); } /// A version of CastAndCall that tries specified lists of types and /// containers. /// template VTKM_CONT_EXPORT void CastAndCall(const Functor &f, TypeList, ContainerList) const; private: boost::shared_ptr ArrayContainer; template VTKM_CONT_EXPORT vtkm::cont::internal::SimplePolymorphicContainer< vtkm::cont::ArrayHandle > * TryCastArrayContainer() const { return dynamic_cast< vtkm::cont::internal::SimplePolymorphicContainer< vtkm::cont::ArrayHandle > *>( this->ArrayContainer.get()); } }; namespace detail { template struct DynamicArrayHandleTryContainer { const DynamicArrayHandle Array; const Functor &Function; bool FoundCast; VTKM_CONT_EXPORT DynamicArrayHandleTryContainer(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()(Container) { if (!this->FoundCast && this->Array.IsTypeAndContainer(Type(), Container())) { this->Function(this->Array.CastToArrayHandle(Type(), Container())); this->FoundCast = true; } } template VTKM_CONT_EXPORT typename boost::disable_if< typename vtkm::cont::internal::IsValidArrayHandle::type >::type operator()(Container) { // 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 DynamicArrayHandleTryContainer TryContainerType; TryContainerType tryContainer = TryContainerType(this->Array, this->Function); vtkm::ListForEach(tryContainer, ContainerList()); if (tryContainer.FoundCast) { this->FoundCast = true; } } }; } // namespace detail template VTKM_CONT_EXPORT void DynamicArrayHandle::CastAndCall(const Functor &f, TypeList, ContainerList) const { 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 { 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 { return DynamicArrayHandleCast(*this); } template VTKM_CONT_EXPORT internal::DynamicArrayHandleCast ResetContainerList(NewContainerList = NewContainerList()) const { return internal::DynamicArrayHandleCast(*this); } template VTKM_CONT_EXPORT void CastAndCall(const Functor &f) const { this->CastAndCall(f, TypeList(), ContainerList()); } template VTKM_CONT_EXPORT void CastAndCall(const Functor &f, TL, CL) const { this->DynamicArrayHandle::CastAndCall(f, TL(), CL()); } }; 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