mirror of
https://gitlab.kitware.com/vtk/vtk-m
synced 2024-09-08 13:23:51 +00:00
Add UncertainArrayHandle
This commit is contained in:
parent
e47cedd972
commit
94aa6449b9
109
docs/changelog/unknown-array-handle.md
Normal file
109
docs/changelog/unknown-array-handle.md
Normal file
@ -0,0 +1,109 @@
|
||||
# UnknownArrayHandle and UncertainArrayHandle for runtime-determined types
|
||||
|
||||
Two new classes have been added to VTK-m: `UnknownArrayHandle` and
|
||||
`UncertainArrayHandle`. These classes serve the same purpose as the set of
|
||||
`VariantArrayHandle` classes and will replace them.
|
||||
|
||||
Motivated mostly by the desire to move away from `ArrayHandleVirtual`, we
|
||||
have multiple reasons to completely refactor the `VariantArrayHandle`
|
||||
class. These include changing the implementation, some behavior, and even
|
||||
the name.
|
||||
|
||||
## Motivation
|
||||
|
||||
We have several reasons that have accumulated to revisit the implementation
|
||||
of `VariantArrayHandle`.
|
||||
|
||||
### Move away from `ArrayHandleVirtual`
|
||||
|
||||
The current implementation of `VariantArrayHandle` internally stores the
|
||||
array wrapped in an `ArrayHandleVirtual`. That makes sense since you might
|
||||
as well consolidate the hierarchy of virtual objects into one.
|
||||
|
||||
Except `ArrayHandleVirtual` is being deprecated, so it no longer makes
|
||||
sense to use that internally.
|
||||
|
||||
So we will transition the class back to managing the data as typeless on
|
||||
its own. We will consider using function pointers rather than actual
|
||||
virtual functions because compilers can be slow in creating lots of virtual
|
||||
subclasses.
|
||||
|
||||
### Reintroduce storage tag lists
|
||||
|
||||
The original implementation of `VariantArrayHandle` (which at the time was
|
||||
called `DynamicArrayHandle`) actually had two type lists: one for the array
|
||||
value type and one for the storage type. The storage type list was removed
|
||||
soon after `ArrayHandleVirtual` was introduced because whatever the type of
|
||||
array it could be access as `ArrayHandleVirtual`.
|
||||
|
||||
However, with `ArrayHandleVirtual` being deprecated, this feature is no
|
||||
longer possible. We are in need again for the list of storage types to try.
|
||||
Thus, we need to reintroduce this template argument to
|
||||
`VariantArrayHandle`.
|
||||
|
||||
### More clear name
|
||||
|
||||
The name of this class has always been unsatisfactory. The first name,
|
||||
`DynamicArrayHandle`, makes it sound like the data is always changing. The
|
||||
second name, `VariantArrayHandle`, makes it sound like an array that holds
|
||||
a value type that can vary (like an `std::variant`).
|
||||
|
||||
We can use a more clear name that expresses better that it is holding an
|
||||
`ArrayHandle` of an _unknown_ type.
|
||||
|
||||
### Take advantage of default types for less templating
|
||||
|
||||
Once upon a time everything in VTK-m was templated header library. Things
|
||||
have changed quite a bit since then. The most recent development is the
|
||||
ability to select the "default types" with CMake configuration that allows
|
||||
you to select a global set of types you care about during compilation. This
|
||||
is so units like filters can be compiled into a library with all types we
|
||||
care about, and we don't have to constantly recompile units.
|
||||
|
||||
This means that we are becoming less concerned about maintaining type lists
|
||||
everywhere. Often we can drop the type list and pass data across libraries.
|
||||
|
||||
With that in mind, it makes less sense for `VariantArrayHandle` to actually
|
||||
be a `using` alias for `VariantArrayHandleBase<VTKM_DEFAULT_TYPE_LIST>`.
|
||||
|
||||
In response, we can revert the is-a relationship between the two. Have a
|
||||
completely typeless version as the base class and have a second version
|
||||
templated version to express when the type of the array has been partially
|
||||
narrowed down to given type lists.
|
||||
|
||||
## New Name and Structure
|
||||
|
||||
The ultimate purpose of this class is to store an `ArrayHandle` where the
|
||||
value and storage types are unknown. Thus, an appropriate name for the
|
||||
class is `UnknownArrayHandle`.
|
||||
|
||||
`UnknownArrayHandle` is _not_ templated. It simply stores an `ArrayHandle`
|
||||
in a typeless (`void *`) buffer. It does, however, contain many templated
|
||||
methods that allow you to query whether the contained array matches given
|
||||
types, to cast to given types, and to cast and call to a given functor
|
||||
(from either given type lists or default lists).
|
||||
|
||||
Rather than have a virtual class structure to manage the typeless array,
|
||||
the new management will use function pointers. This has shown to sometimes
|
||||
improve compile times and generate less code.
|
||||
|
||||
Sometimes it is the case that the set of potential types can be narrowed. In
|
||||
this case, the array ceases to be unknown and becomes _uncertain_. Thus,
|
||||
the companion class to `UnknownArrayHandle` is `UncertainArrayHandle`.
|
||||
|
||||
`UncertainArrayHandle` has two template parameters: a list of potential
|
||||
value types and a list of potential storage types. The behavior of
|
||||
`UncertainArrayHandle` matches that of `UnknownArrayHandle` (and might
|
||||
inherit from it). However, for `CastAndCall` operations, it will use the
|
||||
type lists defined in its template parameters.
|
||||
|
||||
## Serializing UnknownArrayHandle
|
||||
|
||||
Because `UnknownArrayHandle` is not templated, it contains some
|
||||
opportunities to compile things into the `vtkm_cont` library. Templated
|
||||
methods like `CastAndCall` cannot be, but the specializations of DIY's
|
||||
serialize can be.
|
||||
|
||||
And since it only has to be compiled once into a library, we can spend some
|
||||
extra time compiling for more types. We don't have to restrict ourselves to
|
||||
`VTKM_DEFAULT_TYPE_LIST`. We can compile for vtkm::TypeListTagAll.
|
@ -116,6 +116,7 @@ set(headers
|
||||
Token.h
|
||||
TryExecute.h
|
||||
SerializableTypeString.h
|
||||
UncertainArrayHandle.h
|
||||
UnknownArrayHandle.h
|
||||
VariantArrayHandle.h
|
||||
VirtualObjectHandle.h
|
||||
|
122
vtkm/cont/UncertainArrayHandle.h
Normal file
122
vtkm/cont/UncertainArrayHandle.h
Normal file
@ -0,0 +1,122 @@
|
||||
//============================================================================
|
||||
// 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_UncertainArrayHandle_h
|
||||
#define vtk_m_cont_UncertainArrayHandle_h
|
||||
|
||||
#include <vtkm/cont/UnknownArrayHandle.h>
|
||||
|
||||
namespace vtkm
|
||||
{
|
||||
namespace cont
|
||||
{
|
||||
|
||||
/// \brief An ArrayHandle of an uncertain value type and storage.
|
||||
///
|
||||
/// `UncertainArrayHandle` holds an `ArrayHandle` object using runtime polymorphism
|
||||
/// to manage different value and storage types. It behaves like its superclass,
|
||||
/// `UnknownArrayHandle`, except that it also contains two template parameters that
|
||||
/// provide `vtkm::List`s of potential value and storage types, respectively.
|
||||
///
|
||||
/// These potential value and storage types come into play when the `CastAndCall`
|
||||
/// method is called (or the `UncertainArrayHandle` is used in the
|
||||
/// `vtkm::cont::CastAndCall` function). In this case, the `CastAndCall` will
|
||||
/// search for `ArrayHandle`s of types that match these two lists.
|
||||
///
|
||||
/// Both `UncertainArrayHandle` and `UnknownArrayHandle` have a method named
|
||||
/// `ResetTypes` that redefine the lists of potential value and storage types
|
||||
/// by returning a new `UncertainArrayHandle` containing the same `ArrayHandle`
|
||||
/// but with the new value and storage type lists.
|
||||
///
|
||||
template <typename ValueTypeList, typename StorageTypeList>
|
||||
class VTKM_ALWAYS_EXPORT UncertainArrayHandle : public vtkm::cont::UnknownArrayHandle
|
||||
{
|
||||
VTKM_IS_LIST(ValueTypeList);
|
||||
VTKM_IS_LIST(StorageTypeList);
|
||||
|
||||
VTKM_STATIC_ASSERT_MSG((!std::is_same<ValueTypeList, vtkm::ListUniversal>::value),
|
||||
"Cannot use vtkm::ListUniversal with UncertainArrayHandle.");
|
||||
VTKM_STATIC_ASSERT_MSG((!std::is_same<StorageTypeList, vtkm::ListUniversal>::value),
|
||||
"Cannot use vtkm::ListUniversal with UncertainArrayHandle.");
|
||||
|
||||
using Superclass = UnknownArrayHandle;
|
||||
using Thisclass = UncertainArrayHandle<ValueTypeList, StorageTypeList>;
|
||||
|
||||
public:
|
||||
VTKM_CONT UncertainArrayHandle() = default;
|
||||
|
||||
template <typename T, typename S>
|
||||
VTKM_CONT UncertainArrayHandle(const vtkm::cont::ArrayHandle<T, S>& array)
|
||||
: Superclass(array)
|
||||
{
|
||||
}
|
||||
|
||||
explicit VTKM_CONT UncertainArrayHandle(const vtkm::cont::UnknownArrayHandle& src)
|
||||
: Superclass(src)
|
||||
{
|
||||
}
|
||||
|
||||
UncertainArrayHandle(const Thisclass&) = default;
|
||||
|
||||
template <typename OtherValues, typename OtherStorage>
|
||||
VTKM_CONT UncertainArrayHandle(const UncertainArrayHandle<OtherValues, OtherStorage>& src)
|
||||
: Superclass(src)
|
||||
{
|
||||
}
|
||||
|
||||
/// \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 `UncertainArrayHandle` for it. This method is convenient when
|
||||
/// creating output arrays that should be the same type as some input array.
|
||||
///
|
||||
VTKM_CONT Thisclass NewInstance() const { return Thisclass(this->Superclass::NewInstance()); }
|
||||
|
||||
/// Like `ResetTypes` except it only resets the value types.
|
||||
///
|
||||
template <typename NewValueTypeList>
|
||||
VTKM_CONT UncertainArrayHandle<NewValueTypeList, StorageTypeList> ResetValueTypes(
|
||||
NewValueTypeList = NewValueTypeList{}) const
|
||||
{
|
||||
return this->ResetTypes<NewValueTypeList, StorageTypeList>();
|
||||
}
|
||||
|
||||
/// Like `ResetTypes` except it only resets the storage types.
|
||||
///
|
||||
template <typename NewStorageTypeList>
|
||||
VTKM_CONT UncertainArrayHandle<ValueTypeList, NewStorageTypeList> ResetStorageTypes(
|
||||
NewStorageTypeList = NewStorageTypeList{}) const
|
||||
{
|
||||
return this->ResetTypes<ValueTypeList, NewStorageTypeList>();
|
||||
}
|
||||
|
||||
/// \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.
|
||||
///
|
||||
template <typename Functor, typename... Args>
|
||||
VTKM_CONT void CastAndCall(Functor&& functor, Args&&... args) const
|
||||
{
|
||||
this->CastAndCallForTypes<ValueTypeList, StorageTypeList>(std::forward<Functor>(functor),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
// Defined here to avoid circular dependencies between UnknownArrayHandle and UncertainArrayHandle.
|
||||
template <typename NewValueTypeList, typename NewStorageTypeList>
|
||||
VTKM_CONT vtkm::cont::UncertainArrayHandle<NewValueTypeList, NewStorageTypeList>
|
||||
UnknownArrayHandle::ResetTypes(NewValueTypeList, NewStorageTypeList) const
|
||||
{
|
||||
return vtkm::cont::UncertainArrayHandle<NewValueTypeList, NewStorageTypeList>(*this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif //vtk_m_cont_UncertainArrayHandle_h
|
@ -177,10 +177,14 @@ inline std::shared_ptr<UnknownAHContainer> MakeUnknownAHContainerFunctor::operat
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/// \brief An ArrayHandle of an unknown type and storage.
|
||||
// Forward declaration. Include UncertainArrayHandle.h if using this.
|
||||
template <typename ValueTypeList, typename StorageTypeList>
|
||||
class UncertainArrayHandle;
|
||||
|
||||
/// \brief An ArrayHandle of an unknown value type and storage.
|
||||
///
|
||||
/// `UnknownArrayHandle` holds an `ArrayHandle` object using runtime polymorphism
|
||||
/// to manage different value types and storage rather than compile-time templates.
|
||||
/// to manage different value and storage types 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
|
||||
@ -226,13 +230,16 @@ public:
|
||||
/// \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
|
||||
/// returns a new `UnknownArrayHandle` 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();
|
||||
if (this->Container)
|
||||
{
|
||||
newArray.Container = this->Container->MakeNewInstance();
|
||||
}
|
||||
return newArray;
|
||||
}
|
||||
|
||||
@ -262,11 +269,31 @@ public:
|
||||
this->IsStorageType<typename ArrayHandleType::StorageTag>());
|
||||
}
|
||||
|
||||
/// \brief Assigns potential value and storage types.
|
||||
///
|
||||
/// Calling this method will return an `UncertainArrayHandle` with the provided
|
||||
/// value and storage type lists. The returned object will hold the same
|
||||
/// `ArrayHandle`, but `CastAndCall`s on the returned object will be constrained
|
||||
/// to the given types.
|
||||
///
|
||||
// Defined in UncertainArrayHandle.h
|
||||
template <typename NewValueTypeList, typename NewStorageTypeList>
|
||||
VTKM_CONT vtkm::cont::UncertainArrayHandle<NewValueTypeList, NewStorageTypeList> ResetTypes(
|
||||
NewValueTypeList = NewValueTypeList{},
|
||||
NewStorageTypeList = NewStorageTypeList{}) const;
|
||||
|
||||
/// \brief Returns the number of values in the array.
|
||||
///
|
||||
VTKM_CONT vtkm::Id GetNumberOfValues() const
|
||||
{
|
||||
return this->Container->NumberOfValues(this->Container->ArrayHandlePointer);
|
||||
if (this->Container)
|
||||
{
|
||||
return this->Container->NumberOfValues(this->Container->ArrayHandlePointer);
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief Returns the number of components for each value in the array.
|
||||
@ -276,7 +303,14 @@ public:
|
||||
///
|
||||
VTKM_CONT vtkm::IdComponent GetNumberOfComponents() const
|
||||
{
|
||||
return this->Container->NumberOfComponents();
|
||||
if (this->Container)
|
||||
{
|
||||
return this->Container->NumberOfComponents();
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// \brief Determine if the contained array can be passed to the given array type.
|
||||
@ -344,19 +378,32 @@ public:
|
||||
///
|
||||
VTKM_CONT void ReleaseResourcesExecution() const
|
||||
{
|
||||
return this->Container->ReleaseResourcesExecution(this->Container->ArrayHandlePointer);
|
||||
if (this->Container)
|
||||
{
|
||||
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);
|
||||
if (this->Container)
|
||||
{
|
||||
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);
|
||||
if (this->Container)
|
||||
{
|
||||
this->Container->PrintSummary(this->Container->ArrayHandlePointer, out, full);
|
||||
}
|
||||
else
|
||||
{
|
||||
out << "null array" << std::endl;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
// PURPOSE. See the above copyright notice for more information.
|
||||
//============================================================================
|
||||
|
||||
#include <vtkm/cont/UncertainArrayHandle.h>
|
||||
#include <vtkm/cont/UnknownArrayHandle.h>
|
||||
|
||||
#include <vtkm/TypeTraits.h>
|
||||
@ -104,7 +105,7 @@ void CheckUnknownArrayDefaults(const vtkm::cont::UnknownArrayHandle& array,
|
||||
|
||||
std::cout << " CastAndCall with default types" << std::endl;
|
||||
bool called = false;
|
||||
CastAndCall(array, CheckFunctor(), called);
|
||||
vtkm::cont::CastAndCall(array, CheckFunctor(), called);
|
||||
}
|
||||
|
||||
template <typename TypeList, typename StorageList>
|
||||
@ -118,7 +119,12 @@ void CheckUnknownArray(const vtkm::cont::UnknownArrayHandle& array, vtkm::IdComp
|
||||
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).");
|
||||
|
||||
std::cout << " Check CastAndCall again with UncertainArrayHandle" << std::endl;
|
||||
called = false;
|
||||
vtkm::cont::CastAndCall(array.ResetTypes<TypeList, StorageList>(), CheckFunctor{}, called);
|
||||
VTKM_TEST_ASSERT(
|
||||
called, "The functor was never called (and apparently a bad value exception not thrown).");
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user