diff --git a/vtkm/TypeListTag.h b/vtkm/TypeListTag.h index 6d0e95d55..707d60c09 100644 --- a/vtkm/TypeListTag.h +++ b/vtkm/TypeListTag.h @@ -21,7 +21,7 @@ #define vtk_m_TypeListTag_h #ifndef VTKM_DEFAULT_TYPE_LIST_TAG -#define VTKM_DEFAULT_TYPE_LIST_TAG ::vtkm::TypeListTagCommon; +#define VTKM_DEFAULT_TYPE_LIST_TAG ::vtkm::TypeListTagCommon #endif #include diff --git a/vtkm/cont/ArrayContainerControlImplicit.h b/vtkm/cont/ArrayContainerControlImplicit.h index 4acb1c682..7bdfa7c0a 100644 --- a/vtkm/cont/ArrayContainerControlImplicit.h +++ b/vtkm/cont/ArrayContainerControlImplicit.h @@ -105,6 +105,28 @@ public: } }; +//// If you are using this specalization of ArrayContainerControl, it means the +//// type of an ArrayHandle does not match the type of the array portal for the +//// array portal in an implicit array. Currently this use is invalid, but there +//// could be a case for implementing casting where appropriate. +//template +//class ArrayContainerControl > +//{ +//public: +// // This is meant to be invalid because this class should not actually be used. +// struct PortalType +// { +// typedef void *ValueType; +// typedef void *IteratorType; +// }; +// // This is meant to be invalid because this class should not actually be used. +// struct PortalConstType +// { +// typedef void *ValueType; +// typedef void *IteratorType; +// }; +//}; + template class ArrayTransfer< T, ArrayContainerControlTagImplicit, DeviceAdapterTag> diff --git a/vtkm/cont/CMakeLists.txt b/vtkm/cont/CMakeLists.txt index f4c63fec5..bd94588f4 100644 --- a/vtkm/cont/CMakeLists.txt +++ b/vtkm/cont/CMakeLists.txt @@ -25,11 +25,13 @@ set(headers ArrayContainerControlBasic.h ArrayContainerControlImplicit.h ArrayHandle.h + ArrayHandleCounting.h ArrayPortal.h Assert.h ContainerListTag.h DeviceAdapter.h DeviceAdapterSerial.h + DynamicArrayHandle.h Error.h ErrorControl.h ErrorControlAssert.h diff --git a/vtkm/cont/ContainerListTag.h b/vtkm/cont/ContainerListTag.h index 2392244aa..0cb7d15b4 100644 --- a/vtkm/cont/ContainerListTag.h +++ b/vtkm/cont/ContainerListTag.h @@ -21,7 +21,7 @@ #define vtk_m_ContainerListTag_h #ifndef VTKM_DEFAULT_CONTAINER_LIST_TAG -#define VTKM_DEFAULT_CONTAINER_LIST_TAG ::vtkm::cont::ContainerListTagBasic; +#define VTKM_DEFAULT_CONTAINER_LIST_TAG ::vtkm::cont::ContainerListTagBasic #endif #include diff --git a/vtkm/cont/DynamicArrayHandle.h b/vtkm/cont/DynamicArrayHandle.h new file mode 100644 index 000000000..76230799f --- /dev/null +++ b/vtkm/cont/DynamicArrayHandle.h @@ -0,0 +1,280 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +// +// Copyright 2014 Sandia Corporation. +// Copyright 2014 UT-Battelle, LLC. +// Copyright 2014. Los Alamos National Security +// +// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, +// the U.S. Government retains certain rights in this software. +// +// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National +// Laboratory (LANL), the U.S. Government retains certain rights in +// this software. +//============================================================================ +#ifndef vtk_m_cont_DynamicArrayHandle_h +#define vtk_m_cont_DynamicArrayHandle_h + +#include + +#include +#include +#include + +#include + +#include + +namespace vtkm { +namespace cont { + +namespace internal { + +/// Behaves like (and is interchangable with) a DynamicArrayHandle. The +/// difference is that the list of types and list of containers to try when +/// calling CastAndCall is set to the class template arguments. +/// +template +class DynamicArrayHandleCast; + +} // namespace internal + +class DynamicArrayHandle +{ +public: + VTKM_CONT_EXPORT + DynamicArrayHandle() { } + + template + VTKM_CONT_EXPORT + DynamicArrayHandle(const vtkm::cont::ArrayHandle &array) + : ArrayContainer(new vtkm::cont::internal::SimplePolymorphicContainer< + vtkm::cont::ArrayHandle >(array)) + { } + + template + VTKM_CONT_EXPORT + DynamicArrayHandle( + const internal::DynamicArrayHandleCast &dynamicArray) + : ArrayContainer(dynamicArray.ArrayContainer) { } + + /// Returns true if this array is of the provided type and uses the provided + /// container. + /// + template + VTKM_CONT_EXPORT + bool IsTypeAndContainer(Type = Type(), Container = Container()) const { + return (this->TryCastArrayContainer() != NULL); + } + + /// Returns this array cast to an ArrayHandle object of the given type and + /// container. Throws ErrorControlBadValue if the cast does not work. Use + /// IsTypeAndContainer to check if the cast can happen. + /// + template + VTKM_CONT_EXPORT + vtkm::cont::ArrayHandle + CastToArrayHandle(Type = Type(), Container = Container()) const { + vtkm::cont::internal::SimplePolymorphicContainer< + vtkm::cont::ArrayHandle > *container = + this->TryCastArrayContainer(); + if (container == NULL) + { + throw vtkm::cont::ErrorControlBadValue("Bad cast of dynamic array."); + } + return container->Item; + } + + /// Changes the types to try casting to when resolving this dynamic array, + /// which is specified with a list tag like those in TypeListTag.h. Since C++ + /// does not allow you to actually change the template arguments, this method + /// returns a new dynamic array object. This method is particularly useful to + /// narrow down (or expand) the types when using an array of particular + /// constraints. + /// + template + VTKM_CONT_EXPORT + internal::DynamicArrayHandleCast + ResetTypeList(NewTypeList = NewTypeList()) const { + return internal::DynamicArrayHandleCast< + NewTypeList,VTKM_DEFAULT_CONTAINER_LIST_TAG>(*this); + } + + /// Changes the array containers to try casting to when resolving this + /// dynamic array, which is specified with a list tag like those in + /// ContainerListTag.h. Since C++ does not allow you to actually change the + /// template arguments, this method returns a new dynamic array object. This + /// method is particularly useful to narrow down (or expand) the types when + /// using an array of particular constraints. + /// + template + VTKM_CONT_EXPORT + internal::DynamicArrayHandleCast + ResetContainerList(NewContainerList = NewContainerList()) const { + return internal::DynamicArrayHandleCast< + VTKM_DEFAULT_TYPE_LIST_TAG,NewContainerList>(*this); + } + + /// Attempts to cast the held array to a specific value type and container, + /// then call the given functor with the cast array. The types and containers + /// tried in the cast are those in the lists defined by + /// VTKM_DEFAULT_TYPE_LIST_TAG and VTKM_DEFAULT_CONTAINER_LIST_TAG, + /// respectively, unless they have been changed with a previous call to + /// ResetTypeList or ResetContainerList. + /// + template + VTKM_CONT_EXPORT + void CastAndCall(const Functor &f) const + { + this->CastAndCall( + f, VTKM_DEFAULT_TYPE_LIST_TAG(), VTKM_DEFAULT_CONTAINER_LIST_TAG()); + } + + /// A version of CastAndCall that tries specified lists of types and + /// containers. + /// + template + VTKM_CONT_EXPORT + void CastAndCall(const Functor &f, TypeList, ContainerList) const; + +private: + boost::shared_ptr + ArrayContainer; + + template + VTKM_CONT_EXPORT + vtkm::cont::internal::SimplePolymorphicContainer< + vtkm::cont::ArrayHandle > * + TryCastArrayContainer() const { + return + dynamic_cast< + vtkm::cont::internal::SimplePolymorphicContainer< + vtkm::cont::ArrayHandle > *>( + this->ArrayContainer.get()); + } +}; + +namespace detail { + +template +struct DynamicArrayHandleTryContainer { + const DynamicArrayHandle Array; + const Functor &Function; + bool FoundCast; + + VTKM_CONT_EXPORT + DynamicArrayHandleTryContainer(const DynamicArrayHandle &array, + const Functor &f) + : Array(array), Function(f), FoundCast(false) { } + + template + VTKM_CONT_EXPORT + void operator()(Container) { + if (!this->FoundCast && + this->Array.IsTypeAndContainer(Type(), Container())) + { + this->Function(this->Array.CastToArrayHandle(Type(), Container())); + this->FoundCast = true; + } + } +}; + +template +struct DynamicArrayHandleTryType { + const DynamicArrayHandle Array; + const Functor &Function; + bool FoundCast; + + VTKM_CONT_EXPORT + DynamicArrayHandleTryType(const DynamicArrayHandle &array, const Functor &f) + : Array(array), Function(f), FoundCast(false) { } + + template + VTKM_CONT_EXPORT + void operator()(Type) { + if (this->FoundCast) { return; } + typedef DynamicArrayHandleTryContainer TryContainerType; + TryContainerType tryContainer = + TryContainerType(this->Array, this->Function); + vtkm::ListForEach(tryContainer, ContainerList()); + if (tryContainer.FoundCast) + { + this->FoundCast = true; + } + } +}; + +} // namespace detail + +template +VTKM_CONT_EXPORT +void DynamicArrayHandle::CastAndCall(const Functor &f, + TypeList, + ContainerList) const +{ + typedef detail::DynamicArrayHandleTryType TryTypeType; + TryTypeType tryType = TryTypeType(*this, f); + vtkm::ListForEach(tryType, TypeList()); + if (!tryType.FoundCast) + { + throw vtkm::cont::ErrorControlBadValue( + "Could not find appropriate cast in CastAndCall."); + } +} + +namespace internal { + +template +class DynamicArrayHandleCast : public vtkm::cont::DynamicArrayHandle +{ +public: + VTKM_CONT_EXPORT + DynamicArrayHandleCast(const vtkm::cont::DynamicArrayHandle &array) + : DynamicArrayHandle(array) { } + + template + VTKM_CONT_EXPORT + DynamicArrayHandleCast( + const DynamicArrayHandleCast &array) + : DynamicArrayHandle(array) { } + + template + VTKM_CONT_EXPORT + DynamicArrayHandleCast + ResetTypeList(NewTypeList = NewTypeList()) const { + return DynamicArrayHandleCast(*this); + } + + template + VTKM_CONT_EXPORT + internal::DynamicArrayHandleCast + ResetContainerList(NewContainerList = NewContainerList()) const { + return internal::DynamicArrayHandleCast(*this); + } + + template + VTKM_CONT_EXPORT + void CastAndCall(const Functor &f) const + { + this->CastAndCall(f, TypeList(), ContainerList()); + } + + template + VTKM_CONT_EXPORT + void CastAndCall(const Functor &f, TL, CL) const + { + this->DynamicArrayHandle::CastAndCall(f, TL(), CL()); + } +}; + +} // namespace internal + +} +} // namespace vtkm::cont + +#endif //vtk_m_cont_DynamicArrayHandle_h diff --git a/vtkm/cont/internal/CMakeLists.txt b/vtkm/cont/internal/CMakeLists.txt index d1b445f87..4214ac793 100644 --- a/vtkm/cont/internal/CMakeLists.txt +++ b/vtkm/cont/internal/CMakeLists.txt @@ -33,6 +33,7 @@ set(headers DeviceAdapterTag.h DeviceAdapterTagSerial.h IteratorFromArrayPortal.h + SimplePolymorphicContainer.h ) vtkm_declare_headers(${headers}) diff --git a/vtkm/cont/internal/SimplePolymorphicContainer.h b/vtkm/cont/internal/SimplePolymorphicContainer.h new file mode 100644 index 000000000..6a1c44b3b --- /dev/null +++ b/vtkm/cont/internal/SimplePolymorphicContainer.h @@ -0,0 +1,60 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +// +// Copyright 2014 Sandia Corporation. +// Copyright 2014 UT-Battelle, LLC. +// Copyright 2014. Los Alamos National Security +// +// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, +// the U.S. Government retains certain rights in this software. +// +// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National +// Laboratory (LANL), the U.S. Government retains certain rights in +// this software. +//============================================================================ +#ifndef vtk_m_cont_internal_SimplePolymorphicContainer_h +#define vtk_m_cont_internal_SimplePolymorphicContainer_h + +#include + +namespace vtkm { +namespace cont { +namespace internal { + +/// \brief Base class for SimplePolymorphicContainer +/// +struct SimplePolymorphicContainerBase { + virtual ~SimplePolymorphicContainerBase() { } +}; + +/// \brief Simple object container that can use C++ run-time type information. +/// +/// The SimplePolymorphicContainer is a trivial structure that contains a +/// single object. The intention is to be able to pass around a pointer to the +/// superclass SimplePolymorphicContainerBase to methods that cannot know the +/// full type of the object at run-time. This is roughly equivalent to passing +/// around a void* except that C++ will capture run-time type information that +/// allows for safer dynamic downcasts. +/// +template +struct SimplePolymorphicContainer : public SimplePolymorphicContainerBase +{ + T Item; + + VTKM_CONT_EXPORT + SimplePolymorphicContainer() : Item() { } + + VTKM_CONT_EXPORT + SimplePolymorphicContainer(const T &src) : Item(src) { } +}; + +} +} +} // namespace vtkm::cont::internal + +#endif //vtk_m_cont_internal_SimplePolymorphicContainer_h diff --git a/vtkm/cont/testing/CMakeLists.txt b/vtkm/cont/testing/CMakeLists.txt index dd233f55d..26ea33d80 100644 --- a/vtkm/cont/testing/CMakeLists.txt +++ b/vtkm/cont/testing/CMakeLists.txt @@ -35,6 +35,7 @@ set(unit_tests UnitTestDeviceAdapterAlgorithmDependency.cxx UnitTestDeviceAdapterAlgorithmGeneral.cxx UnitTestDeviceAdapterSerial.cxx + UnitTestDynamicArrayHandle.cxx ) vtkm_unit_tests(SOURCES ${unit_tests}) diff --git a/vtkm/cont/testing/UnitTestDynamicArrayHandle.cxx b/vtkm/cont/testing/UnitTestDynamicArrayHandle.cxx new file mode 100644 index 000000000..54e82235d --- /dev/null +++ b/vtkm/cont/testing/UnitTestDynamicArrayHandle.cxx @@ -0,0 +1,294 @@ +//============================================================================ +// Copyright (c) Kitware, Inc. +// All rights reserved. +// See LICENSE.txt for details. +// This software is distributed WITHOUT ANY WARRANTY; without even +// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR +// PURPOSE. See the above copyright notice for more information. +// +// Copyright 2014 Sandia Corporation. +// Copyright 2014 UT-Battelle, LLC. +// Copyright 2014. Los Alamos National Security +// +// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, +// the U.S. Government retains certain rights in this software. +// +// Under the terms of Contract DE-AC52-06NA25396 with Los Alamos National +// Laboratory (LANL), the U.S. Government retains certain rights in +// this software. +//============================================================================ + + +#include + +#include +#include + +#include + +#include +#include + +namespace { + +const vtkm::Id ARRAY_SIZE = 10; + +vtkm::Id TestValue(vtkm::Id index, vtkm::Id) { + return index*100; +} + +vtkm::Scalar TestValue(vtkm::Id index, vtkm::Scalar) { + return static_cast(index)/100; +} + +template +vtkm::Tuple TestValue(vtkm::Id index, vtkm::Tuple) { + vtkm::Tuple value; + for (int i = 0; i < N; i++) + { + value[i] = TestValue(index, T()) + (i + 1); + } + return value; +} + +std::string TestValue(vtkm::Id index, std::string) { + std::stringstream stream; + stream << index; + return stream.str(); +} + +struct TypeListTagString : vtkm::ListTagBase { }; + +template +struct UnusualPortal +{ + typedef T ValueType; + + VTKM_EXEC_CONT_EXPORT + vtkm::Id GetNumberOfValues() const { return ARRAY_SIZE; } + + VTKM_EXEC_CONT_EXPORT + ValueType Get(vtkm::Id index) const { return TestValue(index, ValueType()); } + + typedef vtkm::cont::internal::IteratorFromArrayPortal > + IteratorType; + + VTKM_CONT_EXPORT + IteratorType GetIteratorBegin() const { + return IteratorType(*this); + } + + VTKM_CONT_EXPORT + IteratorType GetIteratorEnd() const { + return IteratorType(*this, this->GetNumberOfValues()); + } +}; + +template +class ArrayHandleWithUnusualContainer + : public vtkm::cont::ArrayHandle > > +{ + typedef vtkm::cont::ArrayHandle > > + Superclass; +public: + VTKM_CONT_EXPORT + ArrayHandleWithUnusualContainer() + : Superclass(typename Superclass::PortalConstControl()) { } +}; + +struct ContainerListTagUnusual : + vtkm::ListTagBase2< + ArrayHandleWithUnusualContainer::ArrayContainerControlTag, + ArrayHandleWithUnusualContainer::ArrayContainerControlTag> +{ }; + +bool CheckCalled; + +struct CheckFunctor +{ + template + void operator()(vtkm::cont::ArrayHandle array) const { + CheckCalled = true; + + VTKM_TEST_ASSERT(array.GetNumberOfValues() == ARRAY_SIZE, + "Unexpected array size."); + + typename vtkm::cont::ArrayHandle::PortalConstControl portal = + array.GetPortalConstControl(); + for (vtkm::Id index = 0; index < ARRAY_SIZE; index++) + { + VTKM_TEST_ASSERT(portal.Get(index) == TestValue(index, T()), + "Got bad value in array. Perhaps a bad cast?"); + } + } +}; + +template +vtkm::cont::DynamicArrayHandle CreateDynamicArray(T) +{ + // Declared static to prevent going out of scope. + static T buffer[ARRAY_SIZE]; + for (vtkm::Id index = 0; index < ARRAY_SIZE; index++) + { + buffer[index] = TestValue(index, T()); + } + + return vtkm::cont::DynamicArrayHandle( + vtkm::cont::make_ArrayHandle(buffer, ARRAY_SIZE)); +} + +template +void TryDefaultType(T) +{ + CheckCalled = false; + + vtkm::cont::DynamicArrayHandle array = CreateDynamicArray(T()); + + array.CastAndCall(CheckFunctor()); + + VTKM_TEST_ASSERT(CheckCalled, + "The functor was never called (and apparently a bad value exception not thrown)."); +} + +struct TryBasicVTKmType +{ + template + void operator()(T) { + CheckCalled = false; + + vtkm::cont::DynamicArrayHandle array = CreateDynamicArray(T()); + + array.ResetTypeList(vtkm::TypeListTagAll()).CastAndCall(CheckFunctor()); + + VTKM_TEST_ASSERT(CheckCalled, + "The functor was never called (and apparently a bad value exception not thrown)."); + } +}; + +void TryUnusualType() +{ + // A string is an unlikely type to be declared elsewhere in VTK-m. + vtkm::cont::DynamicArrayHandle array = CreateDynamicArray(std::string()); + + try + { + array.CastAndCall(CheckFunctor()); + VTKM_TEST_FAIL("CastAndCall failed to error for unrecognized type."); + } + catch (vtkm::cont::ErrorControlBadValue) + { + std::cout << " Caught exception for unrecognized type." << std::endl; + } + + CheckCalled = false; + array.ResetTypeList(TypeListTagString()).CastAndCall(CheckFunctor()); + VTKM_TEST_ASSERT(CheckCalled, + "The functor was never called (and apparently a bad value exception not thrown)."); + std::cout << " Found type when type list was reset." << std:: endl; +} + +void TryUnusualContainer() +{ + vtkm::cont::DynamicArrayHandle array = + ArrayHandleWithUnusualContainer(); + + try + { + array.CastAndCall(CheckFunctor()); + VTKM_TEST_FAIL("CastAndCall failed to error for unrecognized container."); + } + catch (vtkm::cont::ErrorControlBadValue) + { + std::cout << " Caught exception for unrecognized container." << std::endl; + } + + CheckCalled = false; + array.ResetContainerList(ContainerListTagUnusual()).CastAndCall(CheckFunctor()); + VTKM_TEST_ASSERT(CheckCalled, + "The functor was never called (and apparently a bad value exception not thrown)."); + std::cout << " Found instance when container list was reset." << std:: endl; +} + +void TryUnusualTypeAndContainer() +{ + vtkm::cont::DynamicArrayHandle array = + ArrayHandleWithUnusualContainer(); + + try + { + array.CastAndCall(CheckFunctor()); + VTKM_TEST_FAIL( + "CastAndCall failed to error for unrecognized type/container."); + } + catch (vtkm::cont::ErrorControlBadValue) + { + std::cout << " Caught exception for unrecognized type/container." + << std::endl; + } + + try + { + array.ResetTypeList(TypeListTagString()).CastAndCall(CheckFunctor()); + VTKM_TEST_FAIL("CastAndCall failed to error for unrecognized container."); + } + catch (vtkm::cont::ErrorControlBadValue) + { + std::cout << " Caught exception for unrecognized container." << std::endl; + } + + try + { + array.ResetContainerList(ContainerListTagUnusual()). + CastAndCall(CheckFunctor()); + VTKM_TEST_FAIL("CastAndCall failed to error for unrecognized type."); + } + catch (vtkm::cont::ErrorControlBadValue) + { + std::cout << " Caught exception for unrecognized type." << std::endl; + } + + CheckCalled = false; + array + .ResetTypeList(TypeListTagString()) + .ResetContainerList(ContainerListTagUnusual()) + .CastAndCall(CheckFunctor()); + VTKM_TEST_ASSERT(CheckCalled, + "The functor was never called (and apparently a bad value exception not thrown)."); + std::cout << " Found instance when type and container lists were reset." << std:: endl; + + CheckCalled = false; + array + .ResetContainerList(ContainerListTagUnusual()) + .ResetTypeList(TypeListTagString()) + .CastAndCall(CheckFunctor()); + VTKM_TEST_ASSERT(CheckCalled, + "The functor was never called (and apparently a bad value exception not thrown)."); + std::cout << " Found instance when container and type lists were reset." << std:: endl; +} + +void TestDynamicArrayHandle() +{ + std::cout << "Try common types with default type lists." << std::endl; + TryDefaultType(vtkm::Id()); + TryDefaultType(vtkm::Scalar()); + TryDefaultType(vtkm::Vector3()); + + std::cout << "Try all VTK-m types." << std::endl; + vtkm::testing::Testing::TryAllTypes(TryBasicVTKmType()); + + std::cout << "Try unusual type." << std::endl; + TryUnusualType(); + + std::cout << "Try unusual container." << std::endl; + TryUnusualContainer(); + + std::cout << "Try unusual type in unusual container." << std::endl; + TryUnusualTypeAndContainer(); +} + +} // anonymous namespace + +int UnitTestDynamicArrayHandle(int, char *[]) +{ + return vtkm::cont::testing::Testing::Run(TestDynamicArrayHandle); +}