vtk-m/vtkm/cont/ArrayHandleCast.h
2024-06-05 17:51:14 -04:00

331 lines
11 KiB
C++

//============================================================================
// 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 <vtkm/cont/ArrayHandleTransform.h>
#include <vtkm/cont/Logging.h>
#include <vtkm/Range.h>
#include <vtkm/VecTraits.h>
#include <limits>
namespace vtkm
{
namespace cont
{
template <typename SourceT, typename SourceStorage>
struct VTKM_ALWAYS_EXPORT StorageTagCast
{
};
namespace internal
{
template <typename FromType, typename ToType>
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<vtkm::Float32, 3>, 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<ToType>(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 <typename TargetT, typename SourceT, typename SourceStorage, bool... CastFlags>
struct ArrayHandleCastTraits;
template <typename TargetT, typename SourceT, typename SourceStorage>
struct ArrayHandleCastTraits<TargetT, SourceT, SourceStorage>
: ArrayHandleCastTraits<TargetT,
SourceT,
SourceStorage,
std::is_convertible<SourceT, TargetT>::value,
std::is_convertible<TargetT, SourceT>::value>
{
};
// Case where the forward cast is invalid, so this array is invalid.
template <typename TargetT, typename SourceT, typename SourceStorage, bool CanCastBackward>
struct ArrayHandleCastTraits<TargetT, SourceT, SourceStorage, false, CanCastBackward>
{
struct StorageSuperclass : vtkm::cont::internal::UndefinedStorage
{
using PortalType = vtkm::cont::internal::detail::UndefinedArrayPortal<TargetT>;
using PortalConstType = vtkm::cont::internal::detail::UndefinedArrayPortal<TargetT>;
};
};
// Case where the forward cast is valid but the backward cast is invalid.
template <typename TargetT, typename SourceT, typename SourceStorage>
struct ArrayHandleCastTraits<TargetT, SourceT, SourceStorage, true, false>
{
using StorageTagSuperclass = StorageTagTransform<vtkm::cont::ArrayHandle<SourceT, SourceStorage>,
vtkm::cont::internal::Cast<SourceT, TargetT>>;
using StorageSuperclass = vtkm::cont::internal::Storage<TargetT, StorageTagSuperclass>;
};
// Case where both forward and backward casts are valid.
template <typename TargetT, typename SourceT, typename SourceStorage>
struct ArrayHandleCastTraits<TargetT, SourceT, SourceStorage, true, true>
{
using StorageTagSuperclass = StorageTagTransform<vtkm::cont::ArrayHandle<SourceT, SourceStorage>,
vtkm::cont::internal::Cast<SourceT, TargetT>,
vtkm::cont::internal::Cast<TargetT, SourceT>>;
using StorageSuperclass = vtkm::cont::internal::Storage<TargetT, StorageTagSuperclass>;
};
} // namespace detail
template <typename TargetT, typename SourceT, typename SourceStorage_>
struct Storage<TargetT, vtkm::cont::StorageTagCast<SourceT, SourceStorage_>>
: detail::ArrayHandleCastTraits<TargetT, SourceT, SourceStorage_>::StorageSuperclass
{
using Superclass =
typename detail::ArrayHandleCastTraits<TargetT, SourceT, SourceStorage_>::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 <typename T, typename ArrayHandleType>
class ArrayHandleCast
: public vtkm::cont::ArrayHandle<
T,
StorageTagCast<typename ArrayHandleType::ValueType, typename ArrayHandleType::StorageTag>>
{
public:
VTKM_ARRAY_HANDLE_SUBCLASS(
ArrayHandleCast,
(ArrayHandleCast<T, ArrayHandleType>),
(vtkm::cont::ArrayHandle<
T,
StorageTagCast<typename ArrayHandleType::ValueType, typename ArrayHandleType::StorageTag>>));
/// Construct an `ArrayHandleCast` from a source array handle.
ArrayHandleCast(const vtkm::cont::ArrayHandle<typename ArrayHandleType::ValueType,
typename ArrayHandleType::StorageTag>& handle)
: Superclass(Superclass::StorageType::CreateBuffers(handle))
{
this->ValidateTypeCast<typename ArrayHandleType::ValueType>();
}
// 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 <typename SrcValueType>
VTKM_CONT static typename std::enable_if<!std::is_same<T, SrcValueType>::value>::type
ValidateTypeCast()
{
#ifdef VTKM_ENABLE_LOGGING
using DstValueType = T;
using SrcComp = typename vtkm::VecTraits<SrcValueType>::BaseComponentType;
using DstComp = typename vtkm::VecTraits<DstValueType>::BaseComponentType;
using SrcLimits = std::numeric_limits<SrcComp>;
using DstLimits = std::numeric_limits<DstComp>;
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<SrcValueType>().c_str(),
vtkm::cont::TypeToString<DstValueType>().c_str());
}
else if (RangeLoss)
{
VTKM_LOG_F(vtkm::cont::LogLevel::Warn,
"ArrayHandleCast: Casting ComponentType of "
"%s to %s reduces range.",
vtkm::cont::TypeToString<SrcValueType>().c_str(),
vtkm::cont::TypeToString<DstValueType>().c_str());
}
else if (PrecLoss)
{
VTKM_LOG_F(vtkm::cont::LogLevel::Warn,
"ArrayHandleCast: Casting ComponentType of "
"%s to %s reduces precision.",
vtkm::cont::TypeToString<SrcValueType>().c_str(),
vtkm::cont::TypeToString<DstValueType>().c_str());
}
#endif // Logging
}
template <typename SrcValueType>
VTKM_CONT static typename std::enable_if<std::is_same<T, SrcValueType>::value>::type
ValidateTypeCast()
{
//no-op if types match
}
};
namespace detail
{
template <typename CastType, typename OriginalType, typename ArrayType>
struct MakeArrayHandleCastImpl
{
using ReturnType = vtkm::cont::ArrayHandleCast<CastType, ArrayType>;
VTKM_CONT static ReturnType DoMake(const ArrayType& array) { return ReturnType(array); }
};
template <typename T, typename ArrayType>
struct MakeArrayHandleCastImpl<T, T, ArrayType>
{
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 <typename T, typename ArrayType>
VTKM_CONT
typename detail::MakeArrayHandleCastImpl<T, typename ArrayType::ValueType, ArrayType>::ReturnType
make_ArrayHandleCast(const ArrayType& array, const T& = T())
{
VTKM_IS_ARRAY_HANDLE(ArrayType);
using MakeImpl = detail::MakeArrayHandleCastImpl<T, typename ArrayType::ValueType, ArrayType>;
return MakeImpl::DoMake(array);
}
}
} // namespace vtkm::cont
//=============================================================================
// Specializations of serialization related classes
/// @cond SERIALIZATION
namespace vtkm
{
namespace cont
{
template <typename T, typename AH>
struct SerializableTypeString<vtkm::cont::ArrayHandleCast<T, AH>>
{
static VTKM_CONT const std::string& Get()
{
static std::string name =
"AH_Cast<" + SerializableTypeString<T>::Get() + "," + SerializableTypeString<AH>::Get() + ">";
return name;
}
};
template <typename T1, typename T2, typename S>
struct SerializableTypeString<vtkm::cont::ArrayHandle<T1, vtkm::cont::StorageTagCast<T2, S>>>
: SerializableTypeString<vtkm::cont::ArrayHandleCast<T1, vtkm::cont::ArrayHandle<T2, S>>>
{
};
}
} // namespace vtkm::cont
namespace mangled_diy_namespace
{
template <typename TargetT, typename SourceT, typename SourceStorage>
struct Serialization<
vtkm::cont::ArrayHandle<TargetT, vtkm::cont::StorageTagCast<SourceT, SourceStorage>>>
{
private:
using BaseType =
vtkm::cont::ArrayHandle<TargetT, vtkm::cont::StorageTagCast<SourceT, SourceStorage>>;
public:
static VTKM_CONT void save(BinaryBuffer& bb, const BaseType& obj)
{
vtkm::cont::ArrayHandleCast<TargetT, vtkm::cont::ArrayHandle<SourceT, SourceStorage>>
castArray = obj;
vtkmdiy::save(bb, castArray.GetSourceArray());
}
static VTKM_CONT void load(BinaryBuffer& bb, BaseType& obj)
{
vtkm::cont::ArrayHandle<SourceT, SourceStorage> array;
vtkmdiy::load(bb, array);
obj = vtkm::cont::make_ArrayHandleCast<TargetT>(array);
}
};
template <typename TargetT, typename AH>
struct Serialization<vtkm::cont::ArrayHandleCast<TargetT, AH>>
: Serialization<vtkm::cont::ArrayHandle<
TargetT,
vtkm::cont::StorageTagCast<typename AH::ValueType, typename AH::StorageTag>>>
{
};
} // diy
/// @endcond SERIALIZATION
#endif // vtk_m_cont_ArrayHandleCast_h