3.5. Unknown Array Handles

The vtkm::cont::ArrayHandle class uses templating to make very efficient and type-safe access to data. However, it is sometimes inconvenient or impossible to specify the element type and storage at run-time. The vtkm::cont::UnknownArrayHandle class provides a mechanism to manage arrays of data with unspecified types.

vtkm::cont::UnknownArrayHandle holds a reference to an array. Unlike vtkm::cont::ArrayHandle, vtkm::cont::UnknownArrayHandle is not templated. Instead, it uses C++ run-type type information to store the array without type and cast it when appropriate.

class UnknownArrayHandle

An ArrayHandle of an unknown value type and storage.

UnknownArrayHandle holds an ArrayHandle object using runtime polymorphism 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 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.

Subclassed by vtkm::cont::UncertainArrayHandle< ValueTypeList, StorageTypeList >

An vtkm::cont::UnknownArrayHandle can be established by constructing it with or assigning it to an vtkm::cont::ArrayHandle. The following example demonstrates how an vtkm::cont::UnknownArrayHandle might be used to load an array whose type is not known until run-time.

Example 3.27 Creating an vtkm::cont::UnknownArrayHandle.
 1VTKM_CONT
 2vtkm::cont::UnknownArrayHandle LoadUnknownArray(const void* buffer,
 3                                                vtkm::Id length,
 4                                                std::string type)
 5{
 6  vtkm::cont::UnknownArrayHandle handle;
 7  if (type == "float")
 8  {
 9    vtkm::cont::ArrayHandle<vtkm::Float32> concreteArray = vtkm::cont::make_ArrayHandle(
10      reinterpret_cast<const vtkm::Float32*>(buffer), length, vtkm::CopyFlag::On);
11    handle = concreteArray;
12  }
13  else if (type == "int")
14  {
15    vtkm::cont::ArrayHandle<vtkm::Int32> concreteArray = vtkm::cont::make_ArrayHandle(
16      reinterpret_cast<const vtkm::Int32*>(buffer), length, vtkm::CopyFlag::On);
17    handle = concreteArray;
18  }
19  return handle;
20}

It is possible to construct a vtkm::cont::UnknownArrayHandle that does not point to any vtkm::cont::ArrayHandle. In this case, the vtkm::cont::UnknownArrayHandle is considered not “valid.” Validity can be tested with the vtkm::cont::UnknownArrayHandle::IsValid() method.

bool vtkm::cont::UnknownArrayHandle::IsValid() const

Returns whether an array is stored in this UnknownArrayHandle.

If the UnknownArrayHandle is constructed without an ArrayHandle, it will not have an underlying type, and therefore the operations will be invalid. It is still possible to set this UnknownArrayHandle to an ArrayHandle.

Most of the following operations on vtkm::cont::UnknownArrayHandle will fail by throwing an exception if it is not valid. Note that it is also possible for a vtkm::cont::UnknownArrayHandle to contain an empty vtkm::cont::ArrayHandle. A vtkm::cont::UnknownArrayHandle that contains a vtkm::cont::ArrayHandle but has no memory allocated is still considered valid.

Some basic, human-readable information can be retrieved using the vtkm::cont::UnknownArrayHandle::PrintSummary() method. It will print the type and size of the array along with some or all of the values.

void vtkm::cont::UnknownArrayHandle::PrintSummary(std::ostream &out, bool full = false) const

Prints a summary of the array’s type, size, and contents.

3.5.1. Allocation

Data pointed to by an vtkm::cont::UnknownArrayHandle is not directly accessible. However, it is still possible to do some type-agnostic manipulation of the array allocations.

First, it is always possible to call vtkm::cont::UnknownArrayHandle::GetNumberOfValues() to retrieve the current size of the array. It is also possible to call vtkm::cont::UnknownArrayHandle::Allocate() to change the size of an unknown array. vtkm::cont::UnknownArrayHandle’s vtkm::cont::UnknownArrayHandle::Allocate() works exactly the same as the vtkm::cont::ArrayHandle::Allocate() in the basic vtkm::cont::ArrayHandle.

vtkm::Id vtkm::cont::UnknownArrayHandle::GetNumberOfValues() const

Returns the number of values in the array.

void vtkm::cont::UnknownArrayHandle::Allocate(vtkm::Id numValues, vtkm::CopyFlag preserve, vtkm::cont::Token &token) const

Reallocate the data in the array.

The allocation works the same as the Allocate() method of vtkm::cont::ArrayHandle.

void vtkm::cont::UnknownArrayHandle::Allocate(vtkm::Id numValues, vtkm::CopyFlag preserve = vtkm::CopyFlag::Off) const

Reallocate the data in the array.

The allocation works the same as the Allocate() method of vtkm::cont::ArrayHandle.

Example 3.28 Checking the size of a vtkm::cont::ArrayHandle and resizing it.
1  vtkm::cont::UnknownArrayHandle unknownHandle = // ... some valid array
2
3  // Double the size of the array while preserving all the initial values.
4  vtkm::Id originalArraySize = unknownHandle.GetNumberOfValues();
5  unknownHandle.Allocate(originalArraySize * 2, vtkm::CopyFlag::On);

It is often the case where you have an vtkm::cont::UnknownArrayHandle as the input to an operation and you want to generate an output of the same type. To handle this case, use the vtkm::cont::UnknownArrayHandle::NewInstance() method to create a new array of the same type (without having to determine the type).

UnknownArrayHandle vtkm::cont::UnknownArrayHandle::NewInstance() const

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 UnknownArrayHandle for it. This method is convenient when creating output arrays that should be the same type as some input array.

Example 3.29 Creating a new instance of an unknown array handle.
 1  vtkm::cont::UnknownArrayHandle unknownHandle = // ... some valid array
 2
 3  // Double the size of the array while preserving all the initial values.
 4  vtkm::Id originalArraySize = unknownHandle.GetNumberOfValues();
 5  unknownHandle.Allocate(originalArraySize * 2, vtkm::CopyFlag::On);
 6
 7  // Create a new array of the same type as the original.
 8  vtkm::cont::UnknownArrayHandle newArray = unknownHandle.NewInstance();
 9
10  newArray.Allocate(originalArraySize);

That said, there are many special array handles described in Chapter 4.8 (Memory Layout of Array Handles) and Chapter 4.9 (Fancy Array Handles) that either cannot be directly constructed or cannot be used as outputs. Thus, if you do not know the storage of the array, the similar array returned by vtkm::cont::UnknownArrayHandle::NewInstance() could be infeasible for use as an output. Thus, vtkm::cont::UnknownArrayHandle also contains the vtkm::cont::UnknownArrayHandle::NewInstanceBasic() method to create a new array with the same value type but using the basic array storage, which can always be resized and written to.

