4.1. Advanced Types

Chapter 2.1 (Base Types) introduced some of the base data types defined for use in Viskores. However, for simplicity Chapter Chapter 2.1 (Base Types) just briefly touched the high-level concepts of these types. In this chapter we dive into much greater depth and introduce several more types.

4.1.1. Single Number Types

As described in Chapter Chapter 2.1 (Base Types), Viskores provides aliases for all the base C types to ensure the representation matches the variable use. When a specific type width is not required, then the most common types to use are viskores::FloatDefault for floating-point numbers, viskores::Id for array and similar indices, and viskores::IdComponent for shorter-width vector indices.

If a specific type width is desired, then one of the following is used to clearly declare the type and width.

bytes

floating point

signed integer

unsigned integer

1

viskores::Int8

viskores::UInt8

2

viskores::Int16

viskores::UInt16

4

viskores::Float32

viskores::Int32

viskores::UInt32

8

viskores::Float64

viskores::Int64

viskores::UInt64

These Viskores–defined types should be preferred over basic C types like int or float.

4.1.2. Vector Types

Visualization algorithms also often require operations on short vectors. Arrays indexed in up to three dimensions are common. Data are often defined in 2-space and 3-space, and transformations are typically done in homogeneous coordinates of length 4. To simplify these types of operations, Viskores provides the viskores::Vec templated type, which is essentially a fixed length array of a given type.

template<typename T, viskores::IdComponent Size>
class Vec : public viskores::detail::VecBase<T, Size, Vec<T, Size>>

A short fixed-length array.

The Vec templated class holds a short array of values of a size and type specified by the template arguments.

The Vec class is most often used to represent vectors in the mathematical sense as a quantity with a magnitude and direction. Vectors are, of course, used extensively in computational geometry as well as physical simulations. The Vec class can be (and is) repurposed for more general usage of holding a fixed-length sequence of objects.

There is no real limit to the size of the sequence (other than the largest number representable by viskores::IdComponent), but the Vec class is really designed for small sequences (seldom more than 10).

Subclassed by viskores::VecFlat< T, false >

The default constructor of viskores::Vec objects leaves the values uninitialized. All vectors have a constructor with one argument that is used to initialize all components. All viskores::Vec objects also have a constructor that allows you to set the individual components (one per argument). All viskores::Vec objects with a size that is greater than 4 are constructed at run time and support an arbitrary number of initial values. Likewise, there is a viskores::make_Vec() convenience function that builds initialized vector types with an arbitrary number of components. Once created, you can use the bracket operator to get and set component values with the same syntax as an array.

Example 4.1 Creating vector types.
1  viskores::Vec3f_32 A{ 1 };                          // A is (1, 1, 1)
2  A[1] = 2;                                           // A is now (1, 2, 1)
3  viskores::Vec3f_32 B{ 1, 2, 3 };                    // B is (1, 2, 3)
4  viskores::Vec3f_32 C = viskores::make_Vec(3, 4, 5); // C is (3, 4, 5)
5  // Longer Vecs specified with template.
6  viskores::Vec<viskores::Float32, 5> D{ 1 };                 // D is (1, 1, 1, 1, 1)
7  viskores::Vec<viskores::Float32, 5> E{ 1, 2, 3, 4, 5 };     // E is (1, 2, 3, 4, 5)
8  viskores::Vec<viskores::Float32, 5> F = { 6, 7, 8, 9, 10 }; // F is (6, 7, 8, 9, 10)
9  auto G = viskores::make_Vec(1, 3, 5, 7, 9);                 // G is (1, 3, 5, 7, 9)
template<typename T, typename ...Ts>
constexpr viskores::Vec<T, viskores::IdComponent(sizeof...(Ts) + 1)> viskores::make_Vec(T value0, Ts&&... args)

Initializes and returns a Vec containing all the arguments.

The arguments should all be the same type or compile issues will occur.

The types viskores::Id2, viskores::Id3, and viskores::Id4 are type aliases of viskores::Vec<viskores::Id,2>, viskores::Vec<viskores::Id,3>, and viskores::Vec<viskores::Id,4>, respectively. These are used to index arrays of 2, 3, and 4 dimensions, which is common. Likewise, viskores::IdComponent2, viskores::IdComponent3, and viskores::IdComponent4 are type aliases of viskores::Vec<viskores::IdComponent,2>, viskores::Vec<viskores::IdComponent,3>, and viskores::Vec<viskores::IdComponent,4>, respectively.

Because declaring viskores::Vec with all of its template parameters can be cumbersome, Viskores provides easy to use aliases for small vectors of base types. As introduced in Section 2.1.3 (Vector Types), the following type aliases are available.

bytes

size

floating point

signed integer

unsigned integer

1

2

viskores::Vec2i_8

viskores::Vec2ui_8

3

viskores::Vec3i_8

viskores::Vec3ui_8

4

viskores::Vec4i_8

viskores::Vec4ui_8

2

2

viskores::Vec2i_16

viskores::Vec2ui_16

3

viskores::Vec3i_16

viskores::Vec3ui_16

4

viskores::Vec4i_16

viskores::Vec4ui_16

4

2

viskores::Vec2f_32

viskores::Vec2i_32

viskores::Vec2ui_32

3

viskores::Vec3f_32

viskores::Vec3i_32

viskores::Vec3ui_32

4

viskores::Vec4f_32

viskores::Vec4i_32

viskores::Vec4ui_32

8

2

viskores::Vec2f_64

viskores::Vec2i_64

viskores::Vec2ui_64

3

viskores::Vec3f_64

viskores::Vec3i_64

viskores::Vec3ui_64

4

viskores::Vec4f_64

viskores::Vec4i_64

viskores::Vec4ui_64

viskores::Vec supports component-wise arithmetic using the operators for plus (+), minus (-), multiply (*), and divide (/). It also supports scalar to vector multiplication with the multiply operator. The comparison operators equal (==) is true if every pair of corresponding components are true and not equal (!=) is true otherwise. A special viskores::Dot() function is overloaded to provide a dot product for every type of vector.

Example 4.2 Vector operations.
 1  viskores::Vec3f_32 A{ 1, 2, 3 };
 2  viskores::Vec3f_32 B{ 4, 5, 6.5 };
 3  viskores::Vec3f_32 C = A + B;                 // C is (5, 7, 9.5)
 4  viskores::Vec3f_32 D = 2.0f * C;              // D is (10, 14, 19)
 5  viskores::Float32 s = viskores::Dot(A, B);    // s is 33.5
 6  bool b1 = (A == B);                           // b1 is false
 7  bool b2 = (A == viskores::make_Vec(1, 2, 3)); // b2 is true
 8
 9  viskores::Vec<viskores::Float32, 5> E{ 1, 2.5, 3, 4, 5 };    // E is (1, 2, 3, 4, 5)
10  viskores::Vec<viskores::Float32, 5> F{ 6, 7, 8.5, 9, 10.5 }; // F is (6, 7, 8, 9, 10)
11  viskores::Vec<viskores::Float32, 5> G = E + F; // G is (7, 9.5, 11.5, 13, 15.5)
12  bool b3 = (E == F);                            // b3 is false
13  bool b4 = (G == viskores::make_Vec(7.f, 9.5f, 11.5f, 13.f, 15.5f)); // b4 is true

These operators, of course, only work if they are also defined for the component type of the viskores::Vec. For example, the multiply operator will work fine on objects of type viskores::Vec<char,3>, but the multiply operator will not work on objects of type viskores::Vec<std::string,3> because you cannot multiply objects of type std::string.

In addition to generalizing vector operations and making arbitrarily long vectors, viskores::Vec can be repurposed for creating any sequence of homogeneous objects. Here is a simple example of using viskores::Vec to hold the state of a polygon.

Example 4.3 Repurposing a viskores::Vec.
1  viskores::Vec<viskores::Vec2f_32, 3> equilateralTriangle = { { 0.0f, 0.0f },
2                                                               { 1.0f, 0.0f },
3                                                               { 0.5f, 0.8660254f } };

4.1.2.1. Vec-like Types

The viskores::Vec class provides a convenient structure for holding and passing small vectors of data. However, there are times when using viskores::Vec is inconvenient or inappropriate. For example, the size of viskores::Vec must be known at compile time, but there may be need for a vector whose size is unknown until compile time. Also, the data populating a viskores::Vec might come from a source that makes it inconvenient or less efficient to construct a viskores::Vec. For this reason, Viskores also provides several Vec-like objects that behave much like viskores::Vec but are a different class. These Vec-like objects have the same interface as viskores::Vec except that the NUM_COMPONENTS constant is not available on those that are sized at run time. Vec-like objects also come with a CopyInto method that will take their contents and copy them into a standard viskores::Vec class. (The standard viskores::Vec class also has a viskores::Vec::CopyInto() method for consistency.)

4.1.2.1.1. C-Array Vec Wrapper

The first Vec-like object is viskores::VecC, which exposes a C-type array as a viskores::Vec.

template<typename T>
class VecC : public viskores::detail::VecCBase<T, VecC<T>>

A Vec-like representation for short arrays.

The VecC class takes a short array of values and provides an interface that mimics Vec. This provides a mechanism to treat C arrays like a Vec. It is useful in situations where you want to use a Vec but the data must come from elsewhere or in certain situations where the size cannot be determined at compile time. In particular, Vec objects of different sizes can potentially all be converted to a VecC of the same type.

Note that VecC holds a reference to an outside array given to it. If that array gets destroyed (for example because the source goes out of scope), the behavior becomes undefined.

You cannot use VecC with a const type in its template argument. For example, you cannot declare VecC<const viskores::Id>. If you want a non-mutable VecC, the VecCConst class (e.g. VecCConst<viskores::Id>).

The constructor for viskores::VecC takes a C array and a size of that array. There is also a constant version of viskores::VecC named viskores::VecCConst, which takes a constant array and cannot be mutated.

template<typename T>
class VecCConst : public viskores::detail::VecCBase<T, VecCConst<T>>

A const version of VecC.

VecCConst is a non-mutable form of VecC. It can be used in place of VecC when a constant array is available.

A VecC can be automatically converted to a VecCConst, but not vice versa, so function arguments should use VecCConst when the data do not need to be changed.

The viskores/Types.h header defines both viskores::VecC and viskores::VecCConst as well as multiple versions of viskores::make_VecC() to easily convert a C array to either a viskores::VecC or viskores::VecCConst.

template<typename T>
static inline constexpr viskores::VecC<T> viskores::make_VecC(T *array, viskores::IdComponent size)

Creates a VecC from an input array.

template<typename T>
static inline constexpr viskores::VecCConst<T> viskores::make_VecC(const T *array, viskores::IdComponent size)

Creates a VecCConst from a constant input array.

The following example demonstrates converting values from a constant table into a viskores::VecCConst for further consumption. The table and associated methods define how 8 points come together to form a hexahedron.

