Add UnknownArrayHandle

This commit is contained in:
Kenneth Moreland 2020-08-04 21:16:29 -06:00
parent ede0c179b7
commit e47cedd972
7 changed files with 1029 additions and 4 deletions

@ -176,9 +176,9 @@ struct MultiplexerShrinkFunctor
struct MultiplexerReleaseResourcesFunctor
{
template <typename ArrayHandleType>
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;
}
};

@ -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
)
#-----------------------------------------------------------------------------

@ -19,11 +19,13 @@ namespace vtkm
namespace cont
{
class CoordinateSystem;
class Field;
template <typename T, typename S>
class ArrayHandle;
class CoordinateSystem;
class Field;
class UnknownArrayHandle;
template <vtkm::IdComponent>
class CellSetStructured;
@ -65,6 +67,13 @@ void CastAndCall(const vtkm::cont::ArrayHandle<T, U>& handle, Functor&& f, Args&
f(handle, std::forward<Args>(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 <typename Functor, typename... Args>
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

@ -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 <vtkm/cont/UnknownArrayHandle.h>
namespace vtkm
{
namespace cont
{
namespace detail
{
std::shared_ptr<UnknownAHContainer> 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<UnknownAHContainer> 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

@ -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 <vtkm/cont/vtkm_cont_export.h>
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/ArrayHandleCast.h>
#include <vtkm/cont/ArrayHandleMultiplexer.h>
#include <vtkm/cont/DefaultTypes.h>
#include <memory>
#include <typeindex>
namespace vtkm
{
namespace cont
{
namespace detail
{
template <typename T, typename S>
static void UnknownAHDelete(void* mem)
{
using AH = vtkm::cont::ArrayHandle<T, S>;
AH* arrayHandle = reinterpret_cast<AH*>(mem);
delete arrayHandle;
}
template <typename T, typename S>
static void* UnknownADNewInstance()
{
return new vtkm::cont::ArrayHandle<T, S>;
}
template <typename T, typename S>
static vtkm::Id UnknownAHNumberOfValues(void* mem)
{
using AH = vtkm::cont::ArrayHandle<T, S>;
AH* arrayHandle = reinterpret_cast<AH*>(mem);
return arrayHandle->GetNumberOfValues();
}
template <typename T>
static vtkm::IdComponent UnknownAHNumberOfComponents()
{
return vtkm::VecTraits<T>::NUM_COMPONENTS;
}
template <typename T, typename S>
static void UnknownAHReleaseResources(void* mem)
{
using AH = vtkm::cont::ArrayHandle<T, S>;
AH* arrayHandle = reinterpret_cast<AH*>(mem);
arrayHandle->ReleaseResources();
}
template <typename T, typename S>
static void UnknownAHReleaseResourcesExecution(void* mem)
{
using AH = vtkm::cont::ArrayHandle<T, S>;
AH* arrayHandle = reinterpret_cast<AH*>(mem);
arrayHandle->ReleaseResourcesExecution();
}
template <typename T, typename S>
static void UnknownAHPrintSummary(void* mem, std::ostream& out, bool full)
{
using AH = vtkm::cont::ArrayHandle<T, S>;
AH* arrayHandle = reinterpret_cast<AH*>(mem);
vtkm::cont::printSummary_ArrayHandle(*arrayHandle, out, full);
}
struct VTKM_CONT_EXPORT UnknownAHContainer;
struct MakeUnknownAHContainerFunctor
{
template <typename T, typename S>
std::shared_ptr<UnknownAHContainer> operator()(const vtkm::cont::ArrayHandle<T, S>& 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<UnknownAHContainer> MakeNewInstance() const;
template <typename T, typename S>
static std::shared_ptr<UnknownAHContainer> Make(const vtkm::cont::ArrayHandle<T, S>& array)
{
return std::shared_ptr<UnknownAHContainer>(new UnknownAHContainer(array));
}
template <typename TargetT, typename SourceT, typename SourceS>
static std::shared_ptr<UnknownAHContainer> Make(
const vtkm::cont::ArrayHandle<TargetT, vtkm::cont::StorageTagCast<SourceT, SourceS>>& array)
{
return Make(array.GetStorage().GetArray());
}
template <typename T, typename... Ss>
static std::shared_ptr<UnknownAHContainer> Make(
const vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagMultiplexer<Ss...>>& array)
{
auto&& variant = array.GetStorage().GetArrayHandleVariant();
if (variant.IsValid())
{
return variant.CastAndCall(MakeUnknownAHContainerFunctor{});
}
else
{
return std::shared_ptr<UnknownAHContainer>{};
}
}
private:
UnknownAHContainer(const UnknownAHContainer&) = default;
template <typename T, typename S>
explicit UnknownAHContainer(const vtkm::cont::ArrayHandle<T, S>& array)
: ArrayHandlePointer(new vtkm::cont::ArrayHandle<T, S>(array))
, ValueType(typeid(T))
, StorageType(typeid(S))
, DeleteFunction(detail::UnknownAHDelete<T, S>)
, NewInstance(detail::UnknownADNewInstance<T, S>)
, NumberOfValues(detail::UnknownAHNumberOfValues<T, S>)
, NumberOfComponents(detail::UnknownAHNumberOfComponents<T>)
, ReleaseResources(detail::UnknownAHReleaseResources<T, S>)
, ReleaseResourcesExecution(detail::UnknownAHReleaseResourcesExecution<T, S>)
, PrintSummary(detail::UnknownAHPrintSummary<T, S>)
{
}
};
template <typename T, typename S>
inline std::shared_ptr<UnknownAHContainer> MakeUnknownAHContainerFunctor::operator()(
const vtkm::cont::ArrayHandle<T, S>& 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<detail::UnknownAHContainer> 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 <typename T, typename S>
VTKM_CONT UnknownArrayHandle(const vtkm::cont::ArrayHandle<T, S>& 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 <typename ValueType>
VTKM_CONT bool IsValueType() const
{
return this->IsValueTypeImpl(typeid(ValueType));
}
/// Returns true if this array matches the StorageType template argument.
///
template <typename StorageType>
VTKM_CONT bool IsStorageType() const
{
return this->IsStorageTypeImpl(typeid(StorageType));
}
/// Returns true if this array matches the ArrayHandleType template argument.
///
template <typename ArrayHandleType>
VTKM_CONT bool IsType() const
{
VTKM_IS_ARRAY_HANDLE(ArrayHandleType);
return (this->IsValueType<typename ArrayHandleType::ValueType>() &&
this->IsStorageType<typename ArrayHandleType::StorageTag>());
}
/// \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 <typename ArrayHandleType>
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 <typename T, typename S>
VTKM_CONT void AsArrayHandle(vtkm::cont::ArrayHandle<T, S>& array) const
{
using ArrayType = vtkm::cont::ArrayHandle<T, S>;
if (!this->IsType<ArrayType>())
{
VTKM_LOG_CAST_FAIL(*this, decltype(array));
throwFailedDynamicCast(vtkm::cont::TypeToString(*this), vtkm::cont::TypeToString(array));
}
array = *reinterpret_cast<ArrayType*>(this->Container->ArrayHandlePointer);
}
template <typename T, typename... Ss>
VTKM_CONT void AsArrayHandle(
vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagMultiplexer<Ss...>>& array) const;
template <typename TargetT, typename SourceT, typename SourceS>
VTKM_CONT void AsArrayHandle(
vtkm::cont::ArrayHandle<TargetT, vtkm::cont::StorageTagCast<SourceT, SourceS>>& array) const
{
using ContainedArrayType = vtkm::cont::ArrayHandle<SourceT, SourceS>;
array = vtkm::cont::ArrayHandleCast<TargetT, ContainedArrayType>(
this->AsArrayHandle<ContainedArrayType>());
}
template <typename ArrayType>
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 <typename TypeList, typename StorageList, typename Functor, typename... Args>
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 <typename T, typename S>
struct UnknownArrayHandleCanConvert
{
VTKM_CONT bool operator()(const vtkm::cont::UnknownArrayHandle& array) const
{
return array.IsType<vtkm::cont::ArrayHandle<T, S>>();
}
};
template <typename TargetT, typename SourceT, typename SourceS>
struct UnknownArrayHandleCanConvert<TargetT, vtkm::cont::StorageTagCast<SourceT, SourceS>>
{
VTKM_CONT bool operator()(const vtkm::cont::UnknownArrayHandle& array) const
{
return UnknownArrayHandleCanConvert<SourceT, SourceS>{}(array);
}
};
template <typename T>
struct UnknownArrayHandleCanConvertTry
{
template <typename S>
VTKM_CONT void operator()(S, const vtkm::cont::UnknownArrayHandle& array, bool& canConvert) const
{
canConvert |= UnknownArrayHandleCanConvert<T, S>{}(array);
}
};
template <typename T, typename... Ss>
struct UnknownArrayHandleCanConvert<T, vtkm::cont::StorageTagMultiplexer<Ss...>>
{
VTKM_CONT bool operator()(const vtkm::cont::UnknownArrayHandle& array) const
{
bool canConvert = false;
vtkm::ListForEach(UnknownArrayHandleCanConvertTry<T>{}, vtkm::List<Ss...>{}, array, canConvert);
return canConvert;
}
};
} // namespace detail
template <typename ArrayHandleType>
VTKM_CONT bool UnknownArrayHandle::CanConvert() const
{
VTKM_IS_ARRAY_HANDLE(ArrayHandleType);
return detail::UnknownArrayHandleCanConvert<typename ArrayHandleType::ValueType,
typename ArrayHandleType::StorageTag>{}(*this);
}
namespace detail
{
struct UnknownArrayHandleMultplexerCastTry
{
template <typename T, typename S, typename... Ss>
VTKM_CONT void operator()(
S,
const vtkm::cont::UnknownArrayHandle& unknownArray,
vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagMultiplexer<Ss...>>& outputArray,
bool& converted) const
{
using ArrayType = vtkm::cont::ArrayHandle<T, S>;
if (!converted && unknownArray.CanConvert<ArrayType>())
{
outputArray.GetStorage().SetArray(unknownArray.AsArrayHandle<ArrayType>());
converted = true;
}
}
};
} // namespace detail
template <typename T, typename... Ss>
void UnknownArrayHandle::AsArrayHandle(
vtkm::cont::ArrayHandle<T, vtkm::cont::StorageTagMultiplexer<Ss...>>& array) const
{
bool converted = false;
vtkm::ListForEach(
detail::UnknownArrayHandleMultplexerCastTry{}, vtkm::List<Ss...>{}, *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 <typename T, typename S, typename Functor, typename... Args>
void operator()(vtkm::List<T, S>,
Functor&& f,
bool& called,
const vtkm::cont::UnknownArrayHandle& unknownArray,
Args&&... args) const
{
using DerivedArrayType = vtkm::cont::ArrayHandle<T, S>;
if (!called && unknownArray.IsType<DerivedArrayType>())
{
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<T, S>.
// Directly using a subclass of ArrayHandle (e.g. vtkm::cont::ArrayHandleConstant<T>)
// might not work.
f(derivedArray, std::forward<Args>(args)...);
}
}
};
template <typename T>
struct IsUndefinedArrayType
{
};
template <typename T, typename U>
struct IsUndefinedArrayType<vtkm::List<T, U>> : vtkm::cont::internal::IsInvalidArrayHandle<T, U>
{
};
template <typename TypeList, typename StorageList>
using ListAllArrayTypes =
vtkm::ListRemoveIf<vtkm::ListCross<TypeList, StorageList>, IsUndefinedArrayType>;
VTKM_CONT_EXPORT void ThrowCastAndCallException(const vtkm::cont::UnknownArrayHandle&,
const std::type_info&);
} // namespace detail
template <typename TypeList, typename StorageTagList, typename Functor, typename... Args>
VTKM_CONT void UnknownArrayHandle::CastAndCallForTypes(Functor&& f, Args&&... args) const
{
using crossProduct = detail::ListAllArrayTypes<TypeList, StorageTagList>;
bool called = false;
vtkm::ListForEach(detail::UnknownArrayHandleTry{},
crossProduct{},
std::forward<Functor>(f),
called,
*this,
std::forward<Args>(args)...);
if (!called)
{
// throw an exception
VTKM_LOG_CAST_FAIL(*this, TypeList);
detail::ThrowCastAndCallException(*this, typeid(TypeList));
}
}
template <typename Functor, typename... Args>
void CastAndCall(const UnknownArrayHandle& handle, Functor&& f, Args&&... args)
{
handle.CastAndCallForTypes<VTKM_DEFAULT_TYPE_LIST, VTKM_DEFAULT_STORAGE_LIST>(
std::forward<Functor>(f), std::forward<Args>(args)...);
}
}
} // namespace vtkm::cont
#endif //vtk_m_cont_UnknownArrayHandle_h

@ -88,6 +88,7 @@ set(unit_tests
UnitTestTimer.cxx
UnitTestToken.cxx
UnitTestTryExecute.cxx
UnitTestUnknownArrayHandle.cxx
UnitTestVariantArrayHandle.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 <vtkm/cont/UnknownArrayHandle.h>
#include <vtkm/TypeTraits.h>
#include <vtkm/cont/testing/Testing.h>
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<UnusualType> : VecTraits<UnusualType::T>
{
};
} // namespace vtkm
namespace
{
const vtkm::Id ARRAY_SIZE = 10;
struct CheckFunctor
{
template <typename T, typename S>
static void CheckArray(const vtkm::cont::ArrayHandle<T, S>& array)
{
VTKM_TEST_ASSERT(array.GetNumberOfValues() == ARRAY_SIZE, "Unexpected array size.");
CheckPortal(array.ReadPortal());
}
template <typename S>
static void CheckArray(const vtkm::cont::ArrayHandle<UnusualType, S>& 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 <typename T, typename S>
void operator()(const vtkm::cont::ArrayHandle<T, S>& 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 <typename TypeList, typename StorageList>
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<TypeList, StorageList>(CheckFunctor{}, called);
VTKM_TEST_ASSERT(
called, "The functor was never called (and apparently a bad value exception not thrown).");
}
template <typename T>
vtkm::cont::ArrayHandle<T> CreateArray(T)
{
vtkm::cont::ArrayHandle<T> array;
array.Allocate(ARRAY_SIZE);
SetPortal(array.WritePortal());
return array;
}
vtkm::cont::ArrayHandle<UnusualType> CreateArray(UnusualType)
{
vtkm::cont::ArrayHandle<UnusualType> 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 <typename T>
vtkm::cont::UnknownArrayHandle CreateArrayUnknown(T t)
{
return vtkm::cont::UnknownArrayHandle(CreateArray(t));
}
template <typename ArrayHandleType>
void CheckCastToArrayHandle(const ArrayHandleType& array)
{
VTKM_IS_ARRAY_HANDLE(ArrayHandleType);
vtkm::cont::UnknownArrayHandle arrayUnknown = array;
VTKM_TEST_ASSERT(!arrayUnknown.IsType<vtkm::cont::ArrayHandle<UnusualType>>(),
"Dynamic array reporting is wrong type.");
ArrayHandleType castArray1;
arrayUnknown.AsArrayHandle(castArray1);
VTKM_TEST_ASSERT(arrayUnknown.CanConvert<ArrayHandleType>(), "Did not query handle correctly.");
VTKM_TEST_ASSERT(array == castArray1, "Did not get back same array.");
ArrayHandleType castArray2 = arrayUnknown.AsArrayHandle<ArrayHandleType>();
VTKM_TEST_ASSERT(array == castArray2, "Did not get back same array.");
vtkm::cont::UnknownArrayHandle arrayUnknown2 = vtkm::cont::ArrayHandleMultiplexer<
ArrayHandleType,
vtkm::cont::ArrayHandleConstant<typename ArrayHandleType::ValueType>>(array);
VTKM_TEST_ASSERT(arrayUnknown2.IsType<ArrayHandleType>(),
"Putting in multiplexer did not pull out array.");
}
// A vtkm::Vec if NumComps > 1, otherwise a scalar
template <typename T, vtkm::IdComponent NumComps>
using VecOrScalar = typename std::conditional<(NumComps > 1), vtkm::Vec<T, NumComps>, T>::type;
template <typename T>
void TryNewInstance(vtkm::cont::UnknownArrayHandle originalArray)
{
// This check should already have been performed by caller, but just in case.
CheckUnknownArray<vtkm::List<T>, VTKM_DEFAULT_STORAGE_LIST>(originalArray,
vtkm::VecTraits<T>::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<T> 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::List<T>, VTKM_DEFAULT_STORAGE_LIST>(originalArray,
vtkm::VecTraits<T>::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::List<T>, VTKM_DEFAULT_STORAGE_LIST>(newArray,
vtkm::VecTraits<T>::NUM_COMPONENTS);
}
template <typename T>
void TryAsMultiplexer(vtkm::cont::UnknownArrayHandle sourceArray)
{
auto originalArray = sourceArray.AsArrayHandle<vtkm::cont::ArrayHandle<T>>();
{
std::cout << "Get multiplex array through direct type." << std::endl;
using MultiplexerType = vtkm::cont::ArrayHandleMultiplexer<vtkm::cont::ArrayHandle<T>,
vtkm::cont::ArrayHandleConstant<T>>;
VTKM_TEST_ASSERT(sourceArray.CanConvert<MultiplexerType>());
MultiplexerType multiplexArray = sourceArray.AsArrayHandle<MultiplexerType>();
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<T>::template ReplaceBaseComponentType<vtkm::Float64>;
using MultiplexerType = vtkm::cont::ArrayHandleMultiplexer<
vtkm::cont::ArrayHandle<CastT>,
vtkm::cont::ArrayHandleCast<CastT, vtkm::cont::ArrayHandle<T>>>;
VTKM_TEST_ASSERT(sourceArray.CanConvert<MultiplexerType>());
MultiplexerType multiplexArray = sourceArray.AsArrayHandle<MultiplexerType>();
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<T>,
vtkm::cont::ArrayHandleCast<T, vtkm::cont::ArrayHandle<T>>>;
MultiplexerType multiplexArray = sourceArray.AsArrayHandle<MultiplexerType>();
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::ArrayHandleCast<T, vtkm::cont::ArrayHandle<T>>,
vtkm::cont::ArrayHandle<T>>;
MultiplexerType multiplexArray = sourceArray.AsArrayHandle<MultiplexerType>();
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 <typename T>
void TryDefaultType()
{
vtkm::cont::UnknownArrayHandle array = CreateArrayUnknown(T{});
CheckUnknownArrayDefaults(array, vtkm::VecTraits<T>::NUM_COMPONENTS);
TryNewInstance<T>(array);
TryAsMultiplexer<T>(array);
}
struct TryBasicVTKmType
{
template <typename T>
void operator()(T) const
{
vtkm::cont::UnknownArrayHandle array = CreateArrayUnknown(T());
CheckUnknownArray<vtkm::TypeListAll, VTKM_DEFAULT_STORAGE_LIST>(
array, vtkm::VecTraits<T>::NUM_COMPONENTS);
TryNewInstance<T>(array);
}
};
void TryUnusualType()
{
// A string is an unlikely type to be declared elsewhere in VTK-m.
vtkm::cont::UnknownArrayHandle array = CreateArrayUnknown(UnusualType{});
try
{
CheckUnknownArray<VTKM_DEFAULT_TYPE_LIST, VTKM_DEFAULT_STORAGE_LIST>(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::List<UnusualType>, VTKM_DEFAULT_STORAGE_LIST>(array, 1);
std::cout << " Found type when type list was reset." << std::endl;
}
template <typename ArrayHandleType>
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<vtkm::Id> 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<vtkm::Id> knownArray = CreateArray(vtkm::Id{});
vtkm::cont::UnknownArrayHandle unknownArray(
vtkm::cont::make_ArrayHandleCast<vtkm::Float32>(knownArray));
// The unknownArray should actually hold the original knownArray type even though we gave it
// a cast array.
CheckUnknownArray<vtkm::List<vtkm::Id>, vtkm::List<VTKM_DEFAULT_STORAGE_TAG>>(unknownArray, 1);
}
void TrySetMultiplexerArray()
{
vtkm::cont::ArrayHandle<vtkm::Id> knownArray = CreateArray(vtkm::Id{});
vtkm::cont::ArrayHandleMultiplexer<vtkm::cont::ArrayHandle<vtkm::Id>,
vtkm::cont::ArrayHandleConstant<vtkm::Id>>
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<vtkm::Id>, vtkm::List<VTKM_DEFAULT_STORAGE_TAG>>(unknownArray, 1);
}
struct DefaultTypeFunctor
{
template <typename T>
void operator()(T) const
{
TryDefaultType<T>();
}
};
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);
}