constexpr construction for Vec classes

Vec class objects can now be constructed during compile-time
as constant expressions by calling Vec( T, ... ) constructors
or through brace-initialization.
Constant expression using fill constructor and nested vectors
of sizes greater than 4 are not supported yet.
Changes made to WrappedOperators.h for resolving overload
ambiguities in Vec construction and typecasting.
Appropriate test cases were added to UnitTestTypes.cxx.
Addresses issue #199.
This commit is contained in:
Shreeraj Jadhav 2018-06-06 15:50:20 -04:00
parent 7de4bba7e9
commit 947496550e
3 changed files with 148 additions and 69 deletions

@ -613,11 +613,12 @@ public:
using ComponentType = T;
static constexpr vtkm::IdComponent NUM_COMPONENTS = Size;
protected:
VecBase() = default;
VTKM_EXEC_CONT
explicit VecBase(const ComponentType& value)
// The enable_if predicate will disable this constructor for Size=1 so that
// the variadic constructor constexpr VecBase(T, Ts&&...) is called instead.
template <vtkm::IdComponent Size2 = Size, typename std::enable_if<Size2 != 1, int>::type = 0>
VTKM_EXEC_CONT explicit VecBase(const ComponentType& value)
{
for (vtkm::IdComponent i = 0; i < Size; ++i)
{
@ -625,6 +626,13 @@ protected:
}
}
template <typename... Ts>
VTKM_EXEC_CONT constexpr VecBase(ComponentType value0, Ts&&... values)
: Components{ value0, values... }
{
VTKM_STATIC_ASSERT(sizeof...(Ts) + 1 == Size);
}
VTKM_EXEC_CONT
VecBase(std::initializer_list<ComponentType> values)
{
@ -812,21 +820,15 @@ public:
static constexpr vtkm::IdComponent NUM_COMPONENTS = Size;
#endif
using Superclass::Superclass;
Vec() = default;
VTKM_EXEC_CONT explicit Vec(const T& value)
: 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)
: Superclass(src)
#if defined(_MSC_VER) && _MSC_VER < 1910
template <typename... Ts>
constexpr Vec(T value, Ts&&... values)
: Superclass(value, std::forward<Ts>(values)...)
{
}
#endif
inline VTKM_EXEC_CONT void CopyInto(Vec<T, Size>& dest) const { dest = *this; }
};
@ -876,14 +878,10 @@ class VTKM_ALWAYS_EXPORT Vec<T, 1> : public detail::VecBase<T, 1, Vec<T, 1>>
public:
Vec() = default;
VTKM_EXEC_CONT explicit Vec(const T& value)
VTKM_EXEC_CONT constexpr Vec(const T& value)
: 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)
@ -902,14 +900,10 @@ class VTKM_ALWAYS_EXPORT Vec<T, 2> : public detail::VecBase<T, 2, Vec<T, 2>>
public:
Vec() = default;
VTKM_EXEC_CONT explicit Vec(const T& value)
VTKM_EXEC_CONT Vec(const T& value)
: 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)
@ -918,10 +912,9 @@ public:
}
VTKM_EXEC_CONT
Vec(const T& x, const T& y)
constexpr Vec(const T& x, const T& y)
: Superclass(x, y)
{
this->Components[0] = x;
this->Components[1] = y;
}
};
@ -935,14 +928,10 @@ class VTKM_ALWAYS_EXPORT Vec<T, 3> : public detail::VecBase<T, 3, Vec<T, 3>>
public:
Vec() = default;
VTKM_EXEC_CONT explicit Vec(const T& value)
VTKM_EXEC_CONT Vec(const T& value)
: 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)
@ -951,11 +940,9 @@ public:
}
VTKM_EXEC_CONT
Vec(const T& x, const T& y, const T& z)
constexpr Vec(const T& x, const T& y, const T& z)
: Superclass(x, y, z)
{
this->Components[0] = x;
this->Components[1] = y;
this->Components[2] = z;
}
};
@ -970,14 +957,10 @@ class VTKM_ALWAYS_EXPORT Vec<T, 4> : public detail::VecBase<T, 4, Vec<T, 4>>
public:
Vec() = default;
VTKM_EXEC_CONT explicit Vec(const T& value)
VTKM_EXEC_CONT Vec(const T& value)
: 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)
@ -986,32 +969,20 @@ public:
}
VTKM_EXEC_CONT
Vec(const T& x, const T& y, const T& z, const T& w)
constexpr Vec(const T& x, const T& y, const T& z, const T& w)
: Superclass(x, y, z, w)
{
this->Components[0] = x;
this->Components[1] = y;
this->Components[2] = z;
this->Components[3] = w;
}
};
/// 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 <vtkm::IdComponent Size, typename T>
VTKM_EXEC_CONT vtkm::Vec<T, Size> make_Vec(std::initializer_list<T> values)
{
return vtkm::Vec<T, Size>(values);
}
/// 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, typename... Ts>
VTKM_EXEC_CONT vtkm::Vec<T, vtkm::IdComponent(sizeof...(Ts) + 1)> make_Vec(const T& value0,
const Ts&... values)
VTKM_EXEC_CONT constexpr vtkm::Vec<T, vtkm::IdComponent(sizeof...(Ts) + 1)> make_Vec(T value0,
Ts&&... args)
{
return vtkm::Vec<T, vtkm::IdComponent(sizeof...(Ts) + 1)>({ value0, values... });
return vtkm::Vec<T, vtkm::IdComponent(sizeof...(Ts) + 1)>(value0, T(args)...);
}
/// \brief A Vec-like representation for short arrays.