Example 4.4 Using viskores::VecCConst with a constant array.
 1VISKORES_EXEC viskores::VecCConst<viskores::IdComponent> HexagonIndexToIJK(
 2  viskores::IdComponent index)
 3{
 4  static const viskores::IdComponent HexagonIndexToIJKTable[8][3] = {
 5    { 0, 0, 0 }, { 1, 0, 0 }, { 1, 1, 0 }, { 0, 1, 0 },
 6    { 0, 0, 1 }, { 1, 0, 1 }, { 1, 1, 1 }, { 0, 1, 1 }
 7  };
 8
 9  return viskores::make_VecC(HexagonIndexToIJKTable[index], 3);
10}
11
12VISKORES_EXEC viskores::IdComponent HexagonIJKToIndex(
13  viskores::VecCConst<viskores::IdComponent> ijk)
14{
15  static const viskores::IdComponent HexagonIJKToIndexTable[2][2][2] = {
16    {
17      // i=0
18      { 0, 4 }, // j=0
19      { 3, 7 }, // j=1
20    },
21    {
22      // i=1
23      { 1, 5 }, // j=0
24      { 2, 6 }, // j=1
25    }
26  };
27
28  return HexagonIJKToIndexTable[ijk[0]][ijk[1]][ijk[2]];
29}

Common Errors

The viskores::VecC and viskores::VecCConst classes only hold a pointer to a buffer that contains the data. They do not manage the memory holding the data. Thus, if the pointer given to viskores::VecC or viskores::VecCConst becomes invalid, then using the object becomes invalid. Make sure that the scope of the viskores::VecC or viskores::VecCConst does not outlive the scope of the data it points to.

4.1.2.1.2. Variable-Sized Vec

The next Vec-like object is viskores::VecVariable, which provides a Vec-like object that can be resized at run time to a maximum value. Unlike viskores::VecC, viskores::VecVariable holds its own memory, which makes it a bit safer to use. But also unlike viskores::VecC, you must define the maximum size of viskores::VecVariable at compile time. Thus, viskores::VecVariable is really only appropriate to use when there is a predetermined limit to the vector size that is fairly small.

template<typename T, viskores::IdComponent MaxSize>
class VecVariable

A short variable-length array with maximum length.

The VecVariable class is a Vec-like class that holds a short array of some maximum length. To avoid dynamic allocations, the maximum length is specified at compile time. Internally, VecVariable holds a Vec of the maximum length and exposes a subsection of it.

The following example uses a viskores::VecVariable to store the trace of edges within a hexahedron. This example uses the methods defined in Example 4.5.

Example 4.5 Using viskores::VecVariable.
 1viskores::VecVariable<viskores::IdComponent, 4> HexagonShortestPath(
 2  viskores::IdComponent startPoint,
 3  viskores::IdComponent endPoint)
 4{
 5  viskores::VecCConst<viskores::IdComponent> startIJK = HexagonIndexToIJK(startPoint);
 6  viskores::VecCConst<viskores::IdComponent> endIJK = HexagonIndexToIJK(endPoint);
 7
 8  viskores::IdComponent3 currentIJK;
 9  startIJK.CopyInto(currentIJK);
10
11  viskores::VecVariable<viskores::IdComponent, 4> path;
12  path.Append(startPoint);
13  for (viskores::IdComponent dimension = 0; dimension < 3; dimension++)
14  {
15    if (currentIJK[dimension] != endIJK[dimension])
16    {
17      currentIJK[dimension] = endIJK[dimension];
18      path.Append(HexagonIJKToIndex(currentIJK));
19    }
20  }
21
22  return path;
23}

4.1.2.1.3. Vecs from Portals

Viskores provides further examples of Vec-like objects as well. For example, the viskores::VecFromPortal and viskores::VecFromPortalPermute objects allow you to treat a subsection of an arbitrarily large array as a viskores::Vec. These objects work by attaching to array portals, which are described in Section 3.2.4 (Array Portals).

template<typename PortalType>
class VecFromPortal

A short variable-length array from a window in an ArrayPortal.

The VecFromPortal class is a Vec-like class that holds an array portal and exposes a small window of that portal as if it were a Vec.

template<typename IndexVecType, typename PortalType>
class VecFromPortalPermute

A short vector from an ArrayPortal and a vector of indices.

The VecFromPortalPermute class is a Vec-like class that holds an array portal and a second Vec-like containing indices into the array. Each value of this vector is the value from the array with the respective index.

4.1.2.1.4. Point Coordinate Vec

Another example of a Vec-like object is viskores::VecRectilinearPointCoordinates, which efficiently represents the point coordinates in an axis-aligned hexahedron. Such shapes are common in structured grids. These and other data sets are described in Chapter 2.4 (Data Sets).

4.1.3. Range

Viskores provides a convenience structure named viskores::Range to help manage a range of values. The viskores::Range struct contains two data members, viskores::Range::Min and viskores::Range::Max, which represent the ends of the range of numbers. viskores::Range::Min and viskores::Range::Max are both of type viskores::Float64. viskores::Range::Min and viskores::Range::Max can be directly accessed, but viskores::Range also comes with several helper functions to make it easier to build and use ranges. Note that all of these functions treat the minimum and maximum value as inclusive to the range.

struct Range

Represent a continuous scalar range of values.

viskores::Range is a helper class for representing a range of floating point values from a minimum value to a maximum value. This is specified simply enough with a Min and Max value.

Range also contains several helper functions for computing and maintaining the range.

Public Functions

inline Range()

Construct a range with a given minimum and maximum.

If no minimum or maximum is given, the range will be empty.

inline bool IsNonEmpty() const

Determine if the range is valid (i.e.

has at least one valid point).

IsNonEmpty return true if the range contains some valid values between Min and Max. If Max is less than Min, then no values satisfy the range and IsNonEmpty returns false. Otherwise, return true.

IsNonEmpty assumes Min and Max are inclusive. That is, if they are equal then true is returned.

template<typename T>
inline bool Contains(const T &value) const

Determines if a value is within the range.

Contains returns true if the give value is within the range, false otherwise. Contains treats the min and max as inclusive. That is, if the value is exactly the min or max, true is returned.

inline viskores::Float64 Length() const

Returns the length of the range.

Length computes the distance between the min and max. If the range is empty, 0 is returned.

inline viskores::Float64 Center() const

Returns the center of the range.

Center computes the middle value of the range. If the range is empty, NaN is returned.

template<typename T>
inline void Include(const T &value)

Expand range to include a value.

This version of Include expands the range just enough to include the given value. If the range already includes this value, then nothing is done.

inline void Include(const viskores::Range &range)

Expand range to include other range.

This version of Include expands this range just enough to include that of another range. Essentially it is the union of the two ranges.

inline viskores::Range Union(const viskores::Range &otherRange) const

Return the union of this and another range.

This is a nondestructive form of Include.

inline viskores::Range Intersection(const viskores::Range &otherRange) const

Return the intersection of this and another range.

inline viskores::Range operator+(const viskores::Range &otherRange) const

Operator for union

Public Members

viskores::Float64 Min

The minumum value of the range (inclusive).

viskores::Float64 Max

Tha maximum value of the range (inclusive).

The following example demonstrates the operation of viskores::Range.

Example 4.6 Using viskores::Range.
 1  viskores::Range range;        // default constructor is empty range
 2  bool b1 = range.IsNonEmpty(); // b1 is false
 3
 4  range.Include(0.5);            // range now is [0.5 .. 0.5]
 5  bool b2 = range.IsNonEmpty();  // b2 is true
 6  bool b3 = range.Contains(0.5); // b3 is true
 7  bool b4 = range.Contains(0.6); // b4 is false
 8
 9  range.Include(2.0);            // range is now [0.5 .. 2]
10  bool b5 = range.Contains(0.5); // b3 is true
11  bool b6 = range.Contains(0.6); // b4 is true
12
13  range.Include(viskores::Range(-1, 1)); // range is now [-1 .. 2]
14
15  range.Include(viskores::Range(3, 4)); // range is now [-1 .. 4]
16
17  viskores::Float64 lower = range.Min;       // lower is -1
18  viskores::Float64 upper = range.Max;       // upper is 4
19  viskores::Float64 length = range.Length(); // length is 5
20  viskores::Float64 center = range.Center(); // center is 1.5

4.1.4. Bounds

Viskores provides a convenience structure named viskores::Bounds to help manage an axis-aligned region in 3D space. Among other things, this structure is often useful for representing a bounding box for geometry. The viskores::Bounds struct contains three data members, viskores::Bounds::X, viskores::Bounds::Y, and viskores::Bounds::Z, which represent the range of the bounds along each respective axis. All three of these members are of type viskores::Range, which is discussed previously in Section 4.1.3 (Range). viskores::Bounds::X, viskores::Bounds::Y, and viskores::Bounds::Z can be directly accessed, but viskores::Bounds also comes with the following helper functions to make it easier to build and use ranges.

struct Bounds

Represent an axis-aligned 3D bounds in space.

viskores::Bounds is a helper class for representing the axis-aligned box representing some region in space. The typical use of this class is to express the containing box of some geometry. The box is specified as ranges in the x, y, and z directions.

Bounds also contains several helper functions for computing and maintaining the bounds.

Public Functions

inline Bounds()

Construct an empty bounds.

The bounds will represent no space until otherwise modified.

inline Bounds(const viskores::Range &xRange, const viskores::Range &yRange, const viskores::Range &zRange)

Construct a bounds with a given range in the x, y, and z dimensions.

template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
inline Bounds(const T1 &minX, const T2 &maxX, const T3 &minY, const T4 &maxY, const T5 &minZ, const T6 &maxZ)

Construct a bounds with the minimum and maximum coordinates in the x, y, and z directions.

template<typename T>
inline explicit Bounds(const T bounds[6])

Initialize bounds with an array of 6 values in the order xmin, xmax, ymin, ymax, zmin, zmax.

template<typename T>
inline Bounds(const viskores::Vec<T, 3> &minPoint, const viskores::Vec<T, 3> &maxPoint)

Initialize bounds with the minimum corner point and the maximum corner point.

inline bool IsNonEmpty() const

Determine if the bounds are valid (i.e.

has at least one valid point).

IsNonEmpty returns true if the bounds contain some valid points. If the bounds are any real region, even if a single point or it expands to infinity, true is returned.

template<typename T>
inline bool Contains(const viskores::Vec<T, 3> &point) const

Determines if a point coordinate is within the bounds.

inline viskores::Float64 Volume() const

Returns the volume of the bounds.

Volume computes the product of the lengths of the ranges in each dimension. If the bounds are empty, 0 is returned.

inline viskores::Float64 Area() const

Returns the area of the bounds in the X-Y-plane.

Area computes the product of the lengths of the ranges in dimensions X and Y. If the bounds are empty, 0 is returned.

inline viskores::Vec3f_64 Center() const

Returns the center of the range.

Center computes the point at the middle of the bounds. If the bounds are empty, the results are undefined.

inline viskores::Vec3f_64 MinCorner() const

Returns the min point of the bounds

MinCorder returns the minium point of the bounds.If the bounds are empty, the results are undefined.

inline viskores::Vec3f_64 MaxCorner() const

Returns the max point of the bounds

