Check value type in Invoke input arrays.

Instead of just checking that a dispatcher's Invoke input is an
ArrayHandle, also check that the ValueType of the ArrayHandle is
compatible with the types of the worklet operator. This is done by
adding a template argument to the ControlSignature tags that is a type
list tag that gets passed to the type check.
This commit is contained in:
Kenneth Moreland 2014-10-22 16:51:42 -06:00
parent f4fb9f0ace
commit 6b1db2cf04
5 changed files with 213 additions and 50 deletions

@ -22,6 +22,8 @@
#include <vtkm/cont/arg/TypeCheck.h>
#include <vtkm/ListTag.h>
#include <vtkm/cont/ArrayHandle.h>
namespace vtkm {
@ -31,13 +33,40 @@ namespace arg {
/// The Array type check passes for any object that behaves like an \c
/// ArrayHandle class and can be passed to the ArrayIn and ArrayOut transports.
///
struct TypeCheckTagArray { };
template<typename TypeList>
struct TypeCheckTagArray
{
VTKM_IS_LIST_TAG(TypeList);
};
template<typename Type>
struct TypeCheck<TypeCheckTagArray, Type>
namespace detail {
template<typename TypeList, typename ArrayType, bool IsArray>
struct TypeCheckArrayValueType;
template<typename TypeList, typename ArrayType>
struct TypeCheckArrayValueType<TypeList, ArrayType, true>
{
static const bool value =
vtkm::cont::internal::ArrayHandleCheck<Type>::type::value;
vtkm::ListContains<TypeList,typename ArrayType::ValueType>::value;
};
template<typename TypeList, typename ArrayType>
struct TypeCheckArrayValueType<TypeList, ArrayType, false>
{
static const bool value = false;
};
} // namespace detail
template<typename TypeList, typename ArrayType>
struct TypeCheck<TypeCheckTagArray<TypeList>, ArrayType>
{
static const bool value =
detail::TypeCheckArrayValueType<
TypeList,
ArrayType,
vtkm::cont::internal::ArrayHandleCheck<ArrayType>::type::value>::value;
};
}

@ -34,7 +34,8 @@ struct TryArraysOfType
void operator()(T) const
{
using vtkm::cont::arg::TypeCheck;
using vtkm::cont::arg::TypeCheckTagArray;
typedef vtkm::cont::arg::TypeCheckTagArray<vtkm::TypeListTagAll>
TypeCheckTagArray;
typedef vtkm::cont::ArrayHandle<T> StandardArray;
VTKM_TEST_ASSERT((TypeCheck<TypeCheckTagArray,StandardArray>::value),
@ -55,7 +56,6 @@ struct TryArraysOfType
"Not an array type check failed.");
// Another type that is not a valid array.
typedef typename StandardArray::PortalControl NotAnArray;
VTKM_TEST_ASSERT(!(TypeCheck<TypeCheckTagArray,T>::value),
"Not an array type check failed.");
}
@ -64,6 +64,27 @@ struct TryArraysOfType
void TestCheckArray()
{
vtkm::testing::Testing::TryAllTypes(TryArraysOfType());
std::cout << "Trying some arrays with types that do not match the list."
<< std::endl;
using vtkm::cont::arg::TypeCheck;
using vtkm::cont::arg::TypeCheckTagArray;
typedef vtkm::cont::ArrayHandle<vtkm::FloatDefault> ScalarArray;
VTKM_TEST_ASSERT(
(TypeCheck<TypeCheckTagArray<vtkm::TypeListTagFieldScalar>,ScalarArray>::value),
"Scalar for scalar check failed.");
VTKM_TEST_ASSERT(
!(TypeCheck<TypeCheckTagArray<vtkm::TypeListTagFieldVec3>,ScalarArray>::value),
"Scalar for vector check failed.");
typedef vtkm::cont::ArrayHandle<vtkm::Vec<vtkm::FloatDefault,3> > VecArray;
VTKM_TEST_ASSERT(
(TypeCheck<TypeCheckTagArray<vtkm::TypeListTagFieldVec3>,VecArray>::value),
"Vector for vector check failed.");
VTKM_TEST_ASSERT(
!(TypeCheck<TypeCheckTagArray<vtkm::TypeListTagFieldScalar>,VecArray>::value),
"Vector for scalar check failed.");
}
} // anonymous namespace