UnknownArrayHandle vtkm::cont::UnknownArrayHandle::NewInstanceBasic() const

Create a new ArrayHandleBasic with the same ValueType as this array.

This method creates a new ArrayHandleBasic that has the same ValueType as the array held by this one and returns a new UnknownArrayHandle for it. This method is convenient when creating output arrays that should have the same types of values of the input, but the input might not be a writable array.

Example 3.30 Creating a new basic instance of an unknown array handle.
1  vtkm::cont::UnknownArrayHandle indexArray = vtkm::cont::ArrayHandleIndex();
2  // Returns an array of type ArrayHandleBasic<vtkm::Id>
3  vtkm::cont::UnknownArrayHandle basicArray = indexArray.NewInstanceBasic();

It is sometimes the case that you need a new array of a similar type, but that type has to hold floating point values. For example, if you had an operation that computed a discrete cosine transform on an array, the result would be very inaccurate if stored as integers. In this case, you would actually want to store the result in an array of floating point values. For this case, you can use the vtkm::cont::UnknownArrayHandle::NewInstanceFloatBasic() to create a new basic vtkm::cont::ArrayHandle with the component type changed to vtkm::FloatDefault. For example, if the vtkm::cont::UnknownArrayHandle stores an vtkm::cont::ArrayHandle of type vtkm::Id, vtkm::cont::UnknownArrayHandle::NewInstanceFloatBasic() will create an vtkm::cont::ArrayHandle of type vtkm::FloatDefault. If the vtkm::cont::UnknownArrayHandle stores an vtkm::cont::ArrayHandle of type vtkm::Id3, vtkm::cont::UnknownArrayHandle::NewInstanceFloatBasic() will create an vtkm::cont::ArrayHandle of type vtkm::Vec3f.

UnknownArrayHandle vtkm::cont::UnknownArrayHandle::NewInstanceFloatBasic() const

Create a new ArrayHandleBasic with the base component of vtkm::FloatDefault

This method creates a new ArrayHandleBasic that has a ValueType that is similar to the array held by this one except that the base component type is replaced with vtkm::FloatDefault. For example, if the contained array has vtkm::Int32 value types, the returned array will have vtkm::FloatDefault value types. If the contained array has vtkm::Id3 value types, the returned array will have vtkm::Vec3f value types. If the contained array already has vtkm::FloatDefault as the base component (e.g. vtkm::FloatDefault, vtkm::Vec3f, vtkm::Vec<vtkm::Vec2f, 3>), then the value type will be preserved.

The created array is returned in a new UnknownArrayHandle.

This method is used to convert an array of an unknown type to an array of an almost known type.

Example 3.31 Creating a new array instance with floating point values.
1  vtkm::cont::UnknownArrayHandle intArray = vtkm::cont::ArrayHandleIndex();
2  // Returns an array of type ArrayHandleBasic<vtkm::FloatDefault>
3  vtkm::cont::UnknownArrayHandle floatArray = intArray.NewInstanceFloatBasic();
4
5  vtkm::cont::UnknownArrayHandle id3Array = vtkm::cont::ArrayHandle<vtkm::Id3>();
6  // Returns an array of type ArrayHandleBasic<vtkm::Vec3f>
7  vtkm::cont::UnknownArrayHandle float3Array = id3Array.NewInstanceFloatBasic();

Finally, it may be the case where you are finished using a vtkm::cont::UnknownArrayHandle. If you want to free up memory on the device, which may have limited memory, you can do so with vtkm::cont::UnknownArrayHandle::ReleaseResourcesExecution(), which will free any memory on the device but preserve the data on the host. If the data will never be used again, all memory can be freed with vtkm::cont::UnknownArrayHandle::ReleaseResources()

void vtkm::cont::UnknownArrayHandle::ReleaseResourcesExecution() const

Releases any resources being used in the execution environment (that are not being shared by the control environment).

void vtkm::cont::UnknownArrayHandle::ReleaseResources() const

Releases all resources in both the control and execution environments.

3.5.2. Casting to Known Types

Data pointed to by an vtkm::cont::UnknownArrayHandle is not directly accessible. To access the data, you need to retrieve the data as an vtkm::cont::ArrayHandle. If you happen to know (or can guess) the type, you can use the vtkm::cont::UnknownArrayHandle::AsArrayHandle() method to retrieve the array as a specific type.

template<typename T, typename S>
inline void vtkm::cont::UnknownArrayHandle::AsArrayHandle(vtkm::cont::ArrayHandle<T, S> &array) const

Returns this array cast appropriately and stored in the given ArrayHandle type.

Throws a vtkm::cont::ErrorBadType if the stored array cannot be stored in the given array type. Use the CanConvert() method to determine if the array can be returned with the given type.

template<typename ArrayType>
inline ArrayType vtkm::cont::UnknownArrayHandle::AsArrayHandle() const

Returns this array cast appropriately and stored in the given ArrayHandle type.

Throws a vtkm::cont::ErrorBadType if the stored array cannot be stored in the given array type. Use the CanConvert() method to determine if the array can be returned with the given type.

Example 3.32 Retrieving an array of a known type from vtkm::cont::UnknownArrayHandle.
1  vtkm::cont::ArrayHandle<vtkm::Float32> knownArray =
2    unknownArray.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::Float32>>();

vtkm::cont::UnknownArrayHandle::AsArrayHandle() actually has two forms. The first form, shown in the previous example, has no arguments and returns the vtkm::cont::ArrayHandle. This form requires you to specify the type of array as a template parameter. The alternate form has you pass a reference to a concrete vtkm::cont::ArrayHandle as an argument as shown in the following example. This form can imply the template parameter from the argument.

Example 3.33 Alternate form for retrieving an array of a known type from vtkm::cont::UnknownArrayHandle.
1  unknownArray.AsArrayHandle(knownArray);

vtkm::cont::UnknownArrayHandle::AsArrayHandle() treats vtkm::cont::ArrayHandleCast and vtkm::cont::ArrayHandleMultiplexer special. If the special vtkm::cont::ArrayHandle can hold the actual array stored, then vtkm::cont::UnknownArrayHandle::AsArrayHandle() will return successfully. In the following example, vtkm::cont::UnknownArrayHandle::AsArrayHandle() returns an array of type vtkm::Float32 as an vtkm::cont::ArrayHandleCast that converts the values to vtkm::Float64.