MaxCorder returns the minium point of the bounds.If the bounds are empty, the results are undefined.

template<typename T>
inline void Include(const viskores::Vec<T, 3> &point)

Expand bounds to include a point.

This version of Include expands the bounds just enough to include the given point coordinates. If the bounds already include this point, then nothing is done.

inline void Include(const viskores::Bounds &bounds)

Expand bounds to include other bounds.

This version of Include expands these bounds just enough to include that of another bounds. Essentially it is the union of the two bounds.

inline viskores::Bounds Union(const viskores::Bounds &otherBounds) const

Return the union of this and another bounds.

This is a nondestructive form of Include.

inline viskores::Bounds Intersection(const viskores::Bounds &otherBounds) const

Return the intersection of this and another range.

inline viskores::Bounds operator+(const viskores::Bounds &otherBounds) const

Operator for union

Public Members

viskores::Range X

The range of values in the X direction.

The viskores::Range struct provides the minimum and maximum along that axis.

viskores::Range Y

The range of values in the Y direction.

The viskores::Range struct provides the minimum and maximum along that axis.

viskores::Range Z

The range of values in the Z direction.

The viskores::Range struct provides the minimum and maximum along that axis.

The following example demonstrates the operation of viskores::Bounds.

Example 4.7 Using viskores::Bounds.
 1  viskores::Bounds bounds;       // default constructor makes empty
 2  bool b1 = bounds.IsNonEmpty(); // b1 is false
 3
 4  bounds.Include(viskores::make_Vec(0.5, 2.0, 0.0));            // bounds contains only
 5                                                                // the point [0.5, 2, 0]
 6  bool b2 = bounds.IsNonEmpty();                                // b2 is true
 7  bool b3 = bounds.Contains(viskores::make_Vec(0.5, 2.0, 0.0)); // b3 is true
 8  bool b4 = bounds.Contains(viskores::make_Vec(1, 1, 1));       // b4 is false
 9  bool b5 = bounds.Contains(viskores::make_Vec(0, 0, 0));       // b5 is false
10
11  bounds.Include(viskores::make_Vec(4, -1, 2)); // bounds is region [0.5 .. 4] in X,
12                                                //                  [-1 .. 2] in Y,
13                                                //              and [0 .. 2] in Z
14  bool b6 = bounds.Contains(viskores::make_Vec(0.5, 2.0, 0.0)); // b6 is true
15  bool b7 = bounds.Contains(viskores::make_Vec(1, 1, 1));       // b7 is true
16  bool b8 = bounds.Contains(viskores::make_Vec(0, 0, 0));       // b8 is false
17
18  viskores::Bounds otherBounds(viskores::make_Vec(0, 0, 0), viskores::make_Vec(3, 3, 3));
19  // otherBounds is region [0 .. 3] in X, Y, and Z
20  bounds.Include(otherBounds); // bounds is now region [0 .. 4] in X,
21                               //                      [-1 .. 3] in Y,
22                               //                  and [0 .. 3] in Z
23
24  viskores::Vec3f_64 lower(bounds.X.Min, bounds.Y.Min, bounds.Z.Min);
25  // lower is [0, -1, 0]
26  viskores::Vec3f_64 upper(bounds.X.Max, bounds.Y.Max, bounds.Z.Max);
27  // upper is [4, 3, 3]
28
29  viskores::Vec3f_64 center = bounds.Center(); // center is [2, 1, 1.5]

4.1.5. Index Ranges

Just as it is sometimes necessary to track a range of real values, there are times when code has to specify a continuous range of values in an index sequence like an array. For this purpose, Viskores provides RangeId, which behaves similarly to Range except for integer values.

struct RangeId

Represent a range of viskores::Id values.

viskores::RangeId is a helper class for representing a range of viskores::Id values. This is specified simply with a Min and Max value, where Max is exclusive.

RangeId also contains several helper functions for computing and maintaining the range.

Public Functions

inline RangeId()

Construct a range with no indices.

inline RangeId(viskores::Id min, viskores::Id max)

Construct a range with the given minimum (inclusive) and maximum (exclusive) indices.

inline bool IsNonEmpty() const

Determine if the range is valid.

IsNonEmpty return true if the range contains some valid values between Min and Max. If Max <= Min, then no values satisfy the range and IsNonEmpty returns false. Otherwise, return true.

inline bool Contains(viskores::Id value) const

Determines if a value is within the range.

Contains returns true if the give value is within the range, false otherwise.

inline viskores::Id Length() const

Returns the length of the range.

Length computes the distance between the min and max. If the range is empty, 0 is returned.

inline viskores::Id Center() const

Returns the center of the range.

Center computes the middle value of the range.

inline void Include(viskores::Id value)

Expand range to include a value.

This version of Include expands the range just enough to include the given value. If the range already includes this value, then nothing is done.

inline void Include(const viskores::RangeId &range)

Expand range to include other range.

This version of Include expands this range just enough to include that of another range. Essentially it is the union of the two ranges.

inline viskores::RangeId Union(const viskores::RangeId &other) const

Return the union of this and another range.

This is a nondestructive form of Include.

inline viskores::RangeId operator+(const viskores::RangeId &other) const

Operator for union

Public Members

viskores::Id Min

The minimum index of the range (inclusive).

viskores::Id Max

The maximum index of the range (exclusive).

Viskores also often must operate on 2D and 3D arrays (particularly for structured cell sets). For these use cases, RangeId2 and RangeId3 are provided.

struct RangeId2

Represent 2D integer range.

viskores::RangeId2 is a helper class for representing a 2D range of integer values. The typical use of this class is to express a box of indices in the x and y directions.

RangeId2 also contains several helper functions for computing and maintaining the range.

Public Functions

RangeId2() = default

Construct an empty 2D range.

inline RangeId2(const viskores::RangeId &xrange, const viskores::RangeId &yrange)

Construct a range with the given x and y directions.

inline RangeId2(viskores::Id minX, viskores::Id maxX, viskores::Id minY, viskores::Id maxY)

Construct a range with the given minimum (inclusive) and maximum (exclusive) points.

inline explicit RangeId2(const viskores::Id range[4])

Initialize range with an array of 4 values in the order xmin, xmax, ymin, ymax.

inline RangeId2(const viskores::Id2 &min, const viskores::Id2 &max)

Initialize range with the minimum and the maximum corners.

inline bool IsNonEmpty() const

Determine if the range is non-empty.

IsNonEmpty returns true if the range is non-empty.

inline bool Contains(const viskores::Id2 &val) const

Determines if an Id2 value is within the range.

inline viskores::Id2 Center() const

Returns the center of the range.

Center computes the middle of the range.

template<typename T>
inline void Include(const viskores::Vec<T, 2> &point)

Expand range to include a value.

This version of Include expands the range just enough to include the given value. If the range already include this value, then nothing is done.

inline void Include(const viskores::RangeId2 &range)

Expand range to include other range.

This version of Include expands the range just enough to include the other range. Essentially it is the union of the two ranges.

inline viskores::RangeId2 Union(const viskores::RangeId2 &other) const

Return the union of this and another range.

This is a nondestructive form of Include.

inline viskores::RangeId2 operator+(const viskores::RangeId2 &other) const

Operator for union

Public Members

viskores::RangeId X

The range of values in the X direction.

The viskores::RangeId struct provides the minimum and maximum along that axis.

viskores::RangeId Y

The range of values in the Y direction.

The viskores::RangeId struct provides the minimum and maximum along that axis.

struct RangeId3

Represent 3D integer range.

viskores::RangeId3 is a helper class for representing a 3D range of integer values. The typical use of this class is to express a box of indices in the x, y, and z directions.

RangeId3 also contains several helper functions for computing and maintaining the range.

Public Functions

RangeId3() = default

Construct an empty 3D range.

inline RangeId3(const viskores::RangeId &xrange, const viskores::RangeId &yrange, const viskores::RangeId &zrange)

Construct a range with the given x, y, and z directions.

inline RangeId3(viskores::Id minX, viskores::Id maxX, viskores::Id minY, viskores::Id maxY, viskores::Id minZ, viskores::Id maxZ)

Construct a range with the given minimum (inclusive) and maximum (exclusive) points.

inline explicit RangeId3(const viskores::Id range[6])

Initialize range with an array of 6 values in the order xmin, xmax, ymin, ymax, zmin, zmax.

inline RangeId3(const viskores::Id3 &min, const viskores::Id3 &max)

Initialize range with the minimum and the maximum corners.

inline bool IsNonEmpty() const

Determine if the range is non-empty.

IsNonEmpty returns true if the range is non-empty.

inline bool Contains(const viskores::Id3 &val) const

Determines if an Id3 value is within the range.

inline viskores::Id3 Center() const

Returns the center of the range.

Center computes the middle of the range.

template<typename T>
inline void Include(const viskores::Vec<T, 3> &point)

Expand range to include a value.

This version of Include expands the range just enough to include the given value. If the range already include this value, then nothing is done.

inline void Include(const viskores::RangeId3 &range)

Expand range to include other range.

This version of Include expands the range just enough to include the other range. Essentially it is the union of the two ranges.

inline viskores::RangeId3 Union(const viskores::RangeId3 &other) const

Return the union of this and another range.

This is a nondestructive form of Include.

inline viskores::RangeId3 operator+(const viskores::RangeId3 &other) const

Operator for union

Public Members

viskores::RangeId X

The range of values in the X direction.

The viskores::RangeId struct provides the minimum and maximum along that axis.

viskores::RangeId Y

The range of values in the Y direction.

The viskores::RangeId struct provides the minimum and maximum along that axis.

viskores::RangeId Z

The range of values in the Z direction.

The viskores::RangeId struct provides the minimum and maximum along that axis.

4.1.6. Traits

When using templated types, it is often necessary to get information about the type or specialize code based on general properties of the type. Viskores uses traits classes to publish and retrieve information about types. A traits class is simply a templated structure that provides type aliases for tag structures, empty types used for identification. The traits classes might also contain constant numbers and helpful static functions. See Effective C++ Third Edition by Scott Meyers for a description of traits classes and their uses.

4.1.6.1. Type Traits

The viskores::TypeTraits templated class provides basic information about a core type. These type traits are available for all the basic C++ types as well as the core Viskores types described in Chapter 2.1 (Base Types). viskores::TypeTraits contains the following elements.

template<typename T>
class TypeTraits

The TypeTraits class provides helpful compile-time information about the basic types used in Viskores (and a few others for convenience).

The majority of TypeTraits contents are typedefs to tags that can be used to easily override behavior of called functions.

Subclassed by viskores::TypeTraits< T & >, viskores::TypeTraits< const T >

Public Types

using NumericTag = viskores::TypeTraitsUnknownTag

A tag to determine whether the type is integer or real.

This tag is either TypeTraitsRealTag or TypeTraitsIntegerTag.

using DimensionalityTag = viskores::TypeTraitsUnknownTag

A tag to determine whether the type has multiple components.

This tag is either TypeTraitsScalarTag or TypeTraitsVectorTag. Scalars can also be treated as vectors with VecTraits.

