Add support for initializer lists in Vec

Add constructors to the `vtkm::Vec` classes that accept
`std::initializer_list`. The main advantage of this addition is that it
makes it much easier to initialize `Vec`s of arbitrary length.
This commit is contained in:
Kenneth Moreland 2018-05-30 16:49:22 -06:00
parent 2b10108a32
commit ae8d994d21
4 changed files with 150 additions and 17 deletions

@ -0,0 +1,86 @@
# Support initializer lists for Vec
Add constructors to the `vtkm::Vec` classes that accept `std::initializer_list`. The
main advantage of this addition is that it makes it much easier to initialize `Vec`s
of arbitrary length.
Although previously some `Vec` classes could be constructed with values listed in
their parameters, that only worked for initializes up to size 4.
``` cpp
vtkm::Vec<vtkm::Float64, 3> vec1{1.1, 2.2, 3.3}; // New better initializer
vtkm::Vec<vtkm::Float64, 3> vec2 = {1.1, 2.2, 3.3}; // Nice syntax also supported by
// initializer lists.
vtkm::Vec<vtkm::Float64, 3> vec3(1.1, 2.2, 3.3); // Old style that still works but
// probably should be deprecated.
```
The nice thing about the initializer list implementation is that it works for any
size `Vec`. That keeps us from jumping through hoops for larger `Vec`s.
``` cpp
vtkm::Vec<vtkm::Float64, 5> vec1{1.1, 2.2, 3.3, 4.4, 5.5}; // Works fine.
vtkm::Vec<vtkm::Float64, 5> vec2(1.1, 2.2, 3.3, 4.4, 5.5); // ERROR! This constructor
// not implemented!
```
`vtkm::make_Vec` is also updated to support an arbitrary number initial values.
``` cpp
// Creates a vtkm::Vec<vtkm::Float64, 5>
auto vec = vtkm::make_Vec(1.1, 2.2, 3.3, 4.4, 5.5);
```
This is super convenient when dealing with variadic function arguments.
``` cpp
template <typename... Ts>
void ExampleVariadicFunction(const Ts&... params)
{
auto vec = vtkm::make_Vec(params);
```
Of course, this assumes that the type of all the parameters is the same. If not, you
could run into compiler trouble.
There is also a version of `vtkm::make_Vec` that accepts an `std::initializer_list`,
but it is not very useful because you have to separately specify the length of the
`Vec` (due to some limitations of `std::initializer_list` in C++11).
``` cpp
// Creates a vtkm::Vec<vtkm::Float64, 3>
auto vec1 = vtkm::make_Vec<3>({1.1, 2.2, 3.3});
// Creates exactly the same thing
auto vec2 = vtkm::Vec<vtkm::Float64, 3>{1.1, 2.2, 3.3};
```
A limitation of the initializer list constructor is that the compiler has no way to
check the length of the list or force it to a particular length. Thus it is entirely
possible to construct a `Vec` with the wrong number of arguments. Or, more to the
point, the compiler will let you do it, but there is an assert in the constructor to
correct for that. (Of course, asserts are not compiled in release builds.)
``` cpp
// This will compile, but it's results are undefined when it is run.
// In debug builds, it will fail an assert.
vtkm::Vec<vtkm::Float64, 3> vec{1.1, 1.2};
```
There is a distinction between the initializer list constructors and all other types
of constructors. Specifically, the initializer list constructor sets each component
to each value in the initializer list (much like initializing an array). In
particular, there is a considerable difference between the initializer list
constructor and the constructor taking one value that fills up the array. Don't use
the wrong one.
``` cpp
// This creates a vector filled with [3.14, 3.14, 3.14]
vtkm::Vec<vtkm::Float64, 3> vec1(3.14);
// The result of this is underfined. In debug builds, it will fail an assert.
vtkm::Vec<vtkm::Float64, 3> vec1{3.14};
```