Example 3.34 Getting a cast array handle from an vtkm::cont::ArrayHandleCast.
1  vtkm::cont::ArrayHandle<vtkm::Float32> originalArray;
2  vtkm::cont::UnknownArrayHandle unknownArray = originalArray;
3
4  vtkm::cont::ArrayHandleCast<vtkm::Float64, decltype(originalArray)> castArray;
5  unknownArray.AsArrayHandle(castArray);

Did You Know?

The inverse retrieval works as well. If you create an vtkm::cont::UnknownArrayHandle with an vtkm::cont::ArrayHandleCast or vtkm::cont::ArrayHandleMultiplexer, you can get the underlying array with vtkm::cont::UnknownArrayHandle::AsArrayHandle(). These relationships also work recursively (e.g. an array placed in a cast array that is placed in a multiplexer).

If the vtkm::cont::UnknownArrayHandle cannot store its array in the type given to vtkm::cont::UnknownArrayHandle::AsArrayHandle(), it will throw an exception. Thus, you should not use vtkm::cont::UnknownArrayHandle::AsArrayHandle() with types that you are not sure about. Use the vtkm::cont::UnknownArrayHandle::CanConvert() method to determine if a given vtkm::cont::ArrayHandle type will work with vtkm::cont::UnknownArrayHandle::AsArrayHandle().

template<typename ArrayHandleType>
inline bool vtkm::cont::UnknownArrayHandle::CanConvert() const

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.

Example 3.35 Querying whether a given vtkm::cont::ArrayHandle can be retrieved from a vtkm::cont::UnknownArrayHandle.
 1VTKM_CONT vtkm::FloatDefault GetMiddleValue(
 2  const vtkm::cont::UnknownArrayHandle& unknownArray)
 3{
 4  if (unknownArray.CanConvert<vtkm::cont::ArrayHandleConstant<vtkm::FloatDefault>>())
 5  {
 6    // Fast path for known array
 7    vtkm::cont::ArrayHandleConstant<vtkm::FloatDefault> constantArray;
 8    unknownArray.AsArrayHandle(constantArray);
 9    return constantArray.GetValue();
10  }
11  else
12  {
13    // General path
14    auto ranges = vtkm::cont::ArrayRangeCompute(unknownArray);
15    vtkm::Range range = ranges.ReadPortal().Get(0);
16    return static_cast<vtkm::FloatDefault>((range.Min + range.Max) / 2);
17  }
18}

By design, vtkm::cont::UnknownArrayHandle::CanConvert() will return true for types that are not actually stored in the vtkm::cont::UnknownArrayHandle but can be retrieved. If you need to know specifically what type is stored in the vtkm::cont::UnknownArrayHandle, you can use the vtkm::cont::UnknownArrayHandle::IsType() method instead.

template<typename ArrayHandleType>
inline bool vtkm::cont::UnknownArrayHandle::IsType() const

Returns true if this array matches the ArrayHandleType template argument.

Note that UnknownArrayHandle has some special handling for ArrayHandleCast and ArrayHandleMultiplexer. If you stored an array of one of these types into an UnknownArrayHandle, the type of the underlying array will change and IsType() will fail. However, you can still get the array back out as that type using AsArrayHandle.

Use the CanConvert() method instead to determine if the UnknownArrayHandle contains an array that “matches” the array of a given type. Under most circumstances, you should prefer CanConvert() over IsType().

If you need to query either the value type or the storage, you can use vtkm::cont::UnknownArrayHandle::IsValueType() and vtkm::cont::UnknownArrayHandle::IsStorageType(), respectively. vtkm::cont::UnknownArrayHandle also provides vtkm::cont::UnknownArrayHandle::GetValueTypeName(), vtkm::cont::UnknownArrayHandle::GetStorageTypeName(), and vtkm::cont::UnknownArrayHandle::GetArrayTypeName() for debugging purposes.

template<typename ValueType>
inline bool vtkm::cont::UnknownArrayHandle::IsValueType() const

Returns true if this array matches the ValueType template argument.

template<typename StorageType>
inline bool vtkm::cont::UnknownArrayHandle::IsStorageType() const

Returns true if this array matches the StorageType template argument.

std::string vtkm::cont::UnknownArrayHandle::GetValueTypeName() const

Returns the name of the value type stored in the array.

Returns an empty string if no array is stored.

std::string vtkm::cont::UnknownArrayHandle::GetStorageTypeName() const

Returns the name of the storage tag for the array.

Returns an empty string if no array is stored.

std::string vtkm::cont::UnknownArrayHandle::GetArrayTypeName() const

Returns a string representation of the underlying data type.

The returned string will be of the form vtkm::cont::ArrayHandle<T, S> rather than the name of an actual subclass. If no array is stored, an empty string is returned.

Common Errors

vtkm::cont::UnknownArrayHandle::CanConvert() is almost always safer to use than vtkm::cont::UnknownArrayHandle::IsType() or its similar methods. Even though vtkm::cont::UnknownArrayHandle::IsType() reflects the actual array type, vtkm::cont::UnknownArrayHandle::CanConvert() better describes how vtkm::cont::UnknownArrayHandle will behave.

If you do not know the exact type of the array contained in an vtkm::cont::UnknownArrayHandle, a brute force method to get the data out is to copy it to an array of a known type. This can be done with the vtkm::cont::UnknownArrayHandle::DeepCopyFrom() method, which will copy the contents of a target array into an existing array of a (potentially) different type.

void vtkm::cont::UnknownArrayHandle::DeepCopyFrom(const vtkm::cont::UnknownArrayHandle &source)

Deep copies data from another UnknownArrayHandle.

This method takes an UnknownArrayHandle and deep copies data from it.

If this object does not point to an existing ArrayHandle, a new ArrayHandleBasic with the same value type of the source is created.

void vtkm::cont::UnknownArrayHandle::DeepCopyFrom(const vtkm::cont::UnknownArrayHandle &source) const

Deep copies data from another UnknownArrayHandle.

This method takes an UnknownArrayHandle and deep copies data from it.

If this object does not point to an existing ArrayHandle, this const version of DeepCopyFrom() throws an exception.

Example 3.36 Deep copy arrays of unknown types.
1VTKM_CONT vtkm::cont::ArrayHandle<vtkm::FloatDefault> CopyToDefaultArray(
2  const vtkm::cont::UnknownArrayHandle& unknownArray)
3{
4  // Initialize the output UnknownArrayHandle with the array type we want to copy to.
5  vtkm::cont::UnknownArrayHandle output = vtkm::cont::ArrayHandle<vtkm::FloatDefault>{};
6  output.DeepCopyFrom(unknownArray);
7  return output.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::FloatDefault>>();
8}

Did You Know?

The vtkm::cont::UnknownArrayHandle copy methods behave similarly to the vtkm::cont::ArrayCopy() functions.