Public Static Functions

static inline T ZeroInitialization()

A static function that returns 0 (or the closest equivalent to it) for the given type.

The viskores::TypeTraits::NumericTag will be an alias for one of the following tags.

struct TypeTraitsRealTag

Tag used to identify types that store real (floating-point) numbers.

A TypeTraits class will typedef this class to NumericTag if it stores real numbers (or vectors of real numbers).

struct TypeTraitsIntegerTag

Tag used to identify types that store integer numbers.

A TypeTraits class will typedef this class to NumericTag if it stores integer numbers (or vectors of integers).

The viskores::TypeTraits::DimensionalityTag will be an alias for one of the following tags.

struct TypeTraitsScalarTag

Tag used to identify 0 dimensional types (scalars).

Scalars can also be treated like vectors when used with VecTraits. A TypeTraits class will typedef this class to DimensionalityTag.

struct TypeTraitsVectorTag

Tag used to identify 1 dimensional types (vectors).

A TypeTraits class will typedef this class to DimensionalityTag.

If for some reason one of these tags do not apply, viskores::TypeTraitsUnknownTag will be used.

struct TypeTraitsUnknownTag

Tag used to identify types that aren’t Real, Integer, Scalar or Vector.

The definition of viskores::TypeTraits for viskores::Float32 could like something like this.

Example 4.8 Example definition of viskores::TypeTraits<viskores::Float32>.
 1namespace viskores {
 2
 3template<>
 4struct TypeTraits<viskores::Float32>
 5{
 6  using NumericTag = viskores::TypeTraitsRealTag;
 7  using DimensionalityTag = viskores::TypeTraitsScalarTag;
 8
 9  VISKORES_EXEC_CONT
10  static viskores::Float32 ZeroInitialization() { return viskores::Float32(0); }
11};
12
13}

Here is a simple example of using viskores::TypeTraits to implement a generic function that behaves like the remainder operator (%) for all types including floating points and vectors.

Example 4.9 Using viskores::TypeTraits for a generic remainder.
 1#include <viskores/TypeTraits.h>
 2
 3#include <viskores/Math.h>
 4
 5template<typename T>
 6T AnyRemainder(const T& numerator, const T& denominator);
 7
 8namespace detail
 9{
10
11template<typename T>
12T AnyRemainderImpl(const T& numerator,
13                   const T& denominator,
14                   viskores::TypeTraitsIntegerTag,
15                   viskores::TypeTraitsScalarTag)
16{
17  return numerator % denominator;
18}
19
20template<typename T>
21T AnyRemainderImpl(const T& numerator,
22                   const T& denominator,
23                   viskores::TypeTraitsRealTag,
24                   viskores::TypeTraitsScalarTag)
25{
26  // The Viskores math library contains a Remainder function that operates on
27  // floating point numbers.
28  return viskores::Remainder(numerator, denominator);
29}
30
31template<typename T, typename NumericTag>
32T AnyRemainderImpl(const T& numerator,
33                   const T& denominator,
34                   NumericTag,
35                   viskores::TypeTraitsVectorTag)
36{
37  T result;
38  for (int componentIndex = 0; componentIndex < T::NUM_COMPONENTS; componentIndex++)
39  {
40    result[componentIndex] =
41      AnyRemainder(numerator[componentIndex], denominator[componentIndex]);
42  }
43  return result;
44}
45
46} // namespace detail
47
48template<typename T>
49T AnyRemainder(const T& numerator, const T& denominator)
50{
51  return detail::AnyRemainderImpl(numerator,
52                                  denominator,
53                                  typename viskores::TypeTraits<T>::NumericTag(),
54                                  typename viskores::TypeTraits<T>::DimensionalityTag());
55}

4.1.6.2. Vector Traits

The templated viskores::Vec class contains several items for introspection (such as the component type and its size). However, there are other types that behave similarly to viskores::Vec objects but have different ways to perform this introspection.

For example, Viskores contains Vec-like objects that essentially behave the same but might have different features. Also, there may be reason to interchangeably use basic scalar values, like an integer or floating point number, with vectors. To provide a consistent interface to access these multiple types that represents vectors, the viskores::VecTraits templated class provides information and accessors to vector types.It contains the following elements.

template<class T>
struct VecTraits

Traits that can be queried to treat any type as a Vec.

The VecTraits class gives several static members that define how to use a given type as a vector. This is useful for templated functions and methods that have a parameter that could be either a standard scalar type or a Vec or some other Vec-like object. When using this class, scalar objects are treated like a Vec of size 1.

The default implementation of this template treats the type as a scalar. Types that actually behave like vectors should specialize this template to provide the proper information.

Subclassed by viskores::VecTraits< T & >, viskores::VecTraits< T * >, viskores::VecTraits< const T & >, viskores::VecTraits< const T >, viskores::internal::SafeVecTraits< T >

Public Types

using ComponentType = T

Type of the components in the vector.

If the type is really a scalar, then the component type is the same as the scalar type.

using BaseComponentType = T

Base component type in the vector.

Similar to ComponentType except that for nested vectors (e.g. Vec<Vec<T, M>, N>), it returns the base scalar type at the end of the composition (T in this example).

using HasMultipleComponents = viskores::VecTraitsTagSingleComponent

A tag specifying whether this vector has multiple components (i.e.

is a “real” vector).

This type is set to either viskores::VecTraitsTagSingleComponent if the vector length is size 1 or viskores::VecTraitsTagMultipleComponents otherwise. This tag can be useful for creating specialized functions when a vector is really just a scalar. If the vector type is of variable size (that is, IsSizeStatic is viskores::VecTraitsTagSizeVariable), then HasMultipleComponents might be viskores::VecTraitsTagMultipleComponents even when at run time there is only one component.

using IsSizeStatic = viskores::VecTraitsTagSizeStatic

A tag specifying whether the size of this vector is known at compile time.

If set to VecTraitsTagSizeStatic, then NUM_COMPONENTS is set. If set to VecTraitsTagSizeVariable, then the number of components is not known at compile time and must be queried with GetNumberOfComponents.

template<typename NewComponentType>
using ReplaceComponentType = NewComponentType

Get a vector of the same type but with a different component.

This type resolves to another vector with a different component type. For example, viskores::VecTraits<viskores::Vec<T, N>>::ReplaceComponentType<T2> is viskores::Vec<T2, N>. This replacement is not recursive. So VecTraits<Vec<Vec<T, M>, N>::ReplaceComponentType<T2> is viskores::Vec<T2, N>.

template<typename NewComponentType>
using ReplaceBaseComponentType = NewComponentType

Get a vector of the same type but with a different base component.

This type resolves to another vector with a different base component type. The replacement is recursive for nested types. For example, VecTraits<Vec<Vec<T, M>, N>::ReplaceBaseComponentType<T2> is Vec<Vec<T2, M>, N>.

Public Static Functions

static inline constexpr viskores::IdComponent GetNumberOfComponents(const T&)

Returns the number of components in the given vector.

The result of GetNumberOfComponents() is the same value of NUM_COMPONENTS for vector types that have a static size (that is, IsSizeStatic is viskores::VecTraitsTagSizeStatic). But unlike NUM_COMPONENTS, GetNumberOfComponents() works for vectors of any type.

static inline const ComponentType &GetComponent(const T &vector, viskores::IdComponent)

Returns the value in a given component of the vector.

static inline ComponentType &GetComponent(T &vector, viskores::IdComponent)

Returns the value in a given component of the vector.

static inline void SetComponent(T &vector, viskores::IdComponent, ComponentType value)

Changes the value in a given component of the vector.

template<viskores::IdComponent destSize>
static inline void CopyInto(const T &src, viskores::Vec<ComponentType, destSize> &dest)

Copies the components in the given vector into a given Vec object.

Public Static Attributes

static constexpr viskores::IdComponent NUM_COMPONENTS = 1

Number of components in the vector.

This is only defined for vectors of a static size. That is, NUM_COMPONENTS is not available when IsSizeStatic is set to viskores::VecTraitsTagSizeVariable.

The viskores::VecTraits::HasMultipleComponents could be one of the following tags.

struct VecTraitsTagMultipleComponents

A tag for vectors that are “true” vectors (i.e.

have more than one component).

struct VecTraitsTagSingleComponent

A tag for vectors that are really just scalars (i.e.

have only one component)

The viskores::VecTraits::IsSizeStatic could be one of the following tags.

struct VecTraitsTagSizeStatic

A tag for vectors where the number of components are known at compile time.

struct VecTraitsTagSizeVariable

A tag for vectors where the number of components are not determined until run time.

The definition of viskores::VecTraits for viskores::Id3 could look something like this.

Example 4.10 Example definition of viskores::VecTraits<viskores::Id3>.
 1namespace viskores {
 2
 3template<>
 4struct VecTraits<viskores::Id3>
 5{
 6  using ComponentType = viskores::Id;
 7  using BaseComponentType = viskores::Id;
 8  static const int NUM_COMPONENTS = 3;
 9  using IsSizeStatic = viskores::VecTraitsTagSizeStatic;
10  using HasMultipleComponents = viskores::VecTraitsTagMultipleComponents;
11
12  VISKORES_EXEC_CONT
13  static viskores::IdComponent GetNumberOfComponents(const viskores::Id3&)
14  {
15    return NUM_COMPONENTS;
16  }
17
18  VISKORES_EXEC_CONT
19  static const viskores::Id& GetComponent(const viskores::Id3& vector, int component)
20  {
21    return vector[component];
22  }
23  VISKORES_EXEC_CONT
24  static viskores::Id& GetComponent(viskores::Id3& vector, int component)
25  {
26    return vector[component];
27  }
28
29  VISKORES_EXEC_CONT
30  static void SetComponent(viskores::Id3& vector, int component, viskores::Id value)
31  {
32    vector[component] = value;
33  }
34
35  template<typename NewComponentType>
36  using ReplaceComponentType = viskores::Vec<NewComponentType, 3>;
37
38  template<typename NewComponentType>
39  using ReplaceBaseComponentType = viskores::Vec<NewComponentType, 3>;
40
41  template<viskores::IdComponent DestSize>
42  VISKORES_EXEC_CONT static void CopyInto(const viskores::Id3& src,
43                                          viskores::Vec<viskores::Id, DestSize>& dest)
44  {
45    for (viskores::IdComponent index = 0; (index < NUM_COMPONENTS) && (index < DestSize);
46         index++)
47    {
48      dest[index] = src[index];
49    }
50  }
51};
52
53} // namespace viskores

The real power of vector traits is that they simplify creating generic operations on any type that can look like a vector. This includes operations on scalar values as if they were vectors of size one. The following code uses vector traits to simplify the implementation of less functors that define an ordering that can be used for sorting and other operations.

