From e47cedd972c1781d59237f617751debee2b084e8 Mon Sep 17 00:00:00 2001 From: Kenneth Moreland Date: Tue, 4 Aug 2020 21:16:29 -0600 Subject: [PATCH] Add UnknownArrayHandle --- vtkm/cont/ArrayHandleMultiplexer.h | 8 +- vtkm/cont/CMakeLists.txt | 2 + vtkm/cont/CastAndCall.h | 13 +- vtkm/cont/UnknownArrayHandle.cxx | 71 +++ vtkm/cont/UnknownArrayHandle.h | 540 ++++++++++++++++++ vtkm/cont/testing/CMakeLists.txt | 1 + .../testing/UnitTestUnknownArrayHandle.cxx | 398 +++++++++++++ 7 files changed, 1029 insertions(+), 4 deletions(-) create mode 100644 vtkm/cont/UnknownArrayHandle.cxx create mode 100644 vtkm/cont/UnknownArrayHandle.h create mode 100644 vtkm/cont/testing/UnitTestUnknownArrayHandle.cxx diff --git a/vtkm/cont/ArrayHandleMultiplexer.h b/vtkm/cont/ArrayHandleMultiplexer.h index 23955f7c9..ef2d4d634 100644 --- a/vtkm/cont/ArrayHandleMultiplexer.h +++ b/vtkm/cont/ArrayHandleMultiplexer.h @@ -176,9 +176,9 @@ struct MultiplexerShrinkFunctor struct MultiplexerReleaseResourcesFunctor { template - VTKM_CONT vtkm::Id operator()(ArrayHandleType&& array) const + VTKM_CONT void operator()(ArrayHandleType&& array) const { - return array.ReleaseResources(); + array.ReleaseResources(); } }; @@ -334,6 +334,10 @@ public: } VTKM_CONT ArrayHandleVariantType& GetArrayHandleVariant() { return this->ArrayHandleVariant; } + VTKM_CONT const ArrayHandleVariantType& GetArrayHandleVariant() const + { + return this->ArrayHandleVariant; + } }; diff --git a/vtkm/cont/CMakeLists.txt b/vtkm/cont/CMakeLists.txt index 20eaf866e..3c6f852ef 100644 --- a/vtkm/cont/CMakeLists.txt +++ b/vtkm/cont/CMakeLists.txt @@ -116,6 +116,7 @@ set(headers Token.h TryExecute.h SerializableTypeString.h + UnknownArrayHandle.h VariantArrayHandle.h VirtualObjectHandle.h ) @@ -191,6 +192,7 @@ set(sources RuntimeDeviceInformation.cxx StorageVirtual.cxx Timer.cxx + UnknownArrayHandle.cxx ) #----------------------------------------------------------------------------- diff --git a/vtkm/cont/CastAndCall.h b/vtkm/cont/CastAndCall.h index 4dda44264..4632fe5b6 100644 --- a/vtkm/cont/CastAndCall.h +++ b/vtkm/cont/CastAndCall.h @@ -19,11 +19,13 @@ namespace vtkm namespace cont { +class CoordinateSystem; +class Field; + template class ArrayHandle; -class CoordinateSystem; -class Field; +class UnknownArrayHandle; template class CellSetStructured; @@ -65,6 +67,13 @@ void CastAndCall(const vtkm::cont::ArrayHandle& handle, Functor&& f, Args& f(handle, std::forward(args)...); } +/// A specialization of CastAndCall for UnknownArrayHandle. +/// Since we have no hints on the types, use VTKM_DEFAULT_TYPE_LIST +/// and VTKM_DEFAULT_STORAGE_LIST. +// actually implemented in vtkm/cont/UnknownArrayHandle +template +void CastAndCall(const UnknownArrayHandle& handle, Functor&& f, Args&&... args); + /// A specialization of CastAndCall for basic CellSetStructured types, /// Since the type is already known no deduction is needed. /// This specialization is used to simplify numerous worklet algorithms diff --git a/vtkm/cont/UnknownArrayHandle.cxx b/vtkm/cont/UnknownArrayHandle.cxx new file mode 100644 index 000000000..63636eb94 --- /dev/null +++ b/vtkm/cont/UnknownArrayHandle.cxx @@ -0,0 +1,71 @@ +//============================================================================ +// 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. +//============================================================================ + +#include + +namespace vtkm +{ +namespace cont +{ + +namespace detail +{ + +std::shared_ptr UnknownAHContainer::MakeNewInstance() const +{ + // Start by doing an invalid copy to create a new container, then swap out the pointer + // to the array handle to make sure that each object will delete its own ArrayHandle + // when they get destroyed. + std::shared_ptr newContainer(new UnknownAHContainer(*this)); + newContainer->ArrayHandlePointer = this->NewInstance(); + return newContainer; +} + +} // namespace detail + +VTKM_CONT bool UnknownArrayHandle::IsValueTypeImpl(std::type_index type) const +{ + if (!this->Container) + { + return false; + } + + // Needs optimization based on platform. OSX cannot compare typeid across translation units? + return this->Container->ValueType == type; +} + +VTKM_CONT bool UnknownArrayHandle::IsStorageTypeImpl(std::type_index type) const +{ + if (!this->Container) + { + return false; + } + + // Needs optimization based on platform. OSX cannot compare typeid across translation units? + return this->Container->StorageType == type; +} + +namespace detail +{ + +VTKM_CONT_EXPORT void ThrowCastAndCallException(const vtkm::cont::UnknownArrayHandle& ref, + const std::type_info& type) +{ + std::ostringstream out; + out << "Could not find appropriate cast for array in CastAndCall.\n" + "Array: "; + ref.PrintSummary(out); + out << "TypeList: " << type.name() << "\n"; + throw vtkm::cont::ErrorBadValue(out.str()); +} + +} // namespace detail +} +} // namespace vtkm::cont diff --git a/vtkm/cont/UnknownArrayHandle.h b/vtkm/cont/UnknownArrayHandle.h new file mode 100644 index 000000000..7d4fcefa4 --- /dev/null +++ b/vtkm/cont/UnknownArrayHandle.h @@ -0,0 +1,540 @@ +//============================================================================ +// 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_UnknownArrayHandle_h +#define vtk_m_cont_UnknownArrayHandle_h + +#include + +#include +#include +#include + +#include + +#include +#include + +namespace vtkm +{ +namespace cont +{ + +namespace detail +{ + +template +static void UnknownAHDelete(void* mem) +{ + using AH = vtkm::cont::ArrayHandle; + AH* arrayHandle = reinterpret_cast(mem); + delete arrayHandle; +} + +template +static void* UnknownADNewInstance() +{ + return new vtkm::cont::ArrayHandle; +} + +template +static vtkm::Id UnknownAHNumberOfValues(void* mem) +{ + using AH = vtkm::cont::ArrayHandle; + AH* arrayHandle = reinterpret_cast(mem); + return arrayHandle->GetNumberOfValues(); +} + +template +static vtkm::IdComponent UnknownAHNumberOfComponents() +{ + return vtkm::VecTraits::NUM_COMPONENTS; +} + +template +static void UnknownAHReleaseResources(void* mem) +{ + using AH = vtkm::cont::ArrayHandle; + AH* arrayHandle = reinterpret_cast(mem); + arrayHandle->ReleaseResources(); +} + +template +static void UnknownAHReleaseResourcesExecution(void* mem) +{ + using AH = vtkm::cont::ArrayHandle; + AH* arrayHandle = reinterpret_cast(mem); + arrayHandle->ReleaseResourcesExecution(); +} + +template +static void UnknownAHPrintSummary(void* mem, std::ostream& out, bool full) +{ + using AH = vtkm::cont::ArrayHandle; + AH* arrayHandle = reinterpret_cast(mem); + vtkm::cont::printSummary_ArrayHandle(*arrayHandle, out, full); +} + +struct VTKM_CONT_EXPORT UnknownAHContainer; + +struct MakeUnknownAHContainerFunctor +{ + template + std::shared_ptr operator()(const vtkm::cont::ArrayHandle& array) const; +}; + +struct VTKM_CONT_EXPORT UnknownAHContainer +{ + void* ArrayHandlePointer; + + std::type_index ValueType; + std::type_index StorageType; + + using DeleteType = void(void*); + DeleteType* DeleteFunction; + + using NewInstanceType = void*(); + NewInstanceType& NewInstance; + + using NumberOfValuesType = vtkm::Id(void*); + NumberOfValuesType* NumberOfValues; + + using NumberOfComponentsType = vtkm::IdComponent(); + NumberOfComponentsType* NumberOfComponents; + + using ReleaseResourcesType = void(void*); + ReleaseResourcesType* ReleaseResources; + ReleaseResourcesType* ReleaseResourcesExecution; + + using PrintSummaryType = void(void*, std::ostream&, bool); + PrintSummaryType* PrintSummary; + + void operator=(const UnknownAHContainer&) = delete; + + ~UnknownAHContainer() { this->DeleteFunction(this->ArrayHandlePointer); } + + std::shared_ptr MakeNewInstance() const; + + template + static std::shared_ptr Make(const vtkm::cont::ArrayHandle& array) + { + return std::shared_ptr(new UnknownAHContainer(array)); + } + + template + static std::shared_ptr Make( + const vtkm::cont::ArrayHandle>& array) + { + return Make(array.GetStorage().GetArray()); + } + + template + static std::shared_ptr Make( + const vtkm::cont::ArrayHandle>& array) + { + auto&& variant = array.GetStorage().GetArrayHandleVariant(); + if (variant.IsValid()) + { + return variant.CastAndCall(MakeUnknownAHContainerFunctor{}); + } + else + { + return std::shared_ptr{}; + } + } + +private: + UnknownAHContainer(const UnknownAHContainer&) = default; + + template + explicit UnknownAHContainer(const vtkm::cont::ArrayHandle& array) + : ArrayHandlePointer(new vtkm::cont::ArrayHandle(array)) + , ValueType(typeid(T)) + , StorageType(typeid(S)) + , DeleteFunction(detail::UnknownAHDelete) + , NewInstance(detail::UnknownADNewInstance) + , NumberOfValues(detail::UnknownAHNumberOfValues) + , NumberOfComponents(detail::UnknownAHNumberOfComponents) + , ReleaseResources(detail::UnknownAHReleaseResources) + , ReleaseResourcesExecution(detail::UnknownAHReleaseResourcesExecution) + , PrintSummary(detail::UnknownAHPrintSummary) + { + } +}; + +template +inline std::shared_ptr MakeUnknownAHContainerFunctor::operator()( + const vtkm::cont::ArrayHandle& array) const +{ + return UnknownAHContainer::Make(array); +}; + +} // namespace detail + +/// \brief An ArrayHandle of an unknown type and storage. +/// +/// `UnknownArrayHandle` holds an `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 and is the storage +/// mechanism for classes like `DataSet` and `Field` that can hold numerous +/// types. +/// +/// To interface between the runtime polymorphism and the templated algorithms +/// in VTK-m, `UnknownArrayHandle` contains a method named `CastAndCallForTypes` +/// that determines the correct type from some known list of value types and +/// storage. This mechanism is used internally by VTK-m's worklet invocation +/// mechanism to determine the type when running algorithms. +/// +/// If the `UnknownArrayHandle` is used in a context where the possible array +/// types can be whittled down to a finite list (or you have to), you can +/// specify lists of value types and storage using the `ResetTypesAndStorage` +/// method. This will convert this object to an `UncertainArrayHandle` of the +/// given types. In cases where a finite set of types need to specified but +/// there is no known subset, `VTKM_DEFAULT_TYPE_LIST` and +/// `VTKM_DEFAULT_STORAGE_LIST` can be used. +/// +/// `ArrayHandleCast` and `ArrayHandleMultiplexer` are treated special. If +/// the `UnknownArrayHandle` is set to an `ArrayHandle` of one of these +/// types, it will actually store the `ArrayHandle` contained. Likewise, +/// if the `ArrayHandle` is retrieved as one of these types, it will +/// automatically convert it if possible. +/// +class VTKM_CONT_EXPORT UnknownArrayHandle +{ + std::shared_ptr Container; + + VTKM_CONT bool IsValueTypeImpl(std::type_index type) const; + VTKM_CONT bool IsStorageTypeImpl(std::type_index type) const; + +public: + VTKM_CONT UnknownArrayHandle() = default; + + template + VTKM_CONT UnknownArrayHandle(const vtkm::cont::ArrayHandle& array) + : Container(detail::UnknownAHContainer::Make(array)) + { + } + + /// \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 variant array handle for it. This method is convenient when + /// creating output arrays that should be the same type as some input array. + /// + VTKM_CONT UnknownArrayHandle NewInstance() const + { + UnknownArrayHandle newArray; + newArray.Container = this->Container->MakeNewInstance(); + return newArray; + } + + /// Returns true if this array matches the ValueType template argument. + /// + template + VTKM_CONT bool IsValueType() const + { + return this->IsValueTypeImpl(typeid(ValueType)); + } + + /// Returns true if this array matches the StorageType template argument. + /// + template + VTKM_CONT bool IsStorageType() const + { + return this->IsStorageTypeImpl(typeid(StorageType)); + } + + /// Returns true if this array matches the ArrayHandleType template argument. + /// + template + VTKM_CONT bool IsType() const + { + VTKM_IS_ARRAY_HANDLE(ArrayHandleType); + return (this->IsValueType() && + this->IsStorageType()); + } + + /// \brief Returns the number of values in the array. + /// + VTKM_CONT vtkm::Id GetNumberOfValues() const + { + return this->Container->NumberOfValues(this->Container->ArrayHandlePointer); + } + + /// \brief Returns the number of components for each value in the array. + /// + /// If the array holds `vtkm::Vec` objects, this will return the number of components + /// in each value. If the array holds a basic C type (such as `float`), this will return 1. + /// + VTKM_CONT vtkm::IdComponent GetNumberOfComponents() const + { + return this->Container->NumberOfComponents(); + } + + /// \brief Determine if the contained array can be passed to the given array type. + /// + /// This method will return true if calling `AsArrayHandle` of the given type will + /// succeed. The result is similar to `IsType`, and if `IsType` returns true, then + /// this will return true. However, this method will also return true for other + /// types such as an `ArrayHandleMultiplexer` that can contain the array. + /// + template + VTKM_CONT bool CanConvert() const; + + ///@{ + /// Returns this array cast appropriately and stored in the given `ArrayHandle` type. + /// Throws an `ErrorBadType` if the stored array cannot be stored in the given array type. + /// Use the `IsType` method to determine if the array can be returned with the given type. + /// + template + VTKM_CONT void AsArrayHandle(vtkm::cont::ArrayHandle& array) const + { + using ArrayType = vtkm::cont::ArrayHandle; + if (!this->IsType()) + { + VTKM_LOG_CAST_FAIL(*this, decltype(array)); + throwFailedDynamicCast(vtkm::cont::TypeToString(*this), vtkm::cont::TypeToString(array)); + } + + array = *reinterpret_cast(this->Container->ArrayHandlePointer); + } + + template + VTKM_CONT void AsArrayHandle( + vtkm::cont::ArrayHandle>& array) const; + + template + VTKM_CONT void AsArrayHandle( + vtkm::cont::ArrayHandle>& array) const + { + using ContainedArrayType = vtkm::cont::ArrayHandle; + array = vtkm::cont::ArrayHandleCast( + this->AsArrayHandle()); + } + + template + VTKM_CONT ArrayType AsArrayHandle() const + { + VTKM_IS_ARRAY_HANDLE(ArrayType); + ArrayType array; + this->AsArrayHandle(array); + return array; + } + ///@} + + /// \brief Call a functor using the underlying array type. + /// + /// `CastAndCall` attempts to cast the held array to a specific value type, + /// and then calls the given functor with the cast array. You must specify + /// the `TypeList` and `StorageList` as template arguments. + /// + template + VTKM_CONT void CastAndCallForTypes(Functor&& functor, Args&&... args) const; + + /// Releases any resources being used in the execution environment (that are + /// not being shared by the control environment). + /// + VTKM_CONT void ReleaseResourcesExecution() const + { + return this->Container->ReleaseResourcesExecution(this->Container->ArrayHandlePointer); + } + + /// Releases all resources in both the control and execution environments. + /// + VTKM_CONT void ReleaseResources() const + { + return this->Container->ReleaseResources(this->Container->ArrayHandlePointer); + } + + VTKM_CONT void PrintSummary(std::ostream& out, bool full = false) const + { + this->Container->PrintSummary(this->Container->ArrayHandlePointer, out, full); + } +}; + +//============================================================================= +// Out of class implementations + +namespace detail +{ + +template +struct UnknownArrayHandleCanConvert +{ + VTKM_CONT bool operator()(const vtkm::cont::UnknownArrayHandle& array) const + { + return array.IsType>(); + } +}; + +template +struct UnknownArrayHandleCanConvert> +{ + VTKM_CONT bool operator()(const vtkm::cont::UnknownArrayHandle& array) const + { + return UnknownArrayHandleCanConvert{}(array); + } +}; + +template +struct UnknownArrayHandleCanConvertTry +{ + template + VTKM_CONT void operator()(S, const vtkm::cont::UnknownArrayHandle& array, bool& canConvert) const + { + canConvert |= UnknownArrayHandleCanConvert{}(array); + } +}; + +template +struct UnknownArrayHandleCanConvert> +{ + VTKM_CONT bool operator()(const vtkm::cont::UnknownArrayHandle& array) const + { + bool canConvert = false; + vtkm::ListForEach(UnknownArrayHandleCanConvertTry{}, vtkm::List{}, array, canConvert); + return canConvert; + } +}; + +} // namespace detail + +template +VTKM_CONT bool UnknownArrayHandle::CanConvert() const +{ + VTKM_IS_ARRAY_HANDLE(ArrayHandleType); + + return detail::UnknownArrayHandleCanConvert{}(*this); +} + +namespace detail +{ + +struct UnknownArrayHandleMultplexerCastTry +{ + template + VTKM_CONT void operator()( + S, + const vtkm::cont::UnknownArrayHandle& unknownArray, + vtkm::cont::ArrayHandle>& outputArray, + bool& converted) const + { + using ArrayType = vtkm::cont::ArrayHandle; + if (!converted && unknownArray.CanConvert()) + { + outputArray.GetStorage().SetArray(unknownArray.AsArrayHandle()); + converted = true; + } + } +}; + +} // namespace detail + +template +void UnknownArrayHandle::AsArrayHandle( + vtkm::cont::ArrayHandle>& array) const +{ + bool converted = false; + vtkm::ListForEach( + detail::UnknownArrayHandleMultplexerCastTry{}, vtkm::List{}, *this, array, converted); + + if (!converted) + { + VTKM_LOG_CAST_FAIL(*this, decltype(array)); + throwFailedDynamicCast(vtkm::cont::TypeToString(*this), vtkm::cont::TypeToString(array)); + } +} + +namespace detail +{ + +struct UnknownArrayHandleTry +{ + template + void operator()(vtkm::List, + Functor&& f, + bool& called, + const vtkm::cont::UnknownArrayHandle& unknownArray, + Args&&... args) const + { + using DerivedArrayType = vtkm::cont::ArrayHandle; + if (!called && unknownArray.IsType()) + { + called = true; + DerivedArrayType derivedArray; + unknownArray.AsArrayHandle(derivedArray); + VTKM_LOG_CAST_SUCC(unknownArray, derivedArray); + + // If you get a compile error here, it means that you have called CastAndCall for a + // vtkm::cont::UnknownArrayHandle and the arguments of the functor do not match those + // being passed. This is often because it is calling the functor with an ArrayHandle + // type that was not expected. Either add overloads to the functor to accept all + // possible array types or constrain the types tried for the CastAndCall. Note that + // the functor will be called with an array of type vtkm::cont::ArrayHandle. + // Directly using a subclass of ArrayHandle (e.g. vtkm::cont::ArrayHandleConstant) + // might not work. + f(derivedArray, std::forward(args)...); + } + } +}; + +template +struct IsUndefinedArrayType +{ +}; +template +struct IsUndefinedArrayType> : vtkm::cont::internal::IsInvalidArrayHandle +{ +}; + +template +using ListAllArrayTypes = + vtkm::ListRemoveIf, IsUndefinedArrayType>; + + +VTKM_CONT_EXPORT void ThrowCastAndCallException(const vtkm::cont::UnknownArrayHandle&, + const std::type_info&); + +} // namespace detail + + + +template +VTKM_CONT void UnknownArrayHandle::CastAndCallForTypes(Functor&& f, Args&&... args) const +{ + using crossProduct = detail::ListAllArrayTypes; + + bool called = false; + vtkm::ListForEach(detail::UnknownArrayHandleTry{}, + crossProduct{}, + std::forward(f), + called, + *this, + std::forward(args)...); + if (!called) + { + // throw an exception + VTKM_LOG_CAST_FAIL(*this, TypeList); + detail::ThrowCastAndCallException(*this, typeid(TypeList)); + } +} + +template +void CastAndCall(const UnknownArrayHandle& handle, Functor&& f, Args&&... args) +{ + handle.CastAndCallForTypes( + std::forward(f), std::forward(args)...); +} +} +} // namespace vtkm::cont + +#endif //vtk_m_cont_UnknownArrayHandle_h diff --git a/vtkm/cont/testing/CMakeLists.txt b/vtkm/cont/testing/CMakeLists.txt index 163753ce4..e5a9718b9 100644 --- a/vtkm/cont/testing/CMakeLists.txt +++ b/vtkm/cont/testing/CMakeLists.txt @@ -88,6 +88,7 @@ set(unit_tests UnitTestTimer.cxx UnitTestToken.cxx UnitTestTryExecute.cxx + UnitTestUnknownArrayHandle.cxx UnitTestVariantArrayHandle.cxx ) diff --git a/vtkm/cont/testing/UnitTestUnknownArrayHandle.cxx b/vtkm/cont/testing/UnitTestUnknownArrayHandle.cxx new file mode 100644 index 000000000..7bfe18137 --- /dev/null +++ b/vtkm/cont/testing/UnitTestUnknownArrayHandle.cxx @@ -0,0 +1,398 @@ +//============================================================================ +// 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. +//============================================================================ + +#include + +#include + +#include + +namespace +{ + +// Make an "unusual" type to use in the test. This is simply a type that +// is sure not to be declared elsewhere. +struct UnusualType +{ + using T = vtkm::Id; + T X; + UnusualType() = default; + UnusualType(T x) + : X(x) + { + } + UnusualType& operator=(T x) + { + this->X = x; + return *this; + } + operator T() const { return this->X; } +}; + +} // anonymous namespace + +namespace vtkm +{ + +// UnknownArrayHandle requires its value type to have a defined VecTraits +// class. One of the tests is to use an "unusual" array. +// Make an implementation here. Because I am lazy, this is only a partial +// implementation. +template <> +struct VecTraits : VecTraits +{ +}; + +} // namespace vtkm + +namespace +{ + +const vtkm::Id ARRAY_SIZE = 10; + +struct CheckFunctor +{ + template + static void CheckArray(const vtkm::cont::ArrayHandle& array) + { + VTKM_TEST_ASSERT(array.GetNumberOfValues() == ARRAY_SIZE, "Unexpected array size."); + CheckPortal(array.ReadPortal()); + } + + template + static void CheckArray(const vtkm::cont::ArrayHandle& array) + { + VTKM_TEST_ASSERT(array.GetNumberOfValues() == ARRAY_SIZE, "Unexpected array size."); + auto portal = array.ReadPortal(); + for (vtkm::Id index = 0; index < array.GetNumberOfValues(); ++index) + { + VTKM_TEST_ASSERT(portal.Get(index) == TestValue(index, UnusualType::T{})); + } + } + + template + void operator()(const vtkm::cont::ArrayHandle& array, bool& called) const + { + called = true; + std::cout << " Checking for array type " << typeid(T).name() << " with storage " + << typeid(S).name() << std::endl; + + CheckArray(array); + } +}; + +void BasicUnknownArrayChecks(const vtkm::cont::UnknownArrayHandle& array, + vtkm::IdComponent numComponents) +{ + VTKM_TEST_ASSERT(array.GetNumberOfValues() == ARRAY_SIZE, + "Dynamic array reports unexpected size."); + VTKM_TEST_ASSERT(array.GetNumberOfComponents() == numComponents, + "Dynamic array reports unexpected number of components."); +} + +void CheckUnknownArrayDefaults(const vtkm::cont::UnknownArrayHandle& array, + vtkm::IdComponent numComponents) +{ + BasicUnknownArrayChecks(array, numComponents); + + std::cout << " CastAndCall with default types" << std::endl; + bool called = false; + CastAndCall(array, CheckFunctor(), called); +} + +template +void CheckUnknownArray(const vtkm::cont::UnknownArrayHandle& array, vtkm::IdComponent numComponents) +{ + VTKM_IS_LIST(TypeList); + VTKM_IS_LIST(StorageList); + + BasicUnknownArrayChecks(array, numComponents); + + std::cout << " CastAndCall with given types" << std::endl; + bool called = false; + array.CastAndCallForTypes(CheckFunctor{}, called); + + VTKM_TEST_ASSERT( + called, "The functor was never called (and apparently a bad value exception not thrown)."); +} + +template +vtkm::cont::ArrayHandle CreateArray(T) +{ + vtkm::cont::ArrayHandle array; + array.Allocate(ARRAY_SIZE); + SetPortal(array.WritePortal()); + return array; +} + +vtkm::cont::ArrayHandle CreateArray(UnusualType) +{ + vtkm::cont::ArrayHandle array; + array.Allocate(ARRAY_SIZE); + auto portal = array.WritePortal(); + for (vtkm::Id index = 0; index < ARRAY_SIZE; ++index) + { + portal.Set(index, TestValue(index, UnusualType::T{})); + } + return array; +} + +template +vtkm::cont::UnknownArrayHandle CreateArrayUnknown(T t) +{ + return vtkm::cont::UnknownArrayHandle(CreateArray(t)); +} + +template +void CheckCastToArrayHandle(const ArrayHandleType& array) +{ + VTKM_IS_ARRAY_HANDLE(ArrayHandleType); + + vtkm::cont::UnknownArrayHandle arrayUnknown = array; + VTKM_TEST_ASSERT(!arrayUnknown.IsType>(), + "Dynamic array reporting is wrong type."); + + ArrayHandleType castArray1; + arrayUnknown.AsArrayHandle(castArray1); + VTKM_TEST_ASSERT(arrayUnknown.CanConvert(), "Did not query handle correctly."); + VTKM_TEST_ASSERT(array == castArray1, "Did not get back same array."); + + ArrayHandleType castArray2 = arrayUnknown.AsArrayHandle(); + VTKM_TEST_ASSERT(array == castArray2, "Did not get back same array."); + + vtkm::cont::UnknownArrayHandle arrayUnknown2 = vtkm::cont::ArrayHandleMultiplexer< + ArrayHandleType, + vtkm::cont::ArrayHandleConstant>(array); + VTKM_TEST_ASSERT(arrayUnknown2.IsType(), + "Putting in multiplexer did not pull out array."); +} + +// A vtkm::Vec if NumComps > 1, otherwise a scalar +template +using VecOrScalar = typename std::conditional<(NumComps > 1), vtkm::Vec, T>::type; + +template +void TryNewInstance(vtkm::cont::UnknownArrayHandle originalArray) +{ + // This check should already have been performed by caller, but just in case. + CheckUnknownArray, VTKM_DEFAULT_STORAGE_LIST>(originalArray, + vtkm::VecTraits::NUM_COMPONENTS); + + std::cout << "Create new instance of array." << std::endl; + vtkm::cont::UnknownArrayHandle newArray = originalArray.NewInstance(); + + std::cout << "Get a static instance of the new array (which checks the type)." << std::endl; + vtkm::cont::ArrayHandle staticArray; + newArray.AsArrayHandle(staticArray); + + std::cout << "Fill the new array with invalid values and make sure the original" << std::endl + << "is uneffected." << std::endl; + staticArray.Allocate(ARRAY_SIZE); + for (vtkm::Id index = 0; index < ARRAY_SIZE; index++) + { + staticArray.WritePortal().Set(index, TestValue(index + 100, T())); + } + CheckUnknownArray, VTKM_DEFAULT_STORAGE_LIST>(originalArray, + vtkm::VecTraits::NUM_COMPONENTS); + + std::cout << "Set the new static array to expected values and make sure the new" << std::endl + << "dynamic array points to the same new values." << std::endl; + for (vtkm::Id index = 0; index < ARRAY_SIZE; index++) + { + staticArray.WritePortal().Set(index, TestValue(index, T())); + } + CheckUnknownArray, VTKM_DEFAULT_STORAGE_LIST>(newArray, + vtkm::VecTraits::NUM_COMPONENTS); +} + +template +void TryAsMultiplexer(vtkm::cont::UnknownArrayHandle sourceArray) +{ + auto originalArray = sourceArray.AsArrayHandle>(); + + { + std::cout << "Get multiplex array through direct type." << std::endl; + using MultiplexerType = vtkm::cont::ArrayHandleMultiplexer, + vtkm::cont::ArrayHandleConstant>; + VTKM_TEST_ASSERT(sourceArray.CanConvert()); + MultiplexerType multiplexArray = sourceArray.AsArrayHandle(); + + VTKM_TEST_ASSERT(multiplexArray.IsValid()); + VTKM_TEST_ASSERT(test_equal_portals(multiplexArray.ReadPortal(), originalArray.ReadPortal())); + } + + { + std::cout << "Get multiplex array through cast type." << std::endl; + using CastT = typename vtkm::VecTraits::template ReplaceBaseComponentType; + using MultiplexerType = vtkm::cont::ArrayHandleMultiplexer< + vtkm::cont::ArrayHandle, + vtkm::cont::ArrayHandleCast>>; + VTKM_TEST_ASSERT(sourceArray.CanConvert()); + MultiplexerType multiplexArray = sourceArray.AsArrayHandle(); + + VTKM_TEST_ASSERT(multiplexArray.IsValid()); + VTKM_TEST_ASSERT(test_equal_portals(multiplexArray.ReadPortal(), originalArray.ReadPortal())); + } + +#if 0 + // Maybe we should support this, but right now we don't + { + std::cout << "Make sure multiplex array prefers direct array (1st arg)" << std::endl; + using MultiplexerType = vtkm::cont::ArrayHandleMultiplexer< + vtkm::cont::ArrayHandle, + vtkm::cont::ArrayHandleCast>>; + MultiplexerType multiplexArray = sourceArray.AsArrayHandle(); + + VTKM_TEST_ASSERT(multiplexArray.IsValid()); + VTKM_TEST_ASSERT(multiplexArray.GetStorage().GetArrayHandleVariant().GetIndex() == 0); + VTKM_TEST_ASSERT(test_equal_portals(multiplexArray.ReadPortal(), originalArray.ReadPortal())); + } + + { + std::cout << "Make sure multiplex array prefers direct array (2nd arg)" << std::endl; + using MultiplexerType = + vtkm::cont::ArrayHandleMultiplexer>, + vtkm::cont::ArrayHandle>; + MultiplexerType multiplexArray = sourceArray.AsArrayHandle(); + + VTKM_TEST_ASSERT(multiplexArray.IsValid()); + VTKM_TEST_ASSERT(multiplexArray.GetStorage().GetArrayHandleVariant().GetIndex() == 1); + VTKM_TEST_ASSERT(test_equal_portals(multiplexArray.ReadPortal(), originalArray.ReadPortal())); + } +#endif +} + +template +void TryDefaultType() +{ + vtkm::cont::UnknownArrayHandle array = CreateArrayUnknown(T{}); + + CheckUnknownArrayDefaults(array, vtkm::VecTraits::NUM_COMPONENTS); + + TryNewInstance(array); + + TryAsMultiplexer(array); +} + +struct TryBasicVTKmType +{ + template + void operator()(T) const + { + vtkm::cont::UnknownArrayHandle array = CreateArrayUnknown(T()); + + CheckUnknownArray( + array, vtkm::VecTraits::NUM_COMPONENTS); + + TryNewInstance(array); + } +}; + +void TryUnusualType() +{ + // A string is an unlikely type to be declared elsewhere in VTK-m. + vtkm::cont::UnknownArrayHandle array = CreateArrayUnknown(UnusualType{}); + + try + { + CheckUnknownArray(array, 1); + VTKM_TEST_FAIL("CastAndCall failed to error for unrecognized type."); + } + catch (vtkm::cont::ErrorBadValue&) + { + std::cout << " Caught exception for unrecognized type." << std::endl; + } + CheckUnknownArray, VTKM_DEFAULT_STORAGE_LIST>(array, 1); + std::cout << " Found type when type list was reset." << std::endl; +} + +template +void TryCastToArrayHandle(const ArrayHandleType& array) +{ + CheckCastToArrayHandle(array); +} + +void TryCastToArrayHandle() +{ + std::cout << " Normal array handle." << std::endl; + vtkm::Id buffer[ARRAY_SIZE]; + for (vtkm::Id index = 0; index < ARRAY_SIZE; index++) + { + buffer[index] = TestValue(index, vtkm::Id()); + } + + vtkm::cont::ArrayHandle array = + vtkm::cont::make_ArrayHandle(buffer, ARRAY_SIZE, vtkm::CopyFlag::On); + TryCastToArrayHandle(array); + + std::cout << " Constant array handle." << std::endl; + TryCastToArrayHandle(vtkm::cont::make_ArrayHandleConstant(5, ARRAY_SIZE)); +} + +void TrySetCastArray() +{ + vtkm::cont::ArrayHandle knownArray = CreateArray(vtkm::Id{}); + vtkm::cont::UnknownArrayHandle unknownArray( + vtkm::cont::make_ArrayHandleCast(knownArray)); + + // The unknownArray should actually hold the original knownArray type even though we gave it + // a cast array. + CheckUnknownArray, vtkm::List>(unknownArray, 1); +} + +void TrySetMultiplexerArray() +{ + vtkm::cont::ArrayHandle knownArray = CreateArray(vtkm::Id{}); + vtkm::cont::ArrayHandleMultiplexer, + vtkm::cont::ArrayHandleConstant> + multiplexerArray(knownArray); + vtkm::cont::UnknownArrayHandle unknownArray(multiplexerArray); + + // The unknownArray should actually hold the original knownArray type even though we gave it + // a multiplexer array. + CheckUnknownArray, vtkm::List>(unknownArray, 1); +} + +struct DefaultTypeFunctor +{ + template + void operator()(T) const + { + TryDefaultType(); + } +}; + +void TestUnknownArrayHandle() +{ + std::cout << "Try common types with default type lists." << std::endl; + vtkm::testing::Testing::TryTypes(DefaultTypeFunctor{}, VTKM_DEFAULT_TYPE_LIST{}); + + std::cout << "Try exemplar VTK-m types." << std::endl; + vtkm::testing::Testing::TryTypes(TryBasicVTKmType{}); + + std::cout << "Try unusual type." << std::endl; + TryUnusualType(); + + std::cout << "Try CastToArrayHandle" << std::endl; + TryCastToArrayHandle(); + + std::cout << "Try setting ArrayHandleCast" << std::endl; + TrySetCastArray(); + + std::cout << "Try setting ArrayHandleMultiplexer" << std::endl; + TrySetMultiplexerArray(); +} + +} // anonymous namespace + +int UnitTestUnknownArrayHandle(int argc, char* argv[]) +{ + return vtkm::cont::testing::Testing::Run(TestUnknownArrayHandle, argc, argv); +}