It is often the case that you have good reason to believe that an array is of an expected type, but you have no way to be sure. To simplify code, the most rational thing to do is to get the array as the expected type if that is indeed what it is, or to copy it to an array of that type otherwise. The vtkm::cont::ArrayCopyShallowIfPossible() does just that.

template<typename T, typename S>
void vtkm::cont::ArrayCopyShallowIfPossible(const vtkm::cont::UnknownArrayHandle source, vtkm::cont::ArrayHandle<T, S> &destination)

Copies from an unknown to a known array type.

Often times you have an array of an unknown type (likely from a data set), and you need it to be of a particular type (or can make a reasonable but uncertain assumption about it being a particular type). You really just want a shallow copy (a reference in a concrete ArrayHandle) if that is possible.

ArrayCopyShallowIfPossible() pulls an array of a specific type from an UnknownArrayHandle. If the type is compatible, it will perform a shallow copy. If it is not possible, a deep copy is performed to get it to the correct type.

Example 3.37 Using vtkm::cont::ArrayCopyShallowIfPossible() to get an unknown array as a particular type.
1VTKM_CONT vtkm::cont::ArrayHandle<vtkm::FloatDefault> GetAsFloatArray(
2  const vtkm::cont::UnknownArrayHandle& unknownArray)
3{
4  vtkm::cont::ArrayHandle<vtkm::FloatDefault> output;
5  vtkm::cont::ArrayCopyShallowIfPossible(unknownArray, output);
6  return output;
7}

vtkm::cont::UnknownArrayHandle also has a method to do a similar shallow copy into it. This method works by setting an array of a particular type into the vtkm::cont::UnknownArrayHandle.

void vtkm::cont::UnknownArrayHandle::CopyShallowIfPossible(const vtkm::cont::UnknownArrayHandle &source)

Attempts a shallow copy of an array or a deep copy if that is not possible.

This method takes an UnknownArrayHandle and attempts to perform a shallow copy. This shallow copy occurs if this object points to an ArrayHandle of the same type or does not point to any ArrayHandle at all. If this is not possible, then the array is deep copied.

This method is roughly equivalent to the vtkm::cont::ArrayCopyShallowIfPossible() function (defined in vtkm/cont/ArrayCopy.h). This form allows you to copy into a type defined elsewhere (and hidden in the UnknownArrayHandle) whereas ArrayCopyShallowIfPossible() must be copied into an ArrayHandle of a known type.

void vtkm::cont::UnknownArrayHandle::CopyShallowIfPossible(const vtkm::cont::UnknownArrayHandle &source) const

Attempts a shallow copy of an array or a deep copy if that is not possible.

This method takes an UnknownArrayHandle and attempts to perform a shallow copy. This shallow copy occurs if this object points to an ArrayHandle of the same type. If the types are incompatible, then the array is deep copied.

If this object does not point to an existing ArrayHandle, this const version of CopyShallowIfPossible() throws an exception.

This method is roughly equivalent to the vtkm::cont::ArrayCopyShallowIfPossible() function (defined in vtkm/cont/ArrayCopy.h). This form allows you to copy into a type defined elsewhere (and hidden in the UnknownArrayHandle) whereas ArrayCopyShallowIfPossible() must be copied into an ArrayHandle of a known type.

Example 3.38 Using vtkm::cont::UnknownArrayHandle::CopyShallowIfPossible() to get an unknown array as a particular type.
1VTKM_CONT vtkm::cont::ArrayHandle<vtkm::FloatDefault> GetAsDefaultArray(
2  const vtkm::cont::UnknownArrayHandle& unknownArray)
3{
4  // Initialize the output UnknownArrayHandle with the array type we want to copy to.
5  vtkm::cont::UnknownArrayHandle output = vtkm::cont::ArrayHandle<vtkm::FloatDefault>{};
6  output.CopyShallowIfPossible(unknownArray);
7  return output.AsArrayHandle<vtkm::cont::ArrayHandle<vtkm::FloatDefault>>();
8}

3.5.3. Casting to a List of Potential Types

Using vtkm::cont::UnknownArrayHandle::AsArrayHandle() is fine as long as the correct types are known, but often times they are not. For this use case vtkm::cont::UnknownArrayHandle has a method named vtkm::cont::UnknownArrayHandle::CastAndCallForTypes() that attempts to cast the array to some set of types.

The vtkm::cont::UnknownArrayHandle::CastAndCallForTypes() method accepts a functor to run on the appropriately cast array. The functor must have an overloaded const parentheses operator that accepts an vtkm::cont::ArrayHandle of the appropriate type. You also have to specify two template parameters that specify a vtkm::List of value types to try and a vtkm::List of storage types to try, respectively. The macros VTKM_DEFAULT_TYPE_LIST and VTKM_DEFAULT_STORAGE_LIST are often used when nothing more specific is known.

template<typename TypeList, typename StorageList, typename Functor, typename ...Args>
inline void vtkm::cont::UnknownArrayHandle::CastAndCallForTypes(Functor &&functor, Args&&... args) const

Call a functor using the underlying array type.

CastAndCallForTypes() 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.

After the functor argument you may add any number of arguments that will be passed to the functor after the converted ArrayHandle.

 1struct PrintArrayContentsFunctor
 2{
 3  template<typename T, typename S>
 4  VTKM_CONT void operator()(const vtkm::cont::ArrayHandle<T, S>& array) const
 5  {
 6    this->PrintArrayPortal(array.ReadPortal());
 7  }
 8
 9private:
10  template<typename PortalType>
11  VTKM_CONT void PrintArrayPortal(const PortalType& portal) const
12  {
13    for (vtkm::Id index = 0; index < portal.GetNumberOfValues(); index++)
14    {
15      // All ArrayPortal objects have ValueType for the type of each value.
16      using ValueType = typename PortalType::ValueType;
17      using VTraits = vtkm::VecTraits<ValueType>;
18
19      ValueType value = portal.Get(index);
20
21      vtkm::IdComponent numComponents = VTraits::GetNumberOfComponents(value);
22      for (vtkm::IdComponent componentIndex = 0; componentIndex < numComponents;
23           componentIndex++)
24      {
25        std::cout << " " << VTraits::GetComponent(value, componentIndex);
26      }
27      std::cout << std::endl;
28    }
29  }
30};
31
32void PrintArrayContents(const vtkm::cont::UnknownArrayHandle& array)
33{
34  array.CastAndCallForTypes<VTKM_DEFAULT_TYPE_LIST, VTKM_DEFAULT_STORAGE_LIST>(
35    PrintArrayContentsFunctor{});
36}

Did You Know?