@ -22,6 +22,8 @@
#include <vtkm/worklet/internal/WorkletBase.h>
#include <vtkm/TypeListTag.h>
#include <vtkm/cont/arg/TransportTagArrayIn.h>
#include <vtkm/cont/arg/TransportTagArrayOut.h>
#include <vtkm/cont/arg/TypeCheckTagArray.h>
@ -39,16 +41,26 @@ namespace worklet {
class WorkletMapField : public vtkm::worklet::internal::WorkletBase
{
public:
/// A control signature tag for input fields.
/// \brief A control signature tag for input fields.
///
/// This tag takes a template argument that is a type list tag that limits
/// the possible value types in the array.
///
template<typename TypeList = AllTypes>
struct FieldIn {
typedef vtkm::cont::arg::TypeCheckTagArray TypeCheckTag;
typedef vtkm::cont::arg::TypeCheckTagArray<TypeList> TypeCheckTag;
typedef vtkm::cont::arg::TransportTagArrayIn TransportTag;
typedef vtkm::exec::arg::FetchTagArrayDirectIn FetchTag;
};
/// A control signature tag for output fields.
/// \brief A control signature tag for output fields.
///
/// This tag takes a template argument that is a type list tag that limits
/// the possible value types in the array.
///
template<typename TypeList = AllTypes>
struct FieldOut {
typedef vtkm::cont::arg::TypeCheckTagArray TypeCheckTag;
typedef vtkm::cont::arg::TypeCheckTagArray<TypeList> TypeCheckTag;
typedef vtkm::cont::arg::TransportTagArrayOut TransportTag;
typedef vtkm::exec::arg::FetchTagArrayDirectOut FetchTag;
};

@ -20,6 +20,8 @@
#ifndef vtk_m_worklet_internal_WorkletBase_h
#define vtk_m_worklet_internal_WorkletBase_h
#include <vtkm/TypeListTag.h>
#include <vtkm/exec/FunctorBase.h>
#include <vtkm/exec/arg/BasicArg.h>
#include <vtkm/exec/arg/FetchTagExecObject.h>
@ -66,6 +68,90 @@ public:
/// Default input domain is the first argument. Worklet subclasses can
/// override this by redefining this type.
typedef _1 InputDomain;
/// \brief A type list containing the type vtkm::Id.
///
/// This is a convenience type to use as template arguments to \c
/// ControlSignature tags to specify the types of worklet arguments.
typedef vtkm::TypeListTagId IdType;
/// \brief A type list containing the type vtkm::Id2.
///
/// This is a convenience type to use as template arguments to \c
/// ControlSignature tags to specify the types of worklet arguments.
typedef vtkm::TypeListTagId2 Id2Type;
/// \brief A type list containing the type vtkm::Id3.
///
/// This is a convenience type to use as template arguments to \c
/// ControlSignature tags to specify the types of worklet arguments.
typedef vtkm::TypeListTagId3 Id3Type;
/// \brief A list of types commonly used for indexing.
///
/// This is a convenience type to use as template arguments to \c
/// ControlSignature tags to specify the types of worklet arguments.
typedef vtkm::TypeListTagIndex Index;
/// \brief A list of types commonly used for scalar fields.
///
/// This is a convenience type to use as template arguments to \c
/// ControlSignature tags to specify the types of worklet arguments.
typedef vtkm::TypeListTagFieldScalar Scalar;
/// \brief A list of all basic types used for scalar fields.
///
/// This is a convenience type to use as template arguments to \c
/// ControlSignature tags to specify the types of worklet arguments.
typedef vtkm::TypeListTagScalarAll ScalarAll;
/// \brief A list of types commonly used for vector fields of 2 components.
///
/// This is a convenience type to use as template arguments to \c
/// ControlSignature tags to specify the types of worklet arguments.
typedef vtkm::TypeListTagFieldVec2 Vec2;
/// \brief A list of types commonly used for vector fields of 3 components.
///
/// This is a convenience type to use as template arguments to \c
/// ControlSignature tags to specify the types of worklet arguments.
typedef vtkm::TypeListTagFieldVec3 Vec3;
/// \brief A list of types commonly used for vector fields of 4 components.
///
/// This is a convenience type to use as template arguments to \c
/// ControlSignature tags to specify the types of worklet arguments.
typedef vtkm::TypeListTagFieldVec4 Vec4;
/// \brief A list of all basic types used for vector fields.
///
/// This is a convenience type to use as template arguments to \c
/// ControlSignature tags to specify the types of worklet arguments.
typedef vtkm::TypeListTagVecAll VecAll;
/// \brief A list of types (scalar and vector) commonly used in fields.
///
/// This is a convenience type to use as template arguments to \c
/// ControlSignature tags to specify the types of worklet arguments.
typedef vtkm::TypeListTagField FieldCommon;
/// \brief A list of vector types commonly used in fields.
///
/// This is a convenience type to use as template arguments to \c
/// ControlSignature tags to specify the types of worklet arguments.
typedef vtkm::TypeListTagVecCommon VecCommon;
/// \brief A list of generally common types.
///
/// This is a convenience type to use as template arguments to \c
/// ControlSignature tags to specify the types of worklet arguments.
typedef vtkm::TypeListTagCommon CommonTypes;
/// \brief A list of all basic types.
///
/// This is a convenience type to use as template arguments to \c
/// ControlSignature tags to specify the types of worklet arguments.
typedef vtkm::TypeListTagAll AllTypes;
};
}