@ -625,6 +625,19 @@ protected:
}
}
VTKM_EXEC_CONT
VecBase(std::initializer_list<ComponentType> values)
{
VTKM_ASSERT((values.size() == NUM_COMPONENTS) &&
"Vec object initialized wrong number of components.");
ComponentType* dest = this->Components;
for (auto src = values.begin(); src != values.end(); ++src)
{
*dest = *src;
++dest;
}
}
#if (!(defined(VTKM_CUDA) && (__CUDACC_VER_MAJOR__ < 8)))
#if (defined(VTKM_GCC) || defined(VTKM_CLANG))
#pragma GCC diagnostic push
@ -792,6 +805,10 @@ public:
: Superclass(value)
{
}
VTKM_EXEC_CONT Vec(std::initializer_list<T> values)
: Superclass(values)
{
}
template <typename OtherType>
VTKM_EXEC_CONT explicit Vec(const Vec<OtherType, Size>& src)
@ -851,6 +868,10 @@ public:
: Superclass(value)
{
}
VTKM_EXEC_CONT Vec(std::initializer_list<T> values)
: Superclass(values)
{
}
template <typename OtherType>
VTKM_EXEC_CONT Vec(const Vec<OtherType, 1>& src)
@ -873,6 +894,10 @@ public:
: Superclass(value)
{
}
VTKM_EXEC_CONT Vec(std::initializer_list<T> values)
: Superclass(values)
{
}
template <typename OtherType>
VTKM_EXEC_CONT Vec(const Vec<OtherType, 2>& src)
@ -902,6 +927,10 @@ public:
: Superclass(value)
{
}
VTKM_EXEC_CONT Vec(std::initializer_list<T> values)
: Superclass(values)
{
}
template <typename OtherType>
VTKM_EXEC_CONT Vec(const Vec<OtherType, 3>& src)
@ -933,6 +962,10 @@ public:
: Superclass(value)
{
}
VTKM_EXEC_CONT Vec(std::initializer_list<T> values)
: Superclass(values)
{
}
template <typename OtherType>
VTKM_EXEC_CONT Vec(const Vec<OtherType, 4>& src)
@ -950,28 +983,22 @@ public:
}
};
/// Initializes and returns a Vec of length 2.
/// Initializes and returns a Vec initialized by the given intializer_list. Due to limitations in
/// C++11, you also have to specify the length of the Vec.
///
template <typename T>
VTKM_EXEC_CONT vtkm::Vec<T, 2> make_Vec(const T& x, const T& y)
template <vtkm::IdComponent Size, typename T>
VTKM_EXEC_CONT vtkm::Vec<T, Size> make_Vec(std::initializer_list<T> values)
{
return vtkm::Vec<T, 2>(x, y);
return vtkm::Vec<T, Size>(values);
}
/// Initializes and returns a Vec of length 3.
/// Initializes and returns a Vec containing all the arguments. The arguments should all be the
/// same type or compile issues will occur.
///
template <typename T>
VTKM_EXEC_CONT vtkm::Vec<T, 3> make_Vec(const T& x, const T& y, const T& z)
template <typename T, typename... Ts>
VTKM_EXEC_CONT vtkm::Vec<T, sizeof...(Ts) + 1> make_Vec(const T& value0, const Ts&... values)
{
return vtkm::Vec<T, 3>(x, y, z);
}
/// Initializes and returns a Vec of length 4.
///
template <typename T>
VTKM_EXEC_CONT vtkm::Vec<T, 4> make_Vec(const T& x, const T& y, const T& z, const T& w)
{
return vtkm::Vec<T, 4>(x, y, z, w);
return vtkm::Vec<T, sizeof...(Ts) + 1>({ value0, values... });
}
/// \brief A Vec-like representation for short arrays.

@ -453,6 +453,12 @@ void TypeTest(const vtkm::Vec<Scalar, 2>&)
Vector b(1, 2);
Scalar s = 5;
VTKM_TEST_ASSERT(a == vtkm::make_Vec(Scalar(2), Scalar(4)), "make_Vec creates different object.");
VTKM_TEST_ASSERT(a == vtkm::make_Vec<2>({ Scalar(2), Scalar(4) }),
"make_Vec creates different object.");
VTKM_TEST_ASSERT((a == vtkm::Vec<Scalar, 2>{ Scalar(2), Scalar(4) }),
"Construct with initializer list creates different object.");
Vector plus = a + b;
VTKM_TEST_ASSERT(test_equal(plus, vtkm::make_Vec(3, 6)), "Vectors do not add correctly.");
@ -514,6 +520,13 @@ void TypeTest(const vtkm::Vec<Scalar, 3>&)
Vector b(1, 2, 3);
Scalar s = 5;
VTKM_TEST_ASSERT(a == vtkm::make_Vec(Scalar(2), Scalar(4), Scalar(6)),
"make_Vec creates different object.");
VTKM_TEST_ASSERT(a == vtkm::make_Vec<3>({ Scalar(2), Scalar(4), Scalar(6) }),
"make_Vec creates different object.");
VTKM_TEST_ASSERT((a == vtkm::Vec<Scalar, 3>{ Scalar(2), Scalar(4), Scalar(6) }),
"Construct with initializer list creates different object.");
Vector plus = a + b;
VTKM_TEST_ASSERT(test_equal(plus, vtkm::make_Vec(3, 6, 9)), "Vectors do not add correctly.");
@ -576,6 +589,13 @@ void TypeTest(const vtkm::Vec<Scalar, 4>&)
Vector b(1, 2, 3, 4);
Scalar s = 5;
VTKM_TEST_ASSERT(a == vtkm::make_Vec(Scalar(2), Scalar(4), Scalar(6), Scalar(8)),
"make_Vec creates different object.");
VTKM_TEST_ASSERT(a == vtkm::make_Vec<4>({ Scalar(2), Scalar(4), Scalar(6), Scalar(8) }),
"make_Vec creates different object.");
VTKM_TEST_ASSERT((a == vtkm::Vec<Scalar, 4>{ Scalar(2), Scalar(4), Scalar(6), Scalar(8) }),
"Construct with initializer list creates different object.");
Vector plus = a + b;
VTKM_TEST_ASSERT(test_equal(plus, vtkm::make_Vec(3, 6, 9, 12)), "Vectors do not add correctly.");

@ -49,7 +49,7 @@ struct ShiftScaleToRGB : public vtkm::worklet::WorkletMapField
{ //vtkScalarsToColorsLuminanceToRGB
vtkm::Float32 l = (static_cast<vtkm::Float32>(in) + this->Shift) * this->Scale;
colorconversion::Clamp(l);
return vtkm::Vec<vtkm::UInt8, 3>{ static_cast<vtkm::UInt8>(l + 0.5f) };
return vtkm::Vec<vtkm::UInt8, 3>(static_cast<vtkm::UInt8>(l + 0.5f));
}
template <typename T>