The first (required) argument to vtkm::cont::UnknownArrayHandle::CastAndCallForTypes() is the functor to call with the array. You can supply any number of optional arguments after that. Those arguments will be passed directly to the functor. This makes it easy to pass state to the functor.

Did You Know?

When an vtkm::cont::UnknownArrayHandle is used in place of an vtkm::cont::ArrayHandle as an argument to a worklet invocation, it will internally use vtkm::cont::UnknownArrayHandle::CastAndCallForTypes() to attempt to call the worklet with an vtkm::cont::ArrayHandle of the correct type.

vtkm::cont::UnknownArrayHandle has a simple subclass named vtkm::cont::UncertainArrayHandle for use when you can narrow the array to a finite set of types. vtkm::cont::UncertainArrayHandle has two template parameters that must be specified: a vtkm::List of value types and a vtkm::List of storage types.

template<typename ValueTypeList, typename StorageTypeList>
class UncertainArrayHandle : public vtkm::cont::UnknownArrayHandle

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::Lists 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 ArrayHandles 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.

vtkm::cont::UncertainArrayHandle has a method named vtkm::cont::UncertainArrayHandle::CastAndCall() that behaves the same as vtkm::cont::UnknownArrayHandle::CastAndCallForTypes() except that you do not have to specify the types to try. Instead, the types are taken from the template parameters of the vtkm::cont::UncertainArrayHandle itself.

template<typename Functor, typename ...Args>
inline void vtkm::cont::UncertainArrayHandle::CastAndCall(Functor &&functor, Args&&... args) const

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.

Example 3.40 Using vtkm::cont::UncertainArrayHandle to cast and call a functor.
1  vtkm::cont::UncertainArrayHandle<vtkm::TypeListScalarAll, vtkm::cont::StorageListBasic>
2    uncertainArray(unknownArray);
3  uncertainArray.CastAndCall(PrintArrayContentsFunctor{});

Did You Know?

Like with vtkm::cont::UnknownArrayHandle, if an vtkm::cont::UncertainArrayHandle is used in a worklet invocation, it will internally use vtkm::cont::UncertainArrayHandle::CastAndCall(). This provides a convenient way to specify what array types the invoker should try.

Both vtkm::cont::UnknownArrayHandle and vtkm::cont::UncertainArrayHandle provide a method named vtkm::cont::UnknownArrayHandle::ResetTypes() to redefine the types to try. vtkm::cont::UncertainArrayHandle::ResetTypes() has two template parameters that are the vtkm::List’s of value and storage types. vtkm::cont::UnknownArrayHandle::ResetTypes() returns a new vtkm::cont::UncertainArrayHandle with the given types. This is a convenient way to pass these types to functions.

template<typename NewValueTypeList, typename NewStorageTypeList>
vtkm::cont::UncertainArrayHandle<NewValueTypeList, NewStorageTypeList> vtkm::cont::UnknownArrayHandle::ResetTypes(NewValueTypeList = NewValueTypeList{}, NewStorageTypeList = NewStorageTypeList{}) const

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 CastAndCalls on the returned object will be constrained to the given types.

vtkm::cont::UncertainArrayHandle additionally has methods named vtkm::cont::UncertainArrayHandle::ResetValueTypes() and vtkm::cont::UncertainArrayHandle::ResetStorageTypes() to reset the value types and storage types, respectively, without modifying the other.

template<typename NewValueTypeList>
inline UncertainArrayHandle<NewValueTypeList, StorageTypeList> vtkm::cont::UncertainArrayHandle::ResetValueTypes(NewValueTypeList = NewValueTypeList{}) const

Like ResetTypes except it only resets the value types.

template<typename NewStorageTypeList>
inline UncertainArrayHandle<ValueTypeList, NewStorageTypeList> vtkm::cont::UncertainArrayHandle::ResetStorageTypes(NewStorageTypeList = NewStorageTypeList{}) const

Like ResetTypes except it only resets the storage types.

Example 3.41 Resetting the types of an vtkm::cont::UnknownArrayHandle.
1  vtkm::cont::Invoker invoke;
2  invoke(
3    MyWorklet{},
4    unknownArray.ResetTypes<vtkm::TypeListScalarAll, vtkm::cont::StorageListBasic>(),
5    outArray);

Common Errors

Because it returns an vtkm::cont::UncertainArrayHandle, you need to include vtkm/cont/UncertainArrayHandle.h if you use vtkm::cont::UnknownArrayHandle::ResetTypes(). This is true even if you do not directly use the returned object.

3.5.4. Accessing Truly Unknown Arrays

So far in Section 3.5.2 (Casting to Known Types) and Section 3.5.3 (Casting to a List of Potential Types) we explored how to access the data in an vtkm::cont::UnknownArrayHandle when you actually know the array type or can narrow down the array type to some finite number of candidates. But what happens if you cannot practically narrow down the types in the vtkm::cont::UnknownArrayHandle? For this case, vtkm::cont::UnknownArrayHandle provides mechanisms for extracting data knowing little or nothing about the types.

3.5.4.1. Cast with Floating Point Fallback

The problem with vtkm::cont::UnknownArrayHandle::CastAndCallForTypes() and vtkm::cont::UncertainArrayHandle::CastAndCall() is that you can only list a finite amount of value types and storage types to try. If you encounter an vtkm::cont::UnknownArrayHandle containing a different vtkm::cont::ArrayHandle type, the cast and call will simply fail. Since the compiler must create a code path for each possible vtkm::cont::ArrayHandle type, it may not even be feasible to list all known types.

vtkm::cont::UnknownArrayHandle::CastAndCallForTypesWithFloatFallback() works around this problem by providing a fallback in case the contained vtkm::cont::ArrayHandle does not match any of the types tried. If none of the types match, then vtkm::cont::UnknownArrayHandle::CastAndCallForTypesWithFloatFallback() will copy the data to a vtkm::cont::ArrayHandle with vtkm::FloatDefault values (or some compatible vtkm::Vec with vtkm::FloatDefault components) and basic storage. It will then attempt to match again with this copied array.

template<typename TypeList, typename StorageList, typename Functor, typename ...Args>
void vtkm::cont::UnknownArrayHandle::CastAndCallForTypesWithFloatFallback(Functor &&functor, Args&&... args) const

Call a functor using the underlying array type with a float cast fallback.

CastAndCallForTypesWithFloatFallback() 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.

After the functor argument you may add any number of arguments that will be passed to the functor after the converted ArrayHandle.

If the underlying array does not match any of the requested array types, the array is copied to a new ArrayHandleBasic with vtkm::FloatDefault components in its value and attempts to cast to those types.