Example 4.11 Using viskores::VecTraits for less functors.
 1#include <viskores/VecTraits.h>
 2
 3// This functor provides a total ordering of vectors. Every compared vector
 4// will be either less, greater, or equal (assuming all the vector components
 5// also have a total ordering).
 6template<typename T>
 7struct LessTotalOrder
 8{
 9  VISKORES_EXEC_CONT
10  bool operator()(const T& left, const T& right)
11  {
12    for (int index = 0; index < viskores::VecTraits<T>::NUM_COMPONENTS; index++)
13    {
14      using ComponentType = typename viskores::VecTraits<T>::ComponentType;
15      const ComponentType& leftValue = viskores::VecTraits<T>::GetComponent(left, index);
16      const ComponentType& rightValue =
17        viskores::VecTraits<T>::GetComponent(right, index);
18      if (leftValue < rightValue)
19      {
20        return true;
21      }
22      if (rightValue < leftValue)
23      {
24        return false;
25      }
26    }
27    // If we are here, the vectors are equal (or at least equivalent).
28    return false;
29  }
30};
31
32// This functor provides a partial ordering of vectors. It returns true if and
33// only if all components satisfy the less operation. It is possible for
34// vectors to be neither less, greater, nor equal, but the transitive closure
35// is still valid.
36template<typename T>
37struct LessPartialOrder
38{
39  VISKORES_EXEC_CONT
40  bool operator()(const T& left, const T& right)
41  {
42    for (int index = 0; index < viskores::VecTraits<T>::NUM_COMPONENTS; index++)
43    {
44      using ComponentType = typename viskores::VecTraits<T>::ComponentType;
45      const ComponentType& leftValue = viskores::VecTraits<T>::GetComponent(left, index);
46      const ComponentType& rightValue =
47        viskores::VecTraits<T>::GetComponent(right, index);
48      if (!(leftValue < rightValue))
49      {
50        return false;
51      }
52    }
53    // If we are here, all components satisfy less than relation.
54    return true;
55  }
56};

4.1.7. List Templates

Viskores internally uses template metaprogramming, which utilizes C++ templates to run source-generating programs, to customize code to various data and compute platforms. One basic structure often uses with template metaprogramming is a list of class names (also sometimes called a tuple or vector, although both of those names have different meanings in Viskores).

Many Viskores users only need predefined lists, such as the type lists specified in Section 4.1.7.2 (Type Lists). Those users can skip most of the details of this section. However, it is sometimes useful to modify lists, create new lists, or operate on lists, and these usages are documented here.

4.1.7.1. Building Lists

A basic list is defined with the viskores::List template.

template<typename ...Ts>
struct List

A template used to hold a list of types.

List is an empty struct that is used to hold a list of types as its template arguments. Viskores provides templated types that allows a List to be manipulated and used in numerous ways.

It is common (but not necessary) to use the using keyword to define an alias for a list with a particular meaning.

Example 4.12 Creating lists of types.
 1#include <viskores/List.h>
 2
 3// Placeholder classes representing things that might be in a template
 4// metaprogram list.
 5class Foo;
 6class Bar;
 7class Baz;
 8class Qux;
 9class Xyzzy;
10
11// The names of the following tags are indicative of the lists they contain.
12
13using FooList = viskores::List<Foo>;
14
15using FooBarList = viskores::List<Foo, Bar>;
16
17using BazQuxXyzzyList = viskores::List<Baz, Qux, Xyzzy>;
18
19using QuxBazBarFooList = viskores::List<Qux, Baz, Bar, Foo>;

Viskores defines some special and convenience versions of viskores::List.

using viskores::ListEmpty = viskores::List<>

A convenience type for an empty list.

using viskores::ListUniversal = viskores::List<detail::UniversalTypeTag>

A special type for a list that represents holding all potential values.

Note: This list cannot be used with ForEach and some list transforms for obvious reasons.

4.1.7.2. Type Lists

One of the major use cases for template metaprogramming lists in Viskores is to identify a set of potential data types for arrays. The viskores/TypeList.h header contains predefined lists for known Viskores types. The following lists are provided.

using viskores::TypeListId = viskores::List<viskores::Id>

A list containing the type viskores::Id.

using viskores::TypeListId2 = viskores::List<viskores::Id2>

A list containing the type viskores::Id2.

using viskores::TypeListId3 = viskores::List<viskores::Id3>

A list containing the type viskores::Id3.

using viskores::TypeListId4 = viskores::List<viskores::Id4>

A list containing the type viskores::Id4.

using viskores::TypeListIdComponent = viskores::List<viskores::IdComponent>

A list containing the type viskores::IdComponent.

using viskores::TypeListIndex = viskores::List<viskores::Id, viskores::Id2, viskores::Id3>

A list containing types used to index arrays.

Contains viskores::Id, viskores::Id2, and viskores::Id3.

using viskores::TypeListFieldScalar = viskores::List<viskores::Float32, viskores::Float64>

A list containing types used for scalar fields.

Specifically, contains floating point numbers of different widths (i.e. viskores::Float32 and viskores::Float64).

using viskores::TypeListFieldVec2 = viskores::List<viskores::Vec2f_32, viskores::Vec2f_64>

A list containing types for values for fields with two dimensional vectors.

using viskores::TypeListFieldVec3 = viskores::List<viskores::Vec3f_32, viskores::Vec3f_64>

A list containing types for values for fields with three dimensional vectors.

using viskores::TypeListFieldVec4 = viskores::List<viskores::Vec4f_32, viskores::Vec4f_64>

A list containing types for values for fields with four dimensional vectors.

using viskores::TypeListFloatVec = viskores::List<viskores::Vec2f_32, viskores::Vec2f_64, viskores::Vec3f_32, viskores::Vec3f_64, viskores::Vec4f_32, viskores::Vec4f_64>

A list containing common types for floating-point vectors.

Specifically contains floating point vectors of size 2, 3, and 4 with floating point components. Scalars are not included.

using viskores::TypeListField = viskores::List<viskores::Float32, viskores::Float64, viskores::Vec2f_32, viskores::Vec2f_64, viskores::Vec3f_32, viskores::Vec3f_64, viskores::Vec4f_32, viskores::Vec4f_64>

A list containing common types for values in fields.

Specifically contains floating point scalars and vectors of size 2, 3, and 4 with floating point components.

using viskores::TypeListScalarAll = viskores::List<viskores::Int8, viskores::UInt8, viskores::Int16, viskores::UInt16, viskores::Int32, viskores::UInt32, viskores::Int64, viskores::UInt64, viskores::Float32, viskores::Float64>

A list of all scalars defined in viskores/Types.h.

A scalar is a type that holds a single number. This should containing all true variations of scalars, but there might be some arithmetic C types not included. For example, this list contains signed char, and unsigned char, but not char as one of those types will behave the same as it. Two of the three types behave the same, but be aware that template resolution will treat them differently.

using viskores::TypeListBaseC = viskores::ListAppend<viskores::TypeListScalarAll, viskores::List<bool, char, signed long, unsigned long>>
using viskores::TypeListVecCommon = viskores::List<viskores::Vec2ui_8, viskores::Vec2i_32, viskores::Vec2i_64, viskores::Vec2f_32, viskores::Vec2f_64, viskores::Vec3ui_8, viskores::Vec3i_32, viskores::Vec3i_64, viskores::Vec3f_32, viskores::Vec3f_64, viskores::Vec4ui_8, viskores::Vec4i_32, viskores::Vec4i_64, viskores::Vec4f_32, viskores::Vec4f_64>

A list of the most commonly use Vec classes.

Specifically, these are vectors of size 2, 3, or 4 containing either unsigned bytes, signed integers of 32 or 64 bits, or floating point values of 32 or 64 bits.

using viskores::TypeListVecAll = viskores::ListAppend<viskores::TypeListVecCommon, viskores::internal::TypeListVecUncommon>

A list of all vector classes with standard types as components and lengths between 2 and 4.

using viskores::TypeListAll = viskores::ListAppend<viskores::TypeListScalarAll, viskores::TypeListVecAll>

A list of all basic types listed in viskores/Types.h.

Does not include all possible Viskores types like arbitrarily typed and sized Vecs (only up to length 4) or math types like matrices.

using viskores::TypeListCommon = viskores::List<viskores::UInt8, viskores::Int32, viskores::Int64, viskores::Float32, viskores::Float64, viskores::Vec3f_32, viskores::Vec3f_64>

A list of the most commonly used types across multiple domains.

Includes integers, floating points, and 3 dimensional vectors of floating points.

If these lists are not sufficient, it is possible to build new type lists using the existing type lists and the list bases from Section 4.1.7.1 (Building Lists) as demonstrated in the following example.

Example 4.13 Defining new type lists.
1// A list of 2D vector types.
2using Vec2List = viskores::List<viskores::Vec2f_32, viskores::Vec2f_64>;
3
4// An application that uses 2D geometry might commonly encounter this list of
5// types.
6using MyCommonTypes = viskores::ListAppend<Vec2List, viskores::TypeListCommon>;

The viskores/cont/DefaultTypes.h header defines a macro named VISKORES_DEFAULT_TYPE_LIST that defines a default list of types to use when, for example, determining the type of a field array. This macro can change depending on Viskores compile options.

4.1.7.3. Querying Lists

viskores/List.h contains some templated classes to help get information about a list type. This are particularly useful for lists that are provided as templated parameters for which you do not know the exact type.

4.1.7.3.1. Is a List

The VISKORES_IS_LIST does a compile-time check to make sure a particular type is actually a viskores::List of types. If the compile-time check fails, then a build error will occur. This is a good way to verify that a templated class or method that expects a list actually gets a list.

VISKORES_IS_LIST(type)

Checks that the argument is a proper list.

This is a handy concept check for functions and classes to make sure that a template argument is actually a device adapter tag. (You can get weird errors elsewhere in the code when a mistake is made.)