@ -111,19 +111,25 @@ struct WrappedBinaryOperator
template <typename U>
VTKM_EXEC T operator()(const T& x, const PortalValue<U>& y) const
{
return m_f(x, (T)y);
// to support proper implicit conversion, and avoid overload
// ambiguities.
T conv_y = y;
return m_f(x, conv_y);
}
template <typename U>
VTKM_EXEC T operator()(const PortalValue<U>& x, const T& y) const
{
return m_f((T)x, y);
T conv_x = x;
return m_f(conv_x, y);
}
template <typename U, typename V>
VTKM_EXEC T operator()(const PortalValue<U>& x, const PortalValue<V>& y) const
{
return m_f((T)x, (T)y);
T conv_x = x;
T conv_y = y;
return m_f(conv_x, conv_y);
}
VTKM_EXEC T operator()(const T* const x, const T& y) const { return m_f(*x, y); }

@ -442,6 +442,19 @@ void TypeTest(const vtkm::Vec<ComponentType, Size>&)
GeneralVecTypeTest(vtkm::Vec<ComponentType, Size>());
}
template <typename Scalar>
void TypeTest(const vtkm::Vec<Scalar, 1>&)
{
using Vector = vtkm::Vec<Scalar, 1>;
std::cout << "Checking constexpr construction for Vec1." << std::endl;
constexpr Vector constExprVec1(Scalar(1));
constexpr Vector constExprVec2 = { Scalar(1) };
constexpr Vector madeVec = vtkm::make_Vec(Scalar(1));
VTKM_TEST_ASSERT(test_equal(constExprVec1, madeVec), "constexpr Vec1 failed equality test.");
VTKM_TEST_ASSERT(test_equal(constExprVec2, madeVec), "constexpr Vec1 failed equality test.");
}
template <typename Scalar>
void TypeTest(const vtkm::Vec<Scalar, 2>&)
{
@ -454,8 +467,6 @@ void TypeTest(const vtkm::Vec<Scalar, 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.");
@ -507,6 +518,18 @@ void TypeTest(const vtkm::Vec<Scalar, 2>&)
VTKM_TEST_ASSERT((c != a), "operator != wrong");
VTKM_TEST_ASSERT((a != c), "operator != wrong");
std::cout << "Checking constexpr construction for Vec2." << std::endl;
constexpr Vector constExprVec1(Scalar(1), Scalar(2));
constexpr Vector constExprVec2 = { Scalar(1), Scalar(2) };
constexpr Vector madeVec = vtkm::make_Vec(Scalar(1), Scalar(2));
VTKM_TEST_ASSERT(test_equal(constExprVec1, madeVec), "constexpr Vec2 failed equality test.");
VTKM_TEST_ASSERT(test_equal(constExprVec2, madeVec), "constexpr Vec2 failed equality test.");
// Check fill constructor.
Vector fillVec1 = { Scalar(8) };
Vector fillVec2(Scalar(8), Scalar(8));
VTKM_TEST_ASSERT(test_equal(fillVec1, fillVec2), "fill ctor Vec2 failed equality test.");
}
template <typename Scalar>
@ -522,8 +545,6 @@ void TypeTest(const vtkm::Vec<Scalar, 3>&)
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.");
@ -576,6 +597,18 @@ void TypeTest(const vtkm::Vec<Scalar, 3>&)
VTKM_TEST_ASSERT((c != a), "operator != wrong");
VTKM_TEST_ASSERT((a != c), "operator != wrong");
std::cout << "Checking constexpr construction for Vec3." << std::endl;
constexpr Vector constExprVec1(Scalar(1), Scalar(2), Scalar(3));
constexpr Vector constExprVec2 = { Scalar(1), Scalar(2), Scalar(3) };
constexpr Vector madeVec = vtkm::make_Vec(Scalar(1), Scalar(2), Scalar(3));
VTKM_TEST_ASSERT(test_equal(constExprVec1, madeVec), "constexpr Vec3 failed equality test.");
VTKM_TEST_ASSERT(test_equal(constExprVec2, madeVec), "constexpr Vec3 failed equality test.");
// Check fill constructor.
Vector fillVec1 = { Scalar(8) };
Vector fillVec2(Scalar(8), Scalar(8), Scalar(8));
VTKM_TEST_ASSERT(test_equal(fillVec1, fillVec2), "fill ctor Vec3 failed equality test.");
}
template <typename Scalar>
@ -591,8 +624,6 @@ void TypeTest(const vtkm::Vec<Scalar, 4>&)
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.");
@ -645,6 +676,41 @@ void TypeTest(const vtkm::Vec<Scalar, 4>&)
VTKM_TEST_ASSERT((c != a), "operator != wrong");
VTKM_TEST_ASSERT((a != c), "operator != wrong");
std::cout << "Checking constexpr construction for Vec4." << std::endl;
constexpr Vector constExprVec1(Scalar(1), Scalar(2), Scalar(3), Scalar(4));
constexpr Vector constExprVec2 = { Scalar(1), Scalar(2), Scalar(3), Scalar(4) };
constexpr Vector madeVec = vtkm::make_Vec(Scalar(1), Scalar(2), Scalar(3), Scalar(4));
VTKM_TEST_ASSERT(test_equal(constExprVec1, madeVec), "constexpr Vec4 failed equality test.");
VTKM_TEST_ASSERT(test_equal(constExprVec2, madeVec), "constexpr Vec4 failed equality test.");
// Check fill constructor.
Vector fillVec1 = { Scalar(8) };
Vector fillVec2(Scalar(8), Scalar(8), Scalar(8), Scalar(8));
VTKM_TEST_ASSERT(test_equal(fillVec1, fillVec2), "fill ctor Vec4 failed equality test.");
Scalar values[4] = { Scalar(1), Scalar(1), Scalar(1), Scalar(1) };
Vector lvalVec1 = vtkm::make_Vec(values[0], values[1], values[2], values[3]);
Vector lvalVec2 = Vector(values[0], values[1], values[2], values[3]);
VTKM_TEST_ASSERT(test_equal(lvalVec1, lvalVec2), "lvalue ctor Vec4 failed equality test.");
}
template <typename Scalar>
void TypeTest(const vtkm::Vec<Scalar, 6>&)
{
using Vector = vtkm::Vec<Scalar, 6>;
std::cout << "Checking constexpr construction for Vec6." << std::endl;
constexpr Vector constExprVec1(Scalar(1), Scalar(2), Scalar(3), Scalar(4), Scalar(5), Scalar(6));
Vector braceVec = { Scalar(1), Scalar(2), Scalar(3), Scalar(4), Scalar(5), Scalar(6) };
constexpr Vector madeVec =
vtkm::make_Vec(Scalar(1), Scalar(2), Scalar(3), Scalar(4), Scalar(5), Scalar(6));
VTKM_TEST_ASSERT(test_equal(constExprVec1, madeVec), "constexpr Vec6 failed equality test.");
VTKM_TEST_ASSERT(test_equal(braceVec, madeVec), "constexpr Vec6 failed equality test.");
// Check fill constructor.
Vector fillVec1 = { Scalar(8) };
Vector fillVec2 = Vector(Scalar(8), Scalar(8), Scalar(8), Scalar(8), Scalar(8), Scalar(8));
VTKM_TEST_ASSERT(test_equal(fillVec1, fillVec2), "fill ctor Vec6 failed equality test.");
}
template <typename Scalar>
@ -759,6 +825,40 @@ void TypeTest(vtkm::Vec<vtkm::Vec<Scalar, 2>, 3>)
//Vector vec1 = { 0, 1, 2 };
//Vector vec2 = { 0, 1 };
}
{
std::cout << "Checking constexpr construction for Vec3<Vec2>." << std::endl;
constexpr Vector constExprVec1(
vtkm::Vec<Scalar, 2>(1, 2), vtkm::Vec<Scalar, 2>(1, 2), vtkm::Vec<Scalar, 2>(1, 2));
constexpr Vector constExprVec2 = { { 1, 2 }, { 1, 2 }, { 1, 2 } };
constexpr Vector madeVec = vtkm::make_Vec(vtkm::make_Vec(Scalar(1), Scalar(2)),
vtkm::make_Vec(Scalar(1), Scalar(2)),
vtkm::make_Vec(Scalar(1), Scalar(2)));
VTKM_TEST_ASSERT(test_equal(constExprVec1, madeVec),
"constexpr Vec3<Vec2> failed equality test.");
VTKM_TEST_ASSERT(test_equal(constExprVec2, madeVec),
"constexpr Vec3<Vec2> failed equality test.");
// Check fill constructor.
Vector fillVec1 = { { Scalar(1), Scalar(2) } };
Vector fillVec2(
vtkm::Vec<Scalar, 2>(1, 2), vtkm::Vec<Scalar, 2>(1, 2), vtkm::Vec<Scalar, 2>(1, 2));
VTKM_TEST_ASSERT(test_equal(fillVec1, fillVec2), "fill ctor Vec3ofVec2 failed equality test.");
}
}
template <typename Scalar>
void TypeTest(vtkm::Vec<vtkm::Vec<Scalar, 2>, 5>)
{
using Vector = vtkm::Vec<vtkm::Vec<Scalar, 2>, 5>;
Vector braceVec = { { 1, 1 }, { 2, 2 }, { 3, 3 }, { 4, 4 }, { 5, 5 } };
constexpr Vector constExprVec = vtkm::make_Vec(vtkm::make_Vec(Scalar(1), Scalar(1)),
vtkm::make_Vec(Scalar(2), Scalar(2)),
vtkm::make_Vec(Scalar(3), Scalar(3)),
vtkm::make_Vec(Scalar(4), Scalar(4)),
vtkm::make_Vec(Scalar(5), Scalar(5)));
VTKM_TEST_ASSERT(test_equal(constExprVec, braceVec), "Vec5<Vec2> failed equality test.");
}
struct TypeTestFunctor
@ -775,9 +875,11 @@ struct TypesToTest : vtkm::ListTagJoin<vtkm::testing::Testing::TypeListTagExempl
vtkm::Vec<vtkm::Id, 4>,
vtkm::Vec<unsigned char, 4>,
vtkm::Vec<vtkm::Id, 1>,
vtkm::Vec<vtkm::Id, 2>,
vtkm::Vec<vtkm::Float64, 1>,
vtkm::Vec<vtkm::Id2, 3>,
vtkm::Vec<vtkm::Vec<vtkm::Float32, 2>, 3>>>
vtkm::Vec<vtkm::Vec<vtkm::Float32, 2>, 3>,
vtkm::Vec<vtkm::Vec<vtkm::Float32, 2>, 5>>>
{
};