Example 3.42 Cast and call a functor from an vtkm::cont::UnknownArrayHandle with a float fallback.
1  unknownArray.CastAndCallForTypesWithFloatFallback<vtkm::TypeListField,
2                                                    VTKM_DEFAULT_STORAGE_LIST>(
3    PrintArrayContentsFunctor{});

In this case, we do not have to list every possible type because the array will be copied to a known type if nothing matches. Note that when using vtkm::cont::UnknownArrayHandle::CastAndCallForTypesWithFloatFallback(), you still need to include an appropriate type based on vtkm::FloatDefault in the value type list and vtkm::cont::StorageTagBasic in the storage list so that the copied array can match.

vtkm::cont::UncertainArrayHandle has a matching method named vtkm::cont::UncertainArrayHandle::CastAndCallWithFloatFallback() that does the same operation using the types specified in the vtkm::cont::UncertainArrayHandle.

template<typename Functor, typename ...Args>
inline void vtkm::cont::UncertainArrayHandle::CastAndCallWithFloatFallback(Functor &&functor, Args&&... args) const

Call a functor using the underlying array type with a float cast fallback.

CastAndCallWithFloatFallback() attempts to cast the held array to a specific value type, and then calls the given functor with the cast array. If the underlying array does not match any of the requested array types, the array is copied to a new ArrayHandleBasic with vtkm::FloatDefault components in its value and attempts to cast to those types.

Example 3.43 Cast and call a functor from an vtkm::cont::UncertainArrayHandle with a float fallback.
1  uncertainArray.CastAndCallWithFloatFallback(PrintArrayContentsFunctor{});

3.5.4.2. Extracting Components

Using a floating point fallback allows you to use arrays of unknown types in most circumstances, but it does have a few drawbacks. First, and most obvious, is that you may not operate on the data in its native format. If you want to preserve the integer format of data, this may not be the method. Second, the fallback requires a copy of the data. If vtkm::cont::UnknownArrayHandle::CastAndCallForTypesWithFloatFallback() does not match the type of the array, it copies the array to a new type that (hopefully) can be matched. Third, vtkm::cont::UnknownArrayHandle::CastAndCallForTypesWithFloatFallback() still needs to match the number of components in each array value. If the contained vtkm::cont::ArrayHandle contains values that are vtkm::Vec’s of length 2, then the data will be copied to an array of vtkm::Vec2f’s. If vtkm::Vec2f is not included in the types to try, the cast and call will still fail.

A way to get around these problems is to extract a single component from the array. You can use the vtkm::cont::UnknownArrayHandle::ExtractComponent() method to return an vtkm::cont::ArrayHandle with the values for a given component for each value in the array. The type of the returned vtkm::cont::ArrayHandle will be the same regardless of the actual array type stored in the vtkm::cont::UnknownArrayHandle.

template<typename BaseComponentType>
inline vtkm::cont::ArrayHandleStride<BaseComponentType> vtkm::cont::UnknownArrayHandle::ExtractComponent(vtkm::IdComponent componentIndex, vtkm::CopyFlag allowCopy = vtkm::CopyFlag::On) const

Extract a component of the array.

This method returns an array that holds the data for a given flat component of the data. The BaseComponentType has to be specified and must match the contained array (i.e. the result of IsBaseComponentType() must succeed for the given type).

This method treats each value in the array as a flat vtkm::Vec even if it is a vtkm::Vec of Vecs. For example, if the array actually holds values of type vtkm::Vec<vtkm::Vec<T, 3>, 2>, it is treated as if it holds a Vec<T, 6>. See vtkm::VecFlat for details on how vectors are flattened.

The point of using ExtractComponent() over AsArrayHandle() is that it drastically reduces the amount of types you have to try. Most of the time the base component type is one of the basic C types (i.e. int, long, float, etc.). You do not need to know what shape the containing vtkm::Vec is in, nor do you need to know the actual storage of the array.

Note that the type of the array returned is ArrayHandleStride. Using this type of array handle has a slight overhead over basic arrays like ArrayHandleBasic and ArrayHandleSOA.

When extracting a component of an array, a shallow pointer to the data is returned whenever possible. However, in some circumstances it is impossible to conform the array. In these cases, the data are by default copied. If copying the data would cause problems (for example, you are writing into the array), you can select the optional allowCopy flag to vtkm::CopyFlag::Off. In this case, an exception will be thrown if the result cannot be represented by a shallow copy.

vtkm::cont::UnknownArrayHandle::ExtractComponent() must be given a template argument for the base component type. The following example extracts the first component of all vtkm::Vec values in an vtkm::cont::UnknownArrayHandle assuming that the component is of type vtkm::FloatDefault (Example 3.44, line 11).

Example 3.44 Extracting the first component of every value in an vtkm::cont::UnknownArrayHandle.
 1  vtkm::cont::ArrayHandleBasic<vtkm::Vec3f> concreteArray =
 2    vtkm::cont::make_ArrayHandle<vtkm::Vec3f>({ { 0, 1, 2 },
 3                                                { 3, 4, 5 },
 4                                                { 6, 7, 8 },
 5                                                { 9, 10, 11 },
 6                                                { 12, 13, 14 },
 7                                                { 15, 16, 17 } });
 8
 9  vtkm::cont::UnknownArrayHandle unknownArray(concreteArray);
10
11  auto componentArray = unknownArray.ExtractComponent<vtkm::FloatDefault>(0);
12  // componentArray contains [ 0, 3, 6, 9, 12, 15 ].

The code in Example 3.44 works with any array with values based on the default floating point type. If the vtkm::cont::UnknownArrayHandle has an array containing vtkm::FloatDefault, then the returned array has all the same values. If the vtkm::cont::UnknownArrayHandle contains values of type vtkm::Vec3f, then each value in the returned array will be the first component of this array.

If the vtkm::cont::UnknownArrayHandle really contains an array with incompatible value types (such as vtkm::cont::ArrayHandle<vtkm::Id>), then an vtkm::cont::ErrorBadType will be thrown. To check if the vtkm::cont::UnknownArrayHandle contains an array of a compatible type, use the vtkm::cont::UnknownArrayHandle::IsBaseComponentType() method to check the component type being used as the template argument to vtkm::cont::UnknownArrayHandle::ExtractComponent().

template<typename BaseComponentType>
inline bool vtkm::cont::UnknownArrayHandle::IsBaseComponentType() const

Returns true if this array’s ValueType has the provided base component type.

The base component type is the recursive component type of any Vec-like object. So if the array’s ValueType is vtkm::Vec<vtkm::Float32, 3>, then the base component type will be vtkm::Float32. Likewise, if the ValueType is vtkm::Vec<vtkm::Vec<vtkm::Float32, 3>, 2>, then the base component type is still vtkm::Float32.