@ -33,20 +33,44 @@ static const vtkm::Id ARRAY_SIZE = 10;
class TestWorklet : public vtkm::worklet::WorkletMapField
{
public:
typedef void ControlSignature(FieldIn, FieldOut);
typedef _2 ExecutionSignature(_1, WorkIndex);
typedef void ControlSignature(FieldIn<>, FieldOut<>);
typedef void ExecutionSignature(_1, _2, WorkIndex);
template<typename T>
T operator()(T x, vtkm::Id workIndex) const
void operator()(const T &in, T &out, vtkm::Id workIndex) const
{
if (x != TestValue(workIndex, T()) + T(100))
if (in != TestValue(workIndex, T()) + T(100))
{
this->RaiseError("Got wrong input value.");
}
return x - T(100);
out = in - T(100);
}
template<typename T1, typename T2>
void operator()(const T1 &, const T2 &, vtkm::Id) const
{
this->RaiseError("Cannot call this worklet with different types.");
}
};
class TestWorkletLimitedTypes : public vtkm::worklet::WorkletMapField
{
public:
typedef void ControlSignature(FieldIn<ScalarAll>, FieldOut<ScalarAll>);
typedef _2 ExecutionSignature(_1, WorkIndex);
template<typename T>
T operator()(const T &in, vtkm::Id workIndex) const
{
if (in != TestValue(workIndex, T()) + T(100))
{
this->RaiseError("Got wrong input value.");
}
return in - T(100);
}
};
template<typename WorkletType>
struct DoTestWorklet
{
template<typename T>
@ -66,51 +90,42 @@ struct DoTestWorklet
vtkm::cont::ArrayHandle<T> outputHandle;
std::cout << "Create and run dispatcher." << std::endl;
vtkm::worklet::DispatcherMapField<TestWorklet> dispatcher;
vtkm::worklet::DispatcherMapField<WorkletType> dispatcher;
dispatcher.Invoke(inputHandle, outputHandle);
std::cout << "Check result." << std::endl;
CheckPortal(outputHandle.GetPortalConstControl());
// The following test is commented out because as of this writing
// (10-21-2014) there is an issue with getting unexpected types when
// casting dynamic arrays. In particular, this issue is with using dynamic
// arrays. We know that both arrays will always be the same type, but the
// arrays are cast independently. Thus, the compiler will generate code for
// odd combinations that are incompatibile with each other. Thus, we need a
// way to better specify the types expected by the worklet function. I can
// think of two general ways (there might be more).
//
// 1. Specify the expected type in the ControlSignature. This would
// probably be a template argument of the tag with a list of basic types
// that could be in the array. The dynamic array casting would then take
// that into account and only try those specified types. That should be
// fairly straightforward to implement and handle many cases. However, it
// still has the problem that all dynamic arrays are cast independently.
// Thus, for example, if you have a worklet that can operate on vectors of
// any size, you will likely get a compile error when trying to operate on
// two vectors of different size when you expected them to be the same.
// This particular general case might actually be quite rare, so in that
// case the user has the onus to create a default template that handles
// this exceptional case with failure.
//
// 2. Have a mechanism to identify when the type of two things is expected
// to be the same. I'm not sure what the programming interface for that
// would look though.
//
// std::cout << "Repeat with dynamic arrays." << std::endl;
// // Clear out output array.
// outputHandle = vtkm::cont::ArrayHandle<T>();
// vtkm::cont::DynamicArrayHandle inputDynamic(inputHandle);
// vtkm::cont::DynamicArrayHandle outputDynamic(outputHandle);
// dispatcher.Invoke(inputDynamic, outputDynamic);
// CheckPortal(outputHandle.GetPortalConstControl());
std::cout << "Repeat with dynamic arrays." << std::endl;
// Clear out output array.
outputHandle = vtkm::cont::ArrayHandle<T>();
vtkm::cont::DynamicArrayHandle inputDynamic(inputHandle);
vtkm::cont::DynamicArrayHandle outputDynamic(outputHandle);
dispatcher.Invoke(inputDynamic, outputDynamic);
CheckPortal(outputHandle.GetPortalConstControl());
}
};
void TestWorkletMapField()
{
vtkm::testing::Testing::TryTypes(DoTestWorklet(), vtkm::TypeListTagCommon());
std::cout << "--- Worklet accepting all types." << std::endl;
vtkm::testing::Testing::TryTypes(DoTestWorklet<TestWorklet>(),
vtkm::TypeListTagCommon());
std::cout << "--- Worklet accepting some types." << std::endl;
vtkm::testing::Testing::TryTypes(DoTestWorklet<TestWorkletLimitedTypes>(),
vtkm::TypeListTagFieldScalar());
std::cout << "--- Sending bad type to worklet." << std::endl;
try
{
DoTestWorklet<TestWorkletLimitedTypes>()(vtkm::Vec<vtkm::Float32,3>());
VTKM_TEST_FAIL("Did not throw expected error.");
}
catch (vtkm::cont::ErrorControlBadType &error)
{
std::cout << "Got expected error: " << error.GetMessage() << std::endl;
}
}
} // anonymous namespace