Example 4.14 Checking that a template parameter is a valid viskores::List.
 1template<typename List>
 2class MyImportantClass
 3{
 4  VISKORES_IS_LIST(List);
 5  // Implementation...
 6};
 7
 8void DoImportantStuff()
 9{
10  MyImportantClass<viskores::List<viskores::Id>> important1; // This compiles fine
11  MyImportantClass<viskores::Id> important2;  // COMPILE ERROR: viskores::Id is not a list

4.1.7.3.2. List Size

The size of a list can be determined by using the viskores::ListSize template. The type of the template will resolve to a std::integral_constant<viskores::IdComponent,N> where N is the number of types in the list. viskores::ListSize does not work with viskores::ListUniversal.

template<typename List>
using viskores::ListSize = typename detail::ListSizeImpl<List>::type

Becomes an std::integral_constant containing the number of types in a list.

Example 4.15 Getting the size of a viskores::List.
1  using MyList = viskores::List<viskores::Int8, viskores::Int32, viskores::Int64>;
2
3  constexpr viskores::IdComponent myListSize = viskores::ListSize<MyList>::value;
4  // myListSize is 3

4.1.7.3.3. List Contains

The viskores::ListHas template can be used to determine if a viskores::List contains a particular type. viskores::ListHas takes two template parameters. The first parameter is a form of viskores::List. The second parameter is any type to check to see if it is in the list. If the type is in the list, then viskores::ListHas resolves to std::true_type. Otherwise it resolves to std::false_type. viskores::ListHas always returns true for viskores::ListUniversal.

template<typename List, typename T>
using viskores::ListHas = typename detail::ListHasImpl<List, T>::type

Checks to see if the given T is in the list pointed to by List.

Becomes std::true_type if the T is in List. std::false_type otherwise.

Example 4.16 Determining if a viskores::List contains a particular type.
1  using MyList =
2    viskores::List<viskores::Int8, viskores::Int16, viskores::Int32, viskores::Int64>;
3
4  constexpr bool hasInt = viskores::ListHas<MyList, int>::value;
5  // hasInt is true
6
7  constexpr bool hasFloat = viskores::ListHas<MyList, float>::value;
8  // hasFloat is false

4.1.7.3.4. List Indices

The viskores::ListIndexOf template can be used to get the index of a particular type in a viskores::List. viskores::ListIndexOf takes two template parameters. The first parameter is a form of viskores::List. The second parameter is any type to check to see if it is in the list. The type of the template will resolve to a std::integral_constant<viskores::IdComponent,N> where N is the index of the type. If the requested type is not in the list, then viskores::ListIndexOf becomes std::integral_constant<viskores::IdComponent,-1>.

template<typename List, typename T>
using viskores::ListIndexOf = typename detail::ListIndexOfImpl<List, T>::type

Finds the index of a given type.

Becomes a std::integral_constant for the index of the given type. If the given type is not in the list, the value is set to -1.

Conversely, the viskores::ListAt template can be used to get the type for a particular index. The two template parameters for viskores::ListAt are the viskores::List and an index for the list.

template<typename List, viskores::IdComponent Index>
using viskores::ListAt = typename detail::ListAtImpl<List, Index>::type

Finds the type at the given index.

This becomes the type of the list at the given index.

Neither viskores::ListIndexOf nor viskores::ListAt works with viskores::ListUniversal.

Example 4.17 Using indices with viskores::List.
 1  using MyList = viskores::List<viskores::Int8, viskores::Int32, viskores::Int64>;
 2
 3  constexpr viskores::IdComponent indexOfInt8 =
 4    viskores::ListIndexOf<MyList, viskores::Int8>::value;
 5  // indexOfInt8 is 0
 6  constexpr viskores::IdComponent indexOfInt32 =
 7    viskores::ListIndexOf<MyList, viskores::Int32>::value;
 8  // indexOfInt32 is 1
 9  constexpr viskores::IdComponent indexOfInt64 =
10    viskores::ListIndexOf<MyList, viskores::Int64>::value;
11  // indexOfInt64 is 2
12  constexpr viskores::IdComponent indexOfFloat32 =
13    viskores::ListIndexOf<MyList, viskores::Float32>::value;
14  // indexOfFloat32 is -1 (not in list)
15
16  using T0 = viskores::ListAt<MyList, 0>; // T0 is viskores::Int8
17  using T1 = viskores::ListAt<MyList, 1>; // T1 is viskores::Int32
18  using T2 = viskores::ListAt<MyList, 2>; // T2 is viskores::Int64

4.1.7.4. Operating on Lists

In addition to providing the base templates for defining and querying lists, viskores/List.h also contains several features for operating on lists.

4.1.7.4.1. Appending Lists

The viskores::ListAppend template joins together 2 or more viskores::List types. The items are concatenated in the order provided to viskores::ListAppend. viskores::ListAppend does not work with viskores::ListUniversal.

template<typename ...Lists>
using viskores::ListAppend = typename detail::ListAppendImpl<Lists...>::type

Concatinates a set of lists into a single list.

Note that this does not work correctly with viskores::ListUniversal.

Example 4.18 Appending viskores::List types.
 1using BigTypes = viskores::List<viskores::Int64, viskores::Float64>;
 2using MediumTypes = viskores::List<viskores::Int32, viskores::Float32>;
 3using SmallTypes = viskores::List<viskores::Int8>;
 4
 5using SmallAndBigTypes = viskores::ListAppend<SmallTypes, BigTypes>;
 6// SmallAndBigTypes is viskores::List<viskores::Int8, viskores::Int64, viskores::Float64>
 7
 8using AllMyTypes = viskores::ListAppend<BigTypes, MediumTypes, SmallTypes>;
 9// AllMyTypes is
10// viskores::List<viskores::Int64, viskores::Float64, viskores::Int32, viskores::Float32, viskores::Int8>

4.1.7.4.2. Intersecting Lists

The viskores::ListIntersect template takes two viskores::List types and becomes a viskores::List containing all types in both lists. If one of the lists is viskores::ListUniversal, the contents of the other list used.

template<typename List1, typename List2>
using viskores::ListIntersect = typename detail::ListIntersectImpl<List1, List2>::type

Constructs a list containing types present in all lists.

Example 4.19 Intersecting viskores::List types.
1using SignedInts =
2  viskores::List<viskores::Int8, viskores::Int16, viskores::Int32, viskores::Int64>;
3using WordTypes =
4  viskores::List<viskores::Int32, viskores::UInt32, viskores::Int64, viskores::UInt64>;
5
6using SignedWords = viskores::ListIntersect<SignedInts, WordTypes>;
7// SignedWords is viskores::List<viskores::Int32, viskores::Int64>

4.1.7.4.3. Resolve a Template with all Types in a List

The viskores::ListApply template transfers all of the types in a viskores::List to another template. The first template argument of viskores::ListApply is the viskores::List to apply. The second template argument is another template to apply to. viskores::ListApply becomes an instance of the passed template with all the types in the viskores::List. viskores::ListApply can be used to convert a viskores::List to some other template. viskores::ListApply cannot be used with viskores::ListUniversal.

template<typename List, template<typename...> class Target>
using viskores::ListApply = typename detail::ListApplyImpl<List, Target>::type

Applies the list of types to a template.

Given a ListTag and a templated class, returns the class instantiated with the types represented by the ListTag.

Example 4.20 Applying a viskores::List to another template.
1using MyList = viskores::List<viskores::Id, viskores::Id3, viskores::Vec3f>;
2
3using MyTuple = viskores::ListApply<MyList, std::tuple>;
4// MyTuple is std::tuple<viskores::Id, viskores::Id3, viskores::Vec3f>

4.1.7.4.4. Transform Each Type in a List

The viskores::ListTransform template applies each item in a viskores::List to another template and constructs a list from all these applications. The first template argument of viskores::ListTransform is the viskores::List to apply. The second template argument is another template to apply to. viskores::ListTransform becomes an instance of a new viskores::List containing the passed template each type. viskores::ListTransform cannot be used with viskores::ListUniversal.

template<typename List, template<typename> class Transform>
using viskores::ListTransform = typename detail::ListTransformImpl<List, Transform>::type

Constructs a list containing all types in a source list applied to a transform template.

Example 4.21 Transforming a viskores::List using a custom template.
1using MyList = viskores::List<viskores::Int32, viskores::Float32>;
2
3template<typename T>
4using MakeVec = viskores::Vec<T, 3>;
5
6using MyVecList = viskores::ListTransform<MyList, MakeVec>;
7// MyVecList is viskores::List<viskores::Vec<viskores::Int32, 3>, viskores::Vec<viskores::Float32, 3>>

4.1.7.4.5. Conditionally Removing Items from a List

The viskores::ListRemoveIf template removes items from a viskores::List given a predicate. The first template argument of viskores::ListRemoveIf is the viskores::List. The second argument is another template that is used as a predicate to determine if the type should be removed or not. The predicate should become a type with a value member that is a static true or false value. Any type in the list that the predicate evaluates to true is removed. viskores::ListRemoveIf cannot be used with viskores::ListUniversal.

template<typename List, template<typename> class Predicate>
using viskores::ListRemoveIf = typename detail::ListRemoveIfImpl<List, Predicate>::type

Takes an existing List and a predicate template that is applied to each type in the List.

Any type in the List that has a value element equal to true (the equivalent of std::true_type), that item will be removed from the list. For example the following type

viskores::ListRemoveIf<viskores::List<int, float, long long, double>, std::is_integral>

resolves to a List that is equivalent to viskores::List<float, double> because std::is_integral<int> and std::is_integral<long long> resolve to std::true_type whereas std::is_integral<float> and std::is_integral<double> resolve to std::false_type.

Example 4.22 Removing items from a viskores::List.
1using MyList = viskores::List<viskores::Int64,
2                              viskores::Float64,
3                              viskores::Int32,
4                              viskores::Float32,
5                              viskores::Int8>;
6
7using FilteredList = viskores::ListRemoveIf<MyList, std::is_integral>;
8// FilteredList is viskores::List<viskores::Float64, viskores::Float32>

4.1.7.4.6. Combine all Pairs of Two Lists

The viskores::ListCross takes two lists and performs a cross product of them. It does this by creating a new viskores::List that contains nested viskores::List types, each of length 2 and containing all possible pairs of items in the first list with items in the second list. viskores::ListCross is often used in conjunction with another list processing command, such as viskores::ListTransform to build templated types of many combinations. viskores::ListCross cannot be used with viskores::ListUniversal.

template<typename List1, typename List2>
using viskores::ListCross = typename detail::ListCrossImpl<List1, List2>::type

Generates a list that is the cross product of two input lists.

The resulting list has the form of viskores::List<viskores::List<A1,B1>, viskores::List<A1,B2>,...>

Example 4.23 Creating the cross product of 2 viskores::List types.
 1using BaseTypes = viskores::List<viskores::Int8, viskores::Int32, viskores::Int64>;
 2using BoolCases = viskores::List<std::false_type, std::true_type>;
 3
 4using CrossTypes = viskores::ListCross<BaseTypes, BoolCases>;
 5// CrossTypes is
 6//   viskores::List<viskores::List<viskores::Int8, std::false_type>,
 7//              viskores::List<viskores::Int8, std::true_type>,
 8//              viskores::List<viskores::Int32, std::false_type>,
 9//              viskores::List<viskores::Int32, std::true_type>,
10//              viskores::List<viskores::Int64, std::false_type>,
11//              viskores::List<viskores::Int64, std::true_type>>
12
13template<typename TypeAndIsVec>
14using ListPairToType =
15  typename std::conditional<viskores::ListAt<TypeAndIsVec, 1>::value,
16                            viskores::Vec<viskores::ListAt<TypeAndIsVec, 0>, 3>,
17                            viskores::ListAt<TypeAndIsVec, 0>>::type;
18
19using AllTypes = viskores::ListTransform<CrossTypes, ListPairToType>;
20// AllTypes is
21//   viskores::List<viskores::Int8,
22//              viskores::Vec<viskores::Int8, 3>,
23//              viskores::Int32,
24//              viskores::Vec<viskores::Int32, 3>,
25//              viskores::Int64,
26//              viskores::Vec<viskores::Int64, 3>>

4.1.7.4.7. Call a Function For Each Type in a List

The viskores::ListForEach function takes a functor object and a viskores::List. It then calls the functor object with the default object of each type in the list. This is most typically used with C++ run-time type information to convert a run-time polymorphic object to a statically typed (and possibly inlined) call.

template<typename Functor, typename ...Ts, typename ...Args>
void viskores::ListForEach(Functor &&f, viskores::List<Ts...>, Args&&... args)

For each typename represented by the list, call the functor with a default instance of that type.

The following example shows a rudimentary version of converting a dynamically-typed array to a statically-typed array similar to what is done in Viskores classes like viskores::cont::UnknownArrayHandle, which is documented in Chapter 3.5 (Unknown Array Handles).

Example 4.24 Converting dynamic types to static types with viskores::ListForEach.
 1struct MyArrayBase
 2{
 3  // A virtual destructor makes sure C++ RTTI will be generated. It also helps
 4  // ensure subclass destructors are called.
 5  virtual ~MyArrayBase() {}
 6};
 7
 8template<typename T>
 9struct MyArrayImpl : public MyArrayBase
10{
11  std::vector<T> Array;
12};
13
14template<typename T>
15void PrefixSum(std::vector<T>& array)
16{
17  T sum(typename viskores::VecTraits<T>::ComponentType(0));
18  for (typename std::vector<T>::iterator iter = array.begin(); iter != array.end();
19       iter++)
20  {
21    sum = sum + *iter;
22    *iter = sum;
23  }
24}
25
26struct PrefixSumFunctor
27{
28  MyArrayBase* ArrayPointer;
29
30  PrefixSumFunctor(MyArrayBase* arrayPointer)
31    : ArrayPointer(arrayPointer)
32  {
33  }
34
35  template<typename T>
36  void operator()(T)
37  {
38    using ConcreteArrayType = MyArrayImpl<T>;
39    ConcreteArrayType* concreteArray =
40      dynamic_cast<ConcreteArrayType*>(this->ArrayPointer);
41    if (concreteArray != NULL)
42    {
43      PrefixSum(concreteArray->Array);
44    }
45  }
46};
47
48void DoPrefixSum(MyArrayBase* array)
49{
50  PrefixSumFunctor functor = PrefixSumFunctor(array);
51  viskores::ListForEach(functor, viskores::TypeListCommon());
52}

4.1.8. Pair

Viskores defines a viskores::Pair templated object that behaves just like std::pair from the standard template library. The difference is that viskores::Pair will work in both the execution and control environments, whereas the STL std::pair does not always work in the execution environment.

template<typename T1, typename T2>
struct Pair

A viskores::Pair is essentially the same as an STL pair object except that the methods (constructors and operators) are defined to work in both the control and execution environments (whereas std::pair is likely to work only in the control environment).

Public Types

using FirstType = T1

The type of the first object.

using SecondType = T2

The type of the second object.

using first_type = FirstType

The same as FirstType, but follows the naming convention of std::pair.

using second_type = SecondType

The same as SecondType, but follows the naming convention of std::pair.

Public Functions

Pair() = default
inline Pair(const FirstType &firstSrc, const SecondType &secondSrc)
inline Pair(FirstType &&firstSrc, SecondType &&secondSrc) noexcept(noexcept(FirstType{std::declval<FirstType&&>()}, SecondType{std::declval<SecondType&&>()}))
Pair(const Pair&) = default
Pair(Pair&&) = default
template<typename U1, typename U2>
inline Pair(const viskores::Pair<U1, U2> &src)
template<typename U1, typename U2>
inline Pair(viskores::Pair<U1, U2> &&src) noexcept(noexcept(U1{std::declval<U1&&>()}, U2{std::declval<U2&&>()}))
template<typename U1, typename U2>
inline Pair(const std::pair<U1, U2> &src)
template<typename U1, typename U2>
inline Pair(std::pair<U1, U2> &&src) noexcept(noexcept(U1{std::declval<U1&&>()}, U2{std::declval<U2&&>()}))
viskores::Pair<FirstType, SecondType> &operator=(const viskores::Pair<FirstType, SecondType> &src) = default
viskores::Pair<FirstType, SecondType> &operator=(viskores::Pair<FirstType, SecondType> &&src) = default
inline bool operator==(const viskores::Pair<FirstType, SecondType> &other) const
inline bool operator!=(const viskores::Pair<FirstType, SecondType> &other) const
inline bool operator<(const viskores::Pair<FirstType, SecondType> &other) const

Tests ordering on the first object, and then on the second object if the first are equal.

inline bool operator>(const viskores::Pair<FirstType, SecondType> &other) const

Tests ordering on the first object, and then on the second object if the first are equal.

inline bool operator<=(const viskores::Pair<FirstType, SecondType> &other) const

Tests ordering on the first object, and then on the second object if the first are equal.

inline bool operator>=(const viskores::Pair<FirstType, SecondType> &other) const

Tests ordering on the first object, and then on the second object if the first are equal.

Public Members

FirstType first

The pair’s first object.

Note that this field breaks Viskores’s naming conventions to make viskores::Pair more compatible with std::pair.

SecondType second

The pair’s second object.

Note that this field breaks Viskores’s naming conventions to make viskores::Pair more compatible with std::pair.

The Viskores version of viskores::Pair supports the same types, fields, and operations as the STL version. Viskores also provides a viskores::make_Pair() function for convenience.

template<typename T1, typename T2>
viskores::Pair<typename std::decay<T1>::type, typename std::decay<T2>::type> viskores::make_Pair(T1 &&v1, T2 &&v2)

4.1.9. Tuple

Viskores defines a viskores::Tuple templated object that behaves like std::tuple from the standard template library. The main difference is that viskores::Tuple will work in both the execution and control environments, whereas the STL std::tuple does not always work in the execution environment.

template<typename ...Ts>
class Tuple

Viskores replacement for std::tuple.

This function serves the same function as std::tuple and behaves similarly. However, this version of Tuple works on devices that Viskores supports. There are also some implementation details that makes compiling faster for Viskores use. We also provide some methods like Apply and ForEach that are helpful for several Viskores operations.

4.1.9.1. Defining and Constructing

viskores::Tuple takes any number of template parameters that define the objects stored the tuple.

Example 4.25 Defining a viskores::Tuple.
1  viskores::
2    Tuple<viskores::Id, viskores::Vec3f, viskores::cont::ArrayHandle<viskores::Int32>>
3      myTuple;

You can construct a viskores::Tuple with arguments that will be used to initialize the respective objects. As a convenience, you can use viskores::MakeTuple() to construct a viskores::Tuple of types based on the arguments.

template<typename ...Ts>
auto viskores::MakeTuple(Ts&&... args) -> viskores::Tuple<typename std::decay<Ts>::type...>

Creates a new viskores::Tuple with the given types.

template<typename ...Ts>
auto viskores::make_tuple(Ts&&... args) -> decltype(viskores::MakeTuple(std::forward<Ts>(args)...))

Compatible with std::make_tuple for viskores::Tuple.

Example 4.26 Initializing values in a viskores::Tuple.
1  // Initialize a tuple with 0, [0, 1, 2], and an existing ArrayHandle.
2  viskores::
3    Tuple<viskores::Id, viskores::Vec3f, viskores::cont::ArrayHandle<viskores::Float32>>
4      myTuple1(0, viskores::Vec3f(0, 1, 2), array);
5
6  // Another way to create the same tuple.
7  auto myTuple2 = viskores::MakeTuple(viskores::Id(0), viskores::Vec3f(0, 1, 2), array);

4.1.9.2. Querying

The size of a viskores::Tuple can be determined by using the viskores::TupleSize template, which resolves to an std::integral_constant. The types at particular indices can be determined with viskores::TupleElement.

template<typename TupleType>
using viskores::TupleSize = std::integral_constant<viskores::IdComponent, TupleType::Size>

Get the size of a tuple.

Given a viskores::Tuple type, becomes a std::integral_constant of the type.

template<viskores::IdComponent Index, typename TupleType>
using viskores::TupleElement = typename detail::TupleElementImpl<Index, TupleType>::type

Becomes the type of the given index for the given viskores::Tuple.

Example 4.27 Querying viskores::Tuple types.
1  using TupleType = viskores::Tuple<viskores::Id, viskores::Float32, viskores::Float64>;
2
3  // Becomes 3
4  constexpr viskores::IdComponent size = viskores::TupleSize<TupleType>::value;
5
6  using FirstType = viskores::TupleElement<0, TupleType>;  // viskores::Id
7  using SecondType = viskores::TupleElement<1, TupleType>; // viskores::Float32
8  using ThirdType = viskores::TupleElement<2, TupleType>;  // viskores::Float64

The function viskores::Get() can be used to retrieve an element from the viskores::Tuple. viskores::Get() returns a reference to the element, so you can set a viskores::Tuple element by setting the return value of viskores::Get().

template<viskores::IdComponent Index, typename ...Ts>
auto viskores::Get(const viskores::Tuple<Ts...> &tuple)

Retrieve the object from a viskores::Tuple at the given index.

template<viskores::IdComponent Index, typename ...Ts>
auto viskores::Get(viskores::Tuple<Ts...> &tuple)

Retrieve the object from a viskores::Tuple at the given index.

template<std::size_t Index, typename ...Ts>
auto viskores::get(const viskores::Tuple<Ts...> &tuple) -> decltype(viskores::Get<static_cast<viskores::IdComponent>(Index)>(tuple))

Compatible with std::get for viskores::Tuple.

template<std::size_t Index, typename ...Ts>
auto viskores::get(viskores::Tuple<Ts...> &tuple) -> decltype(viskores::Get<static_cast<viskores::IdComponent>(Index)>(tuple))

Compatible with std::get for viskores::Tuple.

Example 4.28 Retrieving values from a viskores::Tuple.
1  auto myTuple = viskores::MakeTuple(viskores::Id3(0, 1, 2), viskores::Vec3f(3, 4, 5));
2
3  // Gets the value [0, 1, 2]
4  viskores::Id3 x = viskores::Get<0>(myTuple);
5
6  // Changes the second object in myTuple to [6, 7, 8]
7  viskores::Get<1>(myTuple) = viskores::Vec3f(6, 7, 8);

4.1.9.3. For Each Tuple Value

The viskores::ForEach() function takes a tuple and a function or functor and calls the function for each of the items in the tuple. Nothing is returned from viskores::ForEach(), and any return value from the function is ignored.

template<typename ...Ts, typename Function>
void viskores::ForEach(const viskores::Tuple<Ts...> &tuple, Function &&f)

Call a function with each value of the given tuple.

The function calls will be done in the order of the values in the viskores::Tuple.

template<typename ...Ts, typename Function>
void viskores::ForEach(viskores::Tuple<Ts...> &tuple, Function &&f)

Call a function with each value of the given tuple.

The function calls will be done in the order of the values in the viskores::Tuple.

viskores::ForEach() can be used to check the validity of each item in a viskores::Tuple.

Example 4.29 Using viskores::Tuple::ForEach() to check the contents.
 1void CheckPositive(viskores::Float64 x)
 2{
 3  if (x < 0)
 4  {
 5    throw viskores::cont::ErrorBadValue("Values need to be positive.");
 6  }
 7}
 8
 9// ...
10
11  viskores::Tuple<viskores::Float64, viskores::Float64, viskores::Float64> tuple(
12    CreateValue(0), CreateValue(1), CreateValue(2));
13
14  // Will throw an error if any of the values are negative.
15  viskores::ForEach(tuple, CheckPositive);

viskores::ForEach() can also be used to aggregate values in a viskores::Tuple.

Example 4.30 Using viskores::Tuple::ForEach() to aggregate.
 1struct SumFunctor
 2{
 3  viskores::Float64 Sum = 0;
 4
 5  template<typename T>
 6  void operator()(const T& x)
 7  {
 8    this->Sum = this->Sum + static_cast<viskores::Float64>(x);
 9  }
10};
11
12// ...
13
14  viskores::Tuple<viskores::Float32, viskores::Float64, viskores::Id> tuple(
15    CreateValue(0), CreateValue(1), CreateValue(2));
16
17  SumFunctor sum;
18  viskores::ForEach(tuple, sum);
19  viskores::Float64 average = sum.Sum / 3;

The previous examples used an explicit struct as the functor for clarity. However, it is often less verbose to use a C++ lambda function.

Example 4.31 Using viskores::Tuple::ForEach() to aggregate.
1  viskores::Tuple<viskores::Float32, viskores::Float64, viskores::Id> tuple(
2    CreateValue(0), CreateValue(1), CreateValue(2));
3
4  viskores::Float64 sum = 0;
5  auto sumFunctor = [&sum](auto x) { sum += static_cast<viskores::Float64>(x); };
6
7  viskores::ForEach(tuple, sumFunctor);
8  viskores::Float64 average = sum / 3;

4.1.9.4. Transform Each Tuple Value

The viskores::Transform() function builds a new viskores::Tuple by calling a function or functor on each of the items in an existing viskores::Tuple. The return value is placed in the corresponding part of the resulting viskores::Tuple, and the type is automatically created from the return type of the function.

template<typename TupleType, typename Function>
auto viskores::Transform(const TupleType &&tuple, Function &&f) -> decltype(Apply(tuple, detail::TupleTransformFunctor(), std::forward<Function>(f)))

Construct a new viskores::Tuple by applying a function to each value.

The viskores::Transform function builds a new viskores::Tuple by calling a function or functor on each of the items in the given tuple. The return value is placed in the corresponding part of the resulting Tuple, and the type is automatically created from the return type of the function.

template<typename TupleType, typename Function>
auto viskores::Transform(TupleType &&tuple, Function &&f) -> decltype(Apply(tuple, detail::TupleTransformFunctor(), std::forward<Function>(f)))

Get the size of a tuple.

Given a viskores::Tuple type, becomes a std::integral_constant of the type.

Example 4.32 Transforming a viskores::Tuple.
 1struct GetReadPortalFunctor
 2{
 3  template<typename Array>
 4  typename Array::ReadPortalType operator()(const Array& array) const
 5  {
 6    VISKORES_IS_ARRAY_HANDLE(Array);
 7    return array.ReadPortal();
 8  }
 9};
10
11// ...
12
13  auto arrayTuple = viskores::MakeTuple(array1, array2, array3);
14
15  auto portalTuple = viskores::Transform(arrayTuple, GetReadPortalFunctor{});

4.1.9.5. Apply

The viskores::Apply() function calls a function or functor using the objects in a viskores::Tuple as the arguments. If the function returns a value, that value is returned from viskores::Apply().

template<typename ...Ts, typename Function, typename ...Args>
auto viskores::Apply(const viskores::Tuple<Ts...> &tuple, Function &&f, Args&&... args) -> decltype(tuple.Apply(std::forward<Function>(f), std::forward<Args>(args)...))

Call a function with the values of a viskores::Tuple as arguments.

If a viskores::Tuple<A, B, C> is given with values a, b, and c, then f will be called as f(a, b, c).

Additional arguments can optionally be given to viskores::Apply(). These arguments will be added to the beginning of the arguments to the function.

The returned value of the function (if any) will be returned from viskores::Apply().

template<typename ...Ts, typename Function, typename ...Args>
auto viskores::Apply(viskores::Tuple<Ts...> &tuple, Function &&f, Args&&... args) -> decltype(tuple.Apply(std::forward<Function>(f), std::forward<Args>(args)...))

Call a function with the values of a viskores::Tuple as arguments.

If a viskores::Tuple<A, B, C> is given with values a, b, and c, then f will be called as f(a, b, c).

Additional arguments can optionally be given to viskores::Apply(). These arguments will be added to the beginning of the arguments to the function.

The returned value of the function (if any) will be returned from viskores::Apply().

Example 4.33 Applying a viskores::Tuple as arguments to a function.
 1struct AddArraysFunctor
 2{
 3  template<typename Array1, typename Array2, typename Array3>
 4  viskores::Id operator()(Array1 inArray1, Array2 inArray2, Array3 outArray) const
 5  {
 6    VISKORES_IS_ARRAY_HANDLE(Array1);
 7    VISKORES_IS_ARRAY_HANDLE(Array2);
 8    VISKORES_IS_ARRAY_HANDLE(Array3);
 9
10    viskores::Id length = inArray1.GetNumberOfValues();
11    VISKORES_ASSERT(inArray2.GetNumberOfValues() == length);
12    outArray.Allocate(length);
13
14    auto inPortal1 = inArray1.ReadPortal();
15    auto inPortal2 = inArray2.ReadPortal();
16    auto outPortal = outArray.WritePortal();
17    for (viskores::Id index = 0; index < length; ++index)
18    {
19      outPortal.Set(index, inPortal1.Get(index) + inPortal2.Get(index));
20    }
21
22    return length;
23  }
24};
25
26// ...
27
28  auto arrayTuple = viskores::MakeTuple(array1, array2, array3);
29
30  viskores::Id arrayLength = viskores::Apply(arrayTuple, AddArraysFunctor{});

If additional arguments are given to viskores::Apply(), they are also passed to the function (before the objects in the viskores::Tuple). This is helpful for passing state to the function.

Example 4.34 Using extra arguments with viskores::Tuple::Apply().
 1struct ScanArrayLengthFunctor
 2{
 3  template<viskores::IdComponent N, typename Array, typename... Remaining>
 4  viskores::Vec<viskores::Id, N + 1 + viskores::IdComponent(sizeof...(Remaining))>
 5  operator()(const viskores::Vec<viskores::Id, N>& partialResult,
 6             const Array& nextArray,
 7             const Remaining&... remainingArrays) const
 8  {
 9    viskores::Vec<viskores::Id, N + 1> nextResult;
10    std::copy(&partialResult[0], &partialResult[0] + N, &nextResult[0]);
11    nextResult[N] = nextResult[N - 1] + nextArray.GetNumberOfValues();
12    return (*this)(nextResult, remainingArrays...);
13  }
14
15  template<viskores::IdComponent N>
16  viskores::Vec<viskores::Id, N> operator()(
17    const viskores::Vec<viskores::Id, N>& result) const
18  {
19    return result;
20  }
21};
22
23// ...
24
25  auto arrayTuple = viskores::MakeTuple(array1, array2, array3);
26
27  viskores::Vec<viskores::Id, 4> sizeScan = viskores::Apply(
28    arrayTuple, ScanArrayLengthFunctor{}, viskores::Vec<viskores::Id, 1>{ 0 });

4.1.10. Error Codes

For operations that occur in the control environment, Viskores uses exceptions to report errors as described in Chapter 2.9 (Error Handling). However, when operating in the execution environment, it is not feasible to throw exceptions. Thus, for operations designed for the execution environment, the status of an operation that can fail is returned as an viskores::ErrorCode, which is an enum.

enum class viskores::ErrorCode

Identifies whether an operation was successful or what type of error it had.

Most errors in Viskores are reported by throwing an exception. However, there are some places, most notably the execution environment, where it is not possible to throw an exception. For those cases, it is typical for a function to return an ErrorCode identifier. The calling code can check to see if the operation was a success or what kind of error was encountered otherwise.

Use the viskores::ErrorString() function to get a descriptive string of the error type.

Values:

enumerator Success

A successful operation.

This code is returned when the operation was successful. Calling code should check the error code against this identifier when checking the status.

enumerator InvalidShapeId

A unknown shape identifier was encountered.

All cell shapes must be listed in viskores::CellShapeIdEnum.

enumerator InvalidNumberOfPoints

The wrong number of points was provided for a given cell type.

For example, if a triangle has 4 points associated with it, you are likely to get this error.

enumerator InvalidCellMetric

A cell metric was requested for a cell that does not support that metric.

enumerator WrongShapeIdForTagType

This is an internal error from the lightweight cell library.

enumerator InvalidPointId

A bad point identifier was detected while operating on a cell.

enumerator InvalidEdgeId

A bad edge identifier was detected while operating on a cell.

enumerator InvalidFaceId

A bad face identifier was detected while operating on a cell.

enumerator SolutionDidNotConverge

An iterative operation did not find an appropriate solution.

This error code might be returned with some results of an iterative solution. However, solution did not appear to resolve, so the results might not be accurate.

enumerator MatrixFactorizationFailed

A solution was not found for a linear system.

Some Viskores computations use linear algebra to solve a system of equations. If the equations does not give a valid result, this error can be returned.

enumerator DegenerateCellDetected

An operation detected a degenerate cell.

A degenerate cell has two or more vertices combined into one, which changes the structure of the cell. For example, if 2 vertices of a tetrahedron are at the same point, the cell degenerates to a triangle. Degenerate cells have the potential to interfere with some computations on cells.

enumerator MalformedCellDetected

An operation detected on a malformed cell.

Most cell shapes have some assumptions about their geometry (e.g. not self intersecting). If an operation detects an expected behavior is violated, this error is returned. (Note that viskores::DegenerateCellDetected has its own error coe.)

enumerator OperationOnEmptyCell

An operation was attempted on a cell with an empty shape.

There is a special “empty” cell shape type (viskores::CellShapeTagEmpty) that can be used as a placeholder for a cell with no information. Math operations such as interpolation cannot be performed on empty cells, and attempting to do so will result in this error.

enumerator CellNotFound

A cell matching some given criteria could not be found.

enumerator InvalidNumberOfIndices

The wrong number of indices was provided for an operation.

This error code is most often used in a cell locator where no cell in the given region could be found.

enumerator Unsupported

The operation is not supported.

enumerator UnknownError

If a function or method returns an viskores::ErrorCode, it is a good practice to check to make sure that the returned value is viskores::ErrorCode::Success. If it is not, you can use the viskores::ErrorString() function to convert the viskores::ErrorCode to a descriptive C string. The easiest thing to do from within a worklet is to call the worklet’s RaiseError method.

inline const char *viskores::ErrorString(viskores::ErrorCode code) noexcept

Convert a viskores::ErrorCode into a human-readable string.

This method is useful when reporting the results of a function that failed.

Example 4.35 Checking an viskores::ErrorCode and reporting errors in a worklet.
1    viskores::ErrorCode status = cellLocator.FindCell(point, cellId, parametric);
2    if (status != viskores::ErrorCode::Success)
3    {
4      this->RaiseError(viskores::ErrorString(status));
5    }