If the ValueType is not Vec-like type, then the base component type is the same. So a ValueType of vtkm::Float32 has a base component type of vtkm::Float32.

Example 3.45 Checking the base component type in an vtkm::cont::UnknownArrayHandle.
1    unknownArray.IsBaseComponentType<vtkm::FloatDefault>()

it is also possible to get a name for the base component type (mostly for debugging purposes) with vtkm::cont::UnknownArrayHandle::GetBaseComponentTypeName().

std::string vtkm::cont::UnknownArrayHandle::GetBaseComponentTypeName() const

Returns the name of the base component of the value type stored in the array.

Returns an empty string if no array is stored.

You will often need to query the number of components that can be extracted from the array. This can be queried with vtkm::cont::UnknownArrayHandle::GetNumberOfComponentsFlat().

vtkm::IdComponent vtkm::cont::UnknownArrayHandle::GetNumberOfComponentsFlat() const

Returns the total number of components for each value in the array.

If the array holds vtkm::Vec objects, this will return the total number of components in each value assuming the object is flattened out to one level of Vec objects. If the array holds a basic C type (such as float), this will return 1. If the array holds a simple Vec (such as vtkm::Vec3f), this will return the number of components (in this case 3). If the array holds a hierarchy of Vecs (such as vtkm::Vec<vtkm::Vec3f, 2>), this will return the total number of vecs (in this case 6).

If this object is holding an array where the number of components can be selected at runtime (for example, vtkm::cont::ArrayHandleRuntimeVec), this method will still return the correct number of components. However, if each value in the array can be a Vec of a different size (such as vtkm::cont::ArrayHandleGroupVecVariable), this method will return 0 (because there is no consistent answer).

This section started with the motivation of getting data from an vtkm::cont::UnknownArrayHandle without knowing anything about the type, yet vtkm::cont::UnknownArrayHandle::ExtractComponent() still requires a type parameter. However, by limiting the type needed to the base component type, you only need to check the base C types (standard integers and floating points) available in C++. You do not need to know whether these components are arranged in vtkm::Vec’s or the size of the vtkm::Vec. A general implementation of an algorithm might have to deal with scalars as well as vtkm::Vec’s of size 2, 3, and 4. If we consider operations on tensors, vtkm::Vec’s of size 6 and 9 can be common as well. But when using vtkm::cont::UnknownArrayHandle::ExtractComponent(), a single condition can handle any potential vtkm::Vec size.

Another advantage of vtkm::cont::UnknownArrayHandle::ExtractComponent() is that the type of storage does not need to be specified. vtkm::cont::UnknownArrayHandle::ExtractComponent() works with any type of vtkm::cont::ArrayHandle storage (with some caveats). So, Example 3.44 works equally as well with vtkm::cont::ArrayHandleBasic, vtkm::cont::ArrayHandleSOA, vtkm::cont::ArrayHandleUniformPointCoordinates, vtkm::cont::ArrayHandleCartesianProduct, and many others. Trying to capture all reasonable types of arrays could easily require hundreds of conditions, all of which and more can be captured with vtkm::cont::UnknownArrayHandle::ExtractComponent() and the roughly 12 basic C data types. In practice, you often only really have to worry about floating point components, which further reduces the cases down to (usually) 2.

vtkm::cont::UnknownArrayHandle::ExtractComponent() works by returning an vtkm::cont::ArrayHandleStride. This is a special vtkm::cont::ArrayHandle that can access data buffers by skipping values at regular intervals. This allows it to access data packed in different ways such as vtkm::cont::ArrayHandleBasic, vtkm::cont::ArrayHandleSOA, and many others. That said, vtkm::cont::ArrayHandleStride is not magic, so if it cannot directly access memory, some or all of it may be copied. If you are attempting to use the array from vtkm::cont::UnknownArrayHandle::ExtractComponent() as an output array, pass vtkm::CopyFlag::Off as a second argument. This will ensure that data are not copied so that any data written will go to the original array (or throw an exception if this cannot be done).

Common Errors

Although vtkm::cont::UnknownArrayHandle::ExtractComponent() will technically work with any vtkm::cont::ArrayHandle (of simple vtkm::Vec types), it may require a very inefficient memory copy. Pay attention if vtkm::cont::UnknownArrayHandle::ExtractComponent() issues a warning about an inefficient memory copy. This is likely a serious performance issue, and the data should be retrieved in a different way (or better yet stored in a different way).

3.5.4.3. Extracting All Components

Example 3.44 accesses the first component of each vtkm::Vec in an array. But in practice you usually want to operate on all components stored in the array. A simple solution is to iterate over each component.

Example 3.46 Extracting each component from an vtkm::cont::UnknownArrayHandle.
 1  std::vector<vtkm::cont::ArrayHandle<vtkm::FloatDefault>> outputArrays(
 2    static_cast<std::size_t>(unknownArray.GetNumberOfComponentsFlat()));
 3  for (vtkm::IdComponent componentIndex = 0;
 4       componentIndex < unknownArray.GetNumberOfComponentsFlat();
 5       ++componentIndex)
 6  {
 7    invoke(MyWorklet{},
 8           unknownArray.ExtractComponent<vtkm::FloatDefault>(componentIndex),
 9           outputArrays[static_cast<std::size_t>(componentIndex)]);
10  }

To ensure that the type of the extracted component is a basic C type, the vtkm::Vec values are “flattened.” That is, they are treated as if they are a single level vtkm::Vec. For example, if you have a value type of vtkm::Vec<vtkm::Id3, 2>, vtkm::cont::UnknownArrayHandle::ExtractComponent() treats this type as vtkm::Vec<vtkm::Id, 6>. This allows you to extract the components as type vtkm::Id rather than having a special case for vtkm::Id3.

Although iterating over components works fine, it can be inconvenient. An alternate mechanism is to use vtkm::cont::UnknownArrayHandle::ExtractArrayFromComponents() to get all the components at once. vtkm::cont::UnknownArrayHandle::ExtractArrayFromComponents() works like vtkm::cont::UnknownArrayHandle::ExtractComponent() except that instead of returning an vtkm::cont::ArrayHandleStride, it returns a special vtkm::cont::ArrayHandleRecombineVec that behaves like an vtkm::cont::ArrayHandle to reference all component arrays at once.

template<typename BaseComponentType>
inline vtkm::cont::ArrayHandleRecombineVec<BaseComponentType> vtkm::cont::UnknownArrayHandle::ExtractArrayFromComponents(vtkm::CopyFlag allowCopy = vtkm::CopyFlag::On) const

