//============================================================================ // 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. //============================================================================ #ifndef vtk_m_cont_ArrayHandleCast_h #define vtk_m_cont_ArrayHandleCast_h #include #include #include #include #include namespace vtkm { namespace cont { template struct VTKM_ALWAYS_EXPORT StorageTagCast { }; namespace internal { template struct VTKM_ALWAYS_EXPORT Cast { // The following operator looks like it should never issue a cast warning because of // the static_cast (and we don't want it to issue a warning). However, if ToType is // an object that has a constructor that takes a value that FromType can be cast to, // that cast can cause a warning. For example, if FromType is vtkm::Float64 and ToType // is vtkm::Vec, the static_cast will first implicitly cast the // Float64 to a Float32 (which causes a warning) before using the static_cast to // construct the Vec with the Float64. The easiest way around the problem is to // just disable all conversion warnings here. (The pragmas are taken from those // used in Types.h for the VecBase class.) #if (!(defined(VTKM_CUDA) && (__CUDACC_VER_MAJOR__ < 8))) #if (defined(VTKM_GCC) || defined(VTKM_CLANG)) #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunknown-pragmas" #pragma GCC diagnostic ignored "-Wpragmas" #pragma GCC diagnostic ignored "-Wconversion" #pragma GCC diagnostic ignored "-Wfloat-conversion" #endif // gcc || clang #endif //not using cuda < 8 #if defined(VTKM_MSVC) #pragma warning(push) #pragma warning(disable : 4244) #endif VTKM_EXEC_CONT ToType operator()(const FromType& val) const { return static_cast(val); } #if (!(defined(VTKM_CUDA) && (__CUDACC_VER_MAJOR__ < 8))) #if (defined(VTKM_GCC) || defined(VTKM_CLANG)) #pragma GCC diagnostic pop #endif // gcc || clang #endif // not using cuda < 8 #if defined(VTKM_MSVC) #pragma warning(pop) #endif }; namespace detail { template struct ArrayHandleCastTraits; template struct ArrayHandleCastTraits : ArrayHandleCastTraits::value, std::is_convertible::value> { }; // Case where the forward cast is invalid, so this array is invalid. template struct ArrayHandleCastTraits { struct StorageSuperclass : vtkm::cont::internal::UndefinedStorage { using PortalType = vtkm::cont::internal::detail::UndefinedArrayPortal; using PortalConstType = vtkm::cont::internal::detail::UndefinedArrayPortal; }; }; // Case where the forward cast is valid but the backward cast is invalid. template struct ArrayHandleCastTraits { using StorageTagSuperclass = StorageTagTransform, vtkm::cont::internal::Cast>; using StorageSuperclass = vtkm::cont::internal::Storage; }; // Case where both forward and backward casts are valid. template struct ArrayHandleCastTraits { using StorageTagSuperclass = StorageTagTransform, vtkm::cont::internal::Cast, vtkm::cont::internal::Cast>; using StorageSuperclass = vtkm::cont::internal::Storage; }; } // namespace detail template struct Storage> : detail::ArrayHandleCastTraits::StorageSuperclass { using Superclass = typename detail::ArrayHandleCastTraits::StorageSuperclass; using Superclass::Superclass; }; } // namespace internal /// \brief Cast the values of an array to the specified type, on demand. /// /// ArrayHandleCast is a specialization of ArrayHandleTransform. Given an ArrayHandle /// and a type, it creates a new handle that returns the elements of the array cast /// to the specified type. /// template class ArrayHandleCast : public vtkm::cont::ArrayHandle< T, StorageTagCast> { public: VTKM_ARRAY_HANDLE_SUBCLASS( ArrayHandleCast, (ArrayHandleCast), (vtkm::cont::ArrayHandle< T, StorageTagCast>)); /// Construct an `ArrayHandleCast` from a source array handle. ArrayHandleCast(const vtkm::cont::ArrayHandle& handle) : Superclass(Superclass::StorageType::CreateBuffers(handle)) { this->ValidateTypeCast(); } // Implemented so that it is defined exclusively in the control environment. // If there is a separate device for the execution environment (for example, // with CUDA), then the automatically generated destructor could be // created for all devices, and it would not be valid for all devices. ~ArrayHandleCast() {} /// \brief Returns the `ArrayHandle` that is being transformed. ArrayHandleType GetSourceArray() const { return Superclass::StorageType::GetArray(this->GetBuffers()); } private: // Log warnings if type cast is valid but lossy: template VTKM_CONT static typename std::enable_if::value>::type ValidateTypeCast() { #ifdef VTKM_ENABLE_LOGGING using DstValueType = T; using SrcComp = typename vtkm::VecTraits::BaseComponentType; using DstComp = typename vtkm::VecTraits::BaseComponentType; using SrcLimits = std::numeric_limits; using DstLimits = std::numeric_limits; const vtkm::Range SrcRange{ SrcLimits::lowest(), SrcLimits::max() }; const vtkm::Range DstRange{ DstLimits::lowest(), DstLimits::max() }; const bool RangeLoss = (SrcRange.Max > DstRange.Max || SrcRange.Min < DstRange.Min); const bool PrecLoss = SrcLimits::digits > DstLimits::digits; if (RangeLoss && PrecLoss) { VTKM_LOG_F(vtkm::cont::LogLevel::Warn, "ArrayHandleCast: Casting ComponentType of " "%s to %s reduces range and precision.", vtkm::cont::TypeToString().c_str(), vtkm::cont::TypeToString().c_str()); } else if (RangeLoss) { VTKM_LOG_F(vtkm::cont::LogLevel::Warn, "ArrayHandleCast: Casting ComponentType of " "%s to %s reduces range.", vtkm::cont::TypeToString().c_str(), vtkm::cont::TypeToString().c_str()); } else if (PrecLoss) { VTKM_LOG_F(vtkm::cont::LogLevel::Warn, "ArrayHandleCast: Casting ComponentType of " "%s to %s reduces precision.", vtkm::cont::TypeToString().c_str(), vtkm::cont::TypeToString().c_str()); } #endif // Logging } template VTKM_CONT static typename std::enable_if::value>::type ValidateTypeCast() { //no-op if types match } }; namespace detail { template struct MakeArrayHandleCastImpl { using ReturnType = vtkm::cont::ArrayHandleCast; VTKM_CONT static ReturnType DoMake(const ArrayType& array) { return ReturnType(array); } }; template struct MakeArrayHandleCastImpl { using ReturnType = ArrayType; VTKM_CONT static ReturnType DoMake(const ArrayType& array) { return array; } }; } // namespace detail /// `make_ArrayHandleCast` is convenience function to generate an /// ArrayHandleCast. /// template VTKM_CONT typename detail::MakeArrayHandleCastImpl::ReturnType make_ArrayHandleCast(const ArrayType& array, const T& = T()) { VTKM_IS_ARRAY_HANDLE(ArrayType); using MakeImpl = detail::MakeArrayHandleCastImpl; return MakeImpl::DoMake(array); } } } // namespace vtkm::cont //============================================================================= // Specializations of serialization related classes /// @cond SERIALIZATION namespace vtkm { namespace cont { template struct SerializableTypeString> { static VTKM_CONT const std::string& Get() { static std::string name = "AH_Cast<" + SerializableTypeString::Get() + "," + SerializableTypeString::Get() + ">"; return name; } }; template struct SerializableTypeString>> : SerializableTypeString>> { }; } } // namespace vtkm::cont namespace mangled_diy_namespace { template struct Serialization< vtkm::cont::ArrayHandle>> { private: using BaseType = vtkm::cont::ArrayHandle>; public: static VTKM_CONT void save(BinaryBuffer& bb, const BaseType& obj) { vtkm::cont::ArrayHandleCast> castArray = obj; vtkmdiy::save(bb, castArray.GetSourceArray()); } static VTKM_CONT void load(BinaryBuffer& bb, BaseType& obj) { vtkm::cont::ArrayHandle array; vtkmdiy::load(bb, array); obj = vtkm::cont::make_ArrayHandleCast(array); } }; template struct Serialization> : Serialization>> { }; } // diy /// @endcond SERIALIZATION #endif // vtk_m_cont_ArrayHandleCast_h