Extract the array knowing only the component type of the array.

This method returns an ArrayHandle that points to the data in the array. This method differs from AsArrayHandle() because you do not need to know the exact ValueType and StorageTag of the array. Instead, you only need to know the base component type.

ExtractArrayFromComponents() works by calling the ExtractComponent() method and then combining them together in a fancy ArrayHandle. This allows you to ignore the storage type of the underlying array as well as any Vec structure of the value type. However, it also places some limitations on how the data can be pulled from the data.

First, you have to specify the base component type. This must match the data in the underlying array (as reported by IsBaseComponentType()).

Second, the array returned will have the Vecs flattened. For example, if the underlying array has a ValueType of vtkm::Vec<vtkm::Vec<T, 3>, 3>, then this method will treat the data as if it was vtkm::Vec<T, 9>. There is no way to get an array with vtkm::Vec of vtkm::Vec values.

Third, because the Vec length of the values in the returned ArrayHandle must be determined at runtime, that can break many assumptions of using vtkm::Vec objects. The type is not going to be a vtkm::Vec<T,N> type but rather an internal class that is intended to behave like that. The type should behave mostly like a vtkm::Vec, but will have some differences that can lead to unexpected behavior. For example, this Vec-like object will not have a NUM_COMPONENTS constant static expression because it is not known at compile time. (Use the GetNumberOfComponents() method instead.) And for the same reason you will not be able to pass these objects to classes overloaded or templated on the Vec type. Also, these Vec-like objects cannot be created as new instances. Thus, you will likely have to iterate over all components rather than do operations on the whole Vec.

Fourth, because ExtractArrayFromComponents() uses ExtractComponent() to pull data from the array (which in turn uses ArrayExtractComponent()), there are some ArrayHandle types that will require copying data to a new array. This could be problematic in cases where you want to write to the array. To prevent data from being copied, set the optional allowCopy to vtkm::CopyFlag::Off. This will cause an exception to be thrown if the resulting array cannot reference the memory held in this UnknownArrayHandle.

Fifth, component arrays are extracted using ArrayHandleStride as the representation for each component. This array adds a slight overhead for each lookup as it performs the arithmetic for finding the index of each component.

Example 3.47 Extracting all components from an vtkm::cont::UnknownArrayHandle at once.
1  invoke(MyWorklet{},
2         unknownArray.ExtractArrayFromComponents<vtkm::FloatDefault>(),
3         outArray);

Common Errors

Although it has the same interface as other vtkm::cont::ArrayHandle’s, vtkm::cont::ArrayHandleRecombineVec has a special value type that breaks some conventions. For example, when used in a worklet, the value type passed from this array to the worklet cannot be replicated. That is, you cannot create a temporary stack value of the same type.

Because you still need to specify a base component type, you will likely still need to check several types to safely extract data from an vtkm::cont::UnknownArrayHandle by component. To do this automatically, you can use the vtkm::cont::UnknownArrayHandle::CastAndCallWithExtractedArray(). This method behaves similarly to vtkm::cont::UncertainArrayHandle::CastAndCall() except that it internally uses vtkm::cont::UnknownArrayHandle::ExtractArrayFromComponents().

template<typename Functor, typename ...Args>
inline void vtkm::cont::UnknownArrayHandle::CastAndCallWithExtractedArray(Functor &&functor, Args&&... args) const

Call a functor on an array extracted from the components.

CastAndCallWithExtractedArray() behaves similarly to CastAndCallForTypes(). It converts the contained data to an ArrayHandle and calls a functor with that ArrayHandle (and any number of optionally specified arguments).

The advantage of CastAndCallWithExtractedArray() is that you do not need to specify any TypeList or StorageList. Instead, it internally uses ExtractArrayFromComponents() to work with most ArrayHandle types with only about 10 instances of the functor. In contrast, calling CastAndCallForTypes() with, for example, VTKM_DEFAULT_TYPE_LIST and VTKM_DEFAULT_STORAGE_LIST results in many more instances of the functor but handling many fewer types of ArrayHandle.

There are, however, costs to using this method. Details of these costs are documented for the ExtractArrayFromComponents() method, but briefly they are that Vec types get flattened, the resulting array has a strange Vec-like value type that has many limitations on its use, there is an overhead for retrieving each value from the array, and there is a potential that data must be copied.

Example 3.48 Calling a functor for nearly any type of array stored in an vtkm::cont::UnknownArrayHandle.
1  unknownArray.CastAndCallWithExtractedArray(PrintArrayContentsFunctor{});

3.5.5. Mutability

One subtle feature of vtkm::cont::UnknownArrayHandle is that the class is, in principle, a pointer to an array pointer. This means that the data in an vtkm::cont::UnknownArrayHandle is always mutable even if the class is declared const. The upshot is that you can pass output arrays as constant vtkm::cont::UnknownArrayHandle references.

Example 3.49 Using a const vtkm::cont::UnknownArrayHandle for a function output.
1void IndexInitialize(vtkm::Id size, const vtkm::cont::UnknownArrayHandle& output)
2{
3  vtkm::cont::ArrayHandleIndex input(size);
4  output.DeepCopyFrom(input);
5}

Although it seems strange, there is a good reason to allow an output vtkm::cont::UnknownArrayHandle to be const. It allows a typed vtkm::cont::ArrayHandle to be used as the argument to the function. In this case, the compiler will automatically convert the vtkm::cont::ArrayHandle to a vtkm::cont::UnknownArrayHandle. When C++ creates objects like this, they can only be passed a constant reference, an Rvalue reference, or by value. So, declaring the output parameter as const vtkm::cont::UnknownArrayHandle allows it to be used for code like this.

Example 3.50 Passing an vtkm::cont::ArrayHandle as an output vtkm::cont::UnknownArrayHandle.
1template<typename T>
2void Foo(const vtkm::cont::ArrayHandle<T>& input, vtkm::cont::ArrayHandle<T>& output)
3{
4  IndexInitialize(input.GetNumberOfValues(), output);
5  // ...

Of course, you could also declare the output by value instead of by reference, but this has the same semantics with extra internal pointer management.

Did You Know?

When possible, it is better to pass a vtkm::cont::UnknownArrayHandle as a constant reference (or by value) rather than a mutable reference, even if the array contents are going to be modified. This allows the function to support automatic conversion of an output vtkm::cont::ArrayHandle.

So if a constant vtkm::cont::UnknownArrayHandle can have its contents modified, what is the difference between a constant reference and a non-constant reference? The difference is that the constant reference can change the array’s content, but not the array itself. If you want to do operations like doing a shallow copy or changing the underlying type of the array, a non-constant reference is needed.