vtk-m/vtkm/cont/testing/UnitTestArrayHandleDecorator.cxx
Kenneth Moreland 567b626080 Remove warnins about unused functions
nvcc is giving me lots of annoying warnings about unused functions.
Typically, this warning is given when a function declared as static or
in an anonymous namespace is never used. The "functions" in question are
not really functions. They are templated methods (in a class in an
anonymous namespace).

Specificaly, I'm getting errors like this:

```
/.../vtkm/cont/testing/UnitTestArrayHandleDecorator.cxx(96): warning #177-D: function "<unnamed>::DecoratorTests::InvertibleDecorImpl::CreateFunctor(PortalTs&&...) const [with PortalTs=<vtkm::cont::internal::decor::GetWritePortalType<vtkm::cont::ArrayHandleCounting<signed char>>, vtkm::cont::internal::decor::GetWritePortalType<vtkm::cont::ArrayHandleConstant<signed char>>, vtkm::cont::internal::decor::GetWritePortalType<vtkm::cont::ArrayHandle<vtkm::Int8, vtkm::cont::StorageTagBasic>>>]" was declared but never referenced
```

That said, these warnings appear to be an annoying but harmless bug in
nvcc. This warning is in error because

1. The method in question is _definitely_ being used.
2. I'm pretty sure this warning should not be given for a templated
method even if it was never used. SFINAE alone should be enough
justification to allow this to exist without warning.

The easiest way around the spurious error is to declare these structures
in a namespace with a name.
2022-07-08 06:28:51 -06:00

655 lines
23 KiB
C++

//============================================================================
// 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.
//============================================================================
#include <vtkm/cont/ArrayHandleDecorator.h>
#include <vtkm/cont/Algorithm.h>
#include <vtkm/cont/ArrayCopy.h>
#include <vtkm/cont/ArrayCopyDevice.h>
#include <vtkm/cont/ArrayHandle.h>
#include <vtkm/cont/ArrayHandleConstant.h>
#include <vtkm/cont/ArrayHandleCounting.h>
#include <vtkm/BinaryOperators.h>
#include <vtkm/cont/testing/Testing.h>
// not anonymous because nvcc sometimes complains about "unused" methods (that
// are definitely being used)
namespace ut_decorator
{
struct DecoratorTests
{
static constexpr vtkm::Id ARRAY_SIZE = 10;
// Decorator implementation that demonstrates how to write invertible functors
// that combine three array handles with complex access logic. The resulting
// ArrayHandleDecorator can be both read from and written to.
//
// Constructs functors that take three portals.
//
// The first portal's values are accessed in reverse order.
// The second portal's values are accessed in normal order.
// The third portal's values are accessed via ((idx + 3) % size).
//
// Functor will return the max of the first two added to the third.
//
// InverseFunctor will update the third portal such that the Functor would
// return the indicated value.
struct InvertibleDecorImpl
{
// The functor used for reading data from the three portals.
template <typename Portal1Type, typename Portal2Type, typename Portal3Type>
struct Functor
{
using ValueType = typename Portal1Type::ValueType;
Portal1Type Portal1;
Portal2Type Portal2;
Portal3Type Portal3;
VTKM_EXEC_CONT ValueType operator()(vtkm::Id idx) const
{
const auto idx1 = this->Portal1.GetNumberOfValues() - idx - 1;
const auto idx2 = idx;
const auto idx3 = (idx + 3) % this->Portal3.GetNumberOfValues();
const auto v1 = this->Portal1.Get(idx1);
const auto v2 = this->Portal2.Get(idx2);
const auto v3 = this->Portal3.Get(idx3);
return vtkm::Max(v1, v2) + v3;
}
};
// The functor used for writing. Only Portal3 is written to, the other
// portals may be read-only.
template <typename Portal1Type, typename Portal2Type, typename Portal3Type>
struct InverseFunctor
{
using ValueType = typename Portal1Type::ValueType;
Portal1Type Portal1;
Portal2Type Portal2;
Portal3Type Portal3;
VTKM_EXEC_CONT void operator()(vtkm::Id idx, const ValueType& vIn) const
{
const auto v1 = this->Portal1.Get(this->Portal1.GetNumberOfValues() - idx - 1);
const auto v2 = this->Portal2.Get(idx);
const auto vNew = static_cast<ValueType>(vIn - vtkm::Max(v1, v2));
this->Portal3.Set((idx + 3) % this->Portal3.GetNumberOfValues(), vNew);
}
};
// Factory function that takes 3 portals as input and creates an instance
// of Functor with them. Variadic template parameters are used here, but are
// not necessary.
template <typename... PortalTs>
Functor<typename std::decay<PortalTs>::type...> CreateFunctor(PortalTs&&... portals) const
{
VTKM_STATIC_ASSERT(sizeof...(PortalTs) == 3);
return { std::forward<PortalTs>(portals)... };
}
// Factory function that takes 3 portals as input and creates an instance
// of InverseFunctor with them. Variadic template parameters are used here,
// but are not necessary.
template <typename... PortalTs>
InverseFunctor<typename std::decay<PortalTs>::type...> CreateInverseFunctor(
PortalTs&&... portals) const
{
VTKM_STATIC_ASSERT(sizeof...(PortalTs) == 3);
return { std::forward<PortalTs>(portals)... };
}
};
// Same as above, but cannot be inverted. The resulting ArrayHandleDecorator
// will be read-only.
struct NonInvertibleDecorImpl
{
template <typename Portal1Type, typename Portal2Type, typename Portal3Type>
struct Functor
{
using ValueType = typename Portal1Type::ValueType;
Portal1Type Portal1;
Portal2Type Portal2;
Portal3Type Portal3;
VTKM_EXEC_CONT ValueType operator()(vtkm::Id idx) const
{
const auto v1 = this->Portal1.Get(this->Portal1.GetNumberOfValues() - idx - 1);
const auto v2 = this->Portal2.Get(idx);
const auto v3 = this->Portal3.Get((idx + 3) % this->Portal3.GetNumberOfValues());
return vtkm::Max(v1, v2) + v3;
}
};
template <typename... PortalTs>
Functor<typename std::decay<PortalTs>::type...> CreateFunctor(PortalTs&&... portals) const
{
VTKM_STATIC_ASSERT(sizeof...(PortalTs) == 3);
return { std::forward<PortalTs>(portals)... };
}
};
// Decorator implementation that demonstrates how to create functors that
// hold custom state. Here, the functors have a customizable Operation
// member.
//
// This implementation is used to create a read-only ArrayHandleDecorator
// that combines the values in two other ArrayHandles using an arbitrary
// binary operation (e.g. vtkm::Maximum, vtkm::Add, etc).
template <typename ValueType, typename OperationType>
struct BinaryOperationDecorImpl
{
OperationType Operation;
// The functor use to read values. Note that it holds extra state in
// addition to the portals.
template <typename Portal1Type, typename Portal2Type>
struct Functor
{
Portal1Type Portal1;
Portal2Type Portal2;
OperationType Operation;
VTKM_EXEC_CONT
ValueType operator()(vtkm::Id idx) const
{
return this->Operation(static_cast<ValueType>(this->Portal1.Get(idx)),
static_cast<ValueType>(this->Portal2.Get(idx)));
}
};
// A non-variadic example of a factory function to produce a functor. This
// is where the extra state is passed into the functor.
template <typename P1T, typename P2T>
Functor<P1T, P2T> CreateFunctor(P1T p1, P2T p2) const
{
return { p1, p2, this->Operation };
}
};
// Decorator implementation that reverses the ScanExtended operation.
//
// The resulting ArrayHandleDecorator will take an array produced by the
// ScanExtended algorithm and return the original ScanExtended input.
//
// Some interesting things about this:
// - The ArrayHandleDecorator's ValueType will not be the same as the
// ScanPortal's ValueType. The Decorator ValueType is determined by the
// return type of Functor::operator().
// - The ScanPortal has more values than the ArrayHandleDecorator. The
// number of values the ArrayHandleDecorator should hold is set during
// construction and may differ from the arrays it holds.
template <typename ValueType>
struct ScanExtendedToNumIndicesDecorImpl
{
template <typename ScanPortalType>
struct Functor
{
ScanPortalType ScanPortal;
VTKM_EXEC_CONT
ValueType operator()(vtkm::Id idx) const
{
return static_cast<ValueType>(this->ScanPortal.Get(idx + 1) - this->ScanPortal.Get(idx));
}
};
template <typename ScanPortalType>
Functor<ScanPortalType> CreateFunctor(ScanPortalType portal) const
{
return { portal };
}
};
// Decorator implementation that combines two source arrays using the formula
// `[source1] * 10 + [source2]` and supports resizing.
template <typename ValueType>
struct DecompositionDecorImpl
{
template <typename Portal1T, typename Portal2T>
struct Functor
{
Portal1T Portal1;
Portal2T Portal2;
VTKM_EXEC_CONT
ValueType operator()(vtkm::Id idx) const
{
return static_cast<ValueType>(this->Portal1.Get(idx) * 10 + this->Portal2.Get(idx));
}
};
template <typename Portal1T, typename Portal2T>
struct InverseFunctor
{
Portal1T Portal1;
Portal2T Portal2;
VTKM_EXEC_CONT
void operator()(vtkm::Id idx, const ValueType& val) const
{
this->Portal1.Set(idx, static_cast<ValueType>(std::floor(val / 10)));
this->Portal2.Set(idx, static_cast<ValueType>(std::fmod(val, 10)));
}
};
template <typename Portal1T, typename Portal2T>
VTKM_CONT Functor<typename std::decay<Portal1T>::type, typename std::decay<Portal2T>::type>
CreateFunctor(Portal1T&& p1, Portal2T&& p2) const
{
return { std::forward<Portal1T>(p1), std::forward<Portal2T>(p2) };
}
template <typename Portal1T, typename Portal2T>
VTKM_CONT
InverseFunctor<typename std::decay<Portal1T>::type, typename std::decay<Portal2T>::type>
CreateInverseFunctor(Portal1T&& p1, Portal2T&& p2) const
{
return { std::forward<Portal1T>(p1), std::forward<Portal2T>(p2) };
}
// Resize methods:
template <typename Array1T, typename Array2T>
VTKM_CONT void AllocateSourceArrays(vtkm::Id numVals,
vtkm::CopyFlag preserve,
vtkm::cont::Token& token,
Array1T&& array1,
Array2T&& array2) const
{
array1.Allocate(numVals, preserve, token);
array2.Allocate(numVals, preserve, token);
}
};
template <typename ValueType>
void InversionTest() const
{
auto ah1 = vtkm::cont::make_ArrayHandleCounting(ValueType{ 0 }, ValueType{ 2 }, ARRAY_SIZE);
auto ah2 = vtkm::cont::make_ArrayHandleConstant(ValueType{ ARRAY_SIZE }, ARRAY_SIZE);
vtkm::cont::ArrayHandle<ValueType> ah3;
ah3.AllocateAndFill(ARRAY_SIZE, ValueType{ ARRAY_SIZE / 2 });
auto ah3Const = vtkm::cont::make_ArrayHandleConstant(ValueType{ ARRAY_SIZE / 2 }, ARRAY_SIZE);
{ // Has a writable handle and an invertible functor:
auto ahInv =
vtkm::cont::make_ArrayHandleDecorator(ARRAY_SIZE, InvertibleDecorImpl{}, ah1, ah2, ah3);
VTKM_TEST_ASSERT(vtkm::cont::internal::IsWritableArrayHandle<decltype(ahInv)>::value);
}
{ // Has no writable handles and an invertible functor:
auto ahNInv = vtkm::cont::make_ArrayHandleDecorator(
ARRAY_SIZE, InvertibleDecorImpl{}, ah1, ah2, ah3Const);
VTKM_TEST_ASSERT(!vtkm::cont::internal::IsWritableArrayHandle<decltype(ahNInv)>::value);
}
{ // Has writable handles, but the functor cannot be inverted:
auto ahNInv =
vtkm::cont::make_ArrayHandleDecorator(ARRAY_SIZE, NonInvertibleDecorImpl{}, ah1, ah2, ah3);
VTKM_TEST_ASSERT(!vtkm::cont::internal::IsWritableArrayHandle<decltype(ahNInv)>::value);
}
{ // Has no writable handles and the functor cannot be inverted:
auto ahNInv = vtkm::cont::make_ArrayHandleDecorator(
ARRAY_SIZE, NonInvertibleDecorImpl{}, ah1, ah2, ah3Const);
VTKM_TEST_ASSERT(!vtkm::cont::internal::IsWritableArrayHandle<decltype(ahNInv)>::value);
}
{ // Test reading/writing to an invertible handle:
// Copy ah3 since we'll be modifying it:
vtkm::cont::ArrayHandle<ValueType> ah3Copy;
vtkm::cont::ArrayCopy(ah3, ah3Copy);
auto ahDecor =
vtkm::cont::make_ArrayHandleDecorator(ARRAY_SIZE, InvertibleDecorImpl{}, ah1, ah2, ah3Copy);
{
auto portalDecor = ahDecor.ReadPortal();
VTKM_TEST_ASSERT(ahDecor.GetNumberOfValues() == ARRAY_SIZE);
VTKM_TEST_ASSERT(portalDecor.GetNumberOfValues() == ARRAY_SIZE);
VTKM_TEST_ASSERT(portalDecor.Get(0) == ValueType{ 23 });
VTKM_TEST_ASSERT(portalDecor.Get(1) == ValueType{ 21 });
VTKM_TEST_ASSERT(portalDecor.Get(2) == ValueType{ 19 });
VTKM_TEST_ASSERT(portalDecor.Get(3) == ValueType{ 17 });
VTKM_TEST_ASSERT(portalDecor.Get(4) == ValueType{ 15 });
VTKM_TEST_ASSERT(portalDecor.Get(5) == ValueType{ 15 });
VTKM_TEST_ASSERT(portalDecor.Get(6) == ValueType{ 15 });
VTKM_TEST_ASSERT(portalDecor.Get(7) == ValueType{ 15 });
VTKM_TEST_ASSERT(portalDecor.Get(8) == ValueType{ 15 });
VTKM_TEST_ASSERT(portalDecor.Get(9) == ValueType{ 15 });
}
// Copy a constant array into the decorator. This should modify ah3Copy.
vtkm::cont::ArrayCopyDevice(vtkm::cont::make_ArrayHandleConstant(ValueType{ 25 }, ARRAY_SIZE),
ahDecor);
{ // Accessing portal should give all 25s:
auto portalDecor = ahDecor.ReadPortal();
VTKM_TEST_ASSERT(ahDecor.GetNumberOfValues() == ARRAY_SIZE);
VTKM_TEST_ASSERT(portalDecor.GetNumberOfValues() == ARRAY_SIZE);
VTKM_TEST_ASSERT(portalDecor.Get(0) == ValueType{ 25 });
VTKM_TEST_ASSERT(portalDecor.Get(1) == ValueType{ 25 });
VTKM_TEST_ASSERT(portalDecor.Get(2) == ValueType{ 25 });
VTKM_TEST_ASSERT(portalDecor.Get(3) == ValueType{ 25 });
VTKM_TEST_ASSERT(portalDecor.Get(4) == ValueType{ 25 });
VTKM_TEST_ASSERT(portalDecor.Get(5) == ValueType{ 25 });
VTKM_TEST_ASSERT(portalDecor.Get(6) == ValueType{ 25 });
VTKM_TEST_ASSERT(portalDecor.Get(7) == ValueType{ 25 });
VTKM_TEST_ASSERT(portalDecor.Get(8) == ValueType{ 25 });
VTKM_TEST_ASSERT(portalDecor.Get(9) == ValueType{ 25 });
}
{ // ah3Copy should have updated values:
auto portalAH3Copy = ah3Copy.ReadPortal();
VTKM_TEST_ASSERT(ahDecor.GetNumberOfValues() == ARRAY_SIZE);
VTKM_TEST_ASSERT(portalAH3Copy.GetNumberOfValues() == ARRAY_SIZE);
VTKM_TEST_ASSERT(portalAH3Copy.Get(0) == ValueType{ 15 });
VTKM_TEST_ASSERT(portalAH3Copy.Get(1) == ValueType{ 15 });
VTKM_TEST_ASSERT(portalAH3Copy.Get(2) == ValueType{ 15 });
VTKM_TEST_ASSERT(portalAH3Copy.Get(3) == ValueType{ 7 });
VTKM_TEST_ASSERT(portalAH3Copy.Get(4) == ValueType{ 9 });
VTKM_TEST_ASSERT(portalAH3Copy.Get(5) == ValueType{ 11 });
VTKM_TEST_ASSERT(portalAH3Copy.Get(6) == ValueType{ 13 });
VTKM_TEST_ASSERT(portalAH3Copy.Get(7) == ValueType{ 15 });
VTKM_TEST_ASSERT(portalAH3Copy.Get(8) == ValueType{ 15 });
VTKM_TEST_ASSERT(portalAH3Copy.Get(9) == ValueType{ 15 });
}
}
}
template <typename ValueType, typename OperationType>
void BinaryOperatorTest() const
{
auto ahCount = vtkm::cont::make_ArrayHandleCounting(ValueType{ 0 }, ValueType{ 1 }, ARRAY_SIZE);
auto ahConst = vtkm::cont::make_ArrayHandleConstant(ValueType{ ARRAY_SIZE / 2 }, ARRAY_SIZE);
const OperationType op;
BinaryOperationDecorImpl<ValueType, OperationType> impl{ op };
auto decorArray = vtkm::cont::make_ArrayHandleDecorator(ARRAY_SIZE, impl, ahCount, ahConst);
{
auto decorPortal = decorArray.ReadPortal();
auto countPortal = ahCount.ReadPortal();
auto constPortal = ahConst.ReadPortal();
for (vtkm::Id i = 0; i < ARRAY_SIZE; ++i)
{
VTKM_TEST_ASSERT(decorPortal.Get(i) == op(countPortal.Get(i), constPortal.Get(i)));
}
}
vtkm::cont::ArrayHandle<ValueType> copiedInExec;
vtkm::cont::ArrayCopyDevice(decorArray, copiedInExec);
{
auto copiedPortal = copiedInExec.ReadPortal();
auto countPortal = ahCount.ReadPortal();
auto constPortal = ahConst.ReadPortal();
for (vtkm::Id i = 0; i < ARRAY_SIZE; ++i)
{
VTKM_TEST_ASSERT(copiedPortal.Get(i) == op(countPortal.Get(i), constPortal.Get(i)));
}
}
}
template <typename ValueType>
void ScanExtendedToNumIndicesTest() const
{
auto numIndicesOrig =
vtkm::cont::make_ArrayHandleCounting(ValueType{ 0 }, ValueType{ 1 }, ARRAY_SIZE);
vtkm::cont::ArrayHandle<vtkm::Id> scan;
vtkm::cont::Algorithm::ScanExtended(vtkm::cont::make_ArrayHandleCast<vtkm::Id>(numIndicesOrig),
scan);
// Some interesting things to notice:
// - `numIndicesDecor` will have `ARRAY_SIZE` entries, while `scan` has
// `ARRAY_SIZE + 1`.
// - `numIndicesDecor` uses the current function scope `ValueType`, since
// that is what the functor from the implementation class returns. `scan`
// uses `vtkm::Id`.
auto numIndicesDecor = vtkm::cont::make_ArrayHandleDecorator(
ARRAY_SIZE, ScanExtendedToNumIndicesDecorImpl<ValueType>{}, scan);
{
auto origPortal = numIndicesOrig.ReadPortal();
auto decorPortal = numIndicesDecor.ReadPortal();
VTKM_STATIC_ASSERT(VTKM_PASS_COMMAS(
std::is_same<decltype(origPortal.Get(0)), decltype(decorPortal.Get(0))>::value));
VTKM_TEST_ASSERT(origPortal.GetNumberOfValues() == decorPortal.GetNumberOfValues());
for (vtkm::Id i = 0; i < origPortal.GetNumberOfValues(); ++i)
{
VTKM_TEST_ASSERT(origPortal.Get(i) == decorPortal.Get(i));
}
}
}
template <typename ValueType>
void DecompositionTest() const
{
vtkm::cont::ArrayHandle<ValueType> a1;
vtkm::cont::ArrayHandle<ValueType> a2;
auto decor =
vtkm::cont::make_ArrayHandleDecorator(0, DecompositionDecorImpl<ValueType>{}, a1, a2);
VTKM_TEST_ASSERT(decor.GetNumberOfValues() == 0);
decor.Allocate(5);
VTKM_TEST_ASSERT(decor.GetNumberOfValues() == 5);
{
auto decorPortal = decor.WritePortal();
decorPortal.Set(0, 13);
decorPortal.Set(1, 8);
decorPortal.Set(2, 43);
decorPortal.Set(3, 92);
decorPortal.Set(4, 117);
}
VTKM_TEST_ASSERT(a1.GetNumberOfValues() == 5);
{
auto a1Portal = a1.ReadPortal();
VTKM_TEST_ASSERT(test_equal(a1Portal.Get(0), 1));
VTKM_TEST_ASSERT(test_equal(a1Portal.Get(1), 0));
VTKM_TEST_ASSERT(test_equal(a1Portal.Get(2), 4));
VTKM_TEST_ASSERT(test_equal(a1Portal.Get(3), 9));
VTKM_TEST_ASSERT(test_equal(a1Portal.Get(4), 11));
}
VTKM_TEST_ASSERT(a2.GetNumberOfValues() == 5);
{
auto a2Portal = a2.ReadPortal();
VTKM_TEST_ASSERT(test_equal(a2Portal.Get(0), 3));
VTKM_TEST_ASSERT(test_equal(a2Portal.Get(1), 8));
VTKM_TEST_ASSERT(test_equal(a2Portal.Get(2), 3));
VTKM_TEST_ASSERT(test_equal(a2Portal.Get(3), 2));
VTKM_TEST_ASSERT(test_equal(a2Portal.Get(4), 7));
}
decor.Allocate(3, vtkm::CopyFlag::On);
VTKM_TEST_ASSERT(decor.GetNumberOfValues() == 3);
{
auto decorPortal = decor.ReadPortal();
VTKM_TEST_ASSERT(test_equal(decorPortal.Get(0), 13));
VTKM_TEST_ASSERT(test_equal(decorPortal.Get(1), 8));
VTKM_TEST_ASSERT(test_equal(decorPortal.Get(2), 43));
}
VTKM_TEST_ASSERT(a1.GetNumberOfValues() == 3);
{
auto a1Portal = a1.ReadPortal();
VTKM_TEST_ASSERT(test_equal(a1Portal.Get(0), 1));
VTKM_TEST_ASSERT(test_equal(a1Portal.Get(1), 0));
VTKM_TEST_ASSERT(test_equal(a1Portal.Get(2), 4));
}
VTKM_TEST_ASSERT(a2.GetNumberOfValues() == 3);
{
auto a2Portal = a2.ReadPortal();
VTKM_TEST_ASSERT(test_equal(a2Portal.Get(0), 3));
VTKM_TEST_ASSERT(test_equal(a2Portal.Get(1), 8));
VTKM_TEST_ASSERT(test_equal(a2Portal.Get(2), 3));
}
}
template <typename ValueType>
void operator()(const ValueType) const
{
this->InversionTest<ValueType>();
this->BinaryOperatorTest<ValueType, vtkm::Maximum>();
this->BinaryOperatorTest<ValueType, vtkm::Minimum>();
this->BinaryOperatorTest<ValueType, vtkm::Add>();
this->BinaryOperatorTest<ValueType, vtkm::Subtract>();
this->BinaryOperatorTest<ValueType, vtkm::Multiply>();
this->ScanExtendedToNumIndicesTest<ValueType>();
this->DecompositionTest<ValueType>();
}
};
// ArrayHandleDecorator that implements AllocateSourceArrays, thus allowing
// it to be resized.
struct ResizableDecorImpl
{
// We don't actually read/write from this, so use a dummy functor:
struct Functor
{
VTKM_EXEC_CONT vtkm::Id operator()(vtkm::Id) const { return 0; }
};
template <typename... PortalTs>
VTKM_CONT Functor CreateFunctor(PortalTs...) const
{
return Functor{};
}
template <typename Array1T, typename Array2T>
void AllocateSourceArrays(vtkm::Id newSize,
vtkm::CopyFlag preserve,
vtkm::cont::Token& token,
Array1T& a1,
Array2T& a2) const
{
VTKM_IS_ARRAY_HANDLE(Array1T);
VTKM_IS_ARRAY_HANDLE(Array2T);
// Resize differently based on preserve to verify the flag is correct.
if (preserve == vtkm::CopyFlag::Off)
{
// Resize each to 3*newSize:
a1.Allocate(3 * newSize, preserve, token);
a2.Allocate(3 * newSize, preserve, token);
}
else
{
// Resize each to 2*newSize:
a1.Allocate(2 * newSize, vtkm::CopyFlag::On);
a2.Allocate(2 * newSize, vtkm::CopyFlag::On);
}
}
};
// ArrayHandleDecorator that does not implement AllocateSourceArrays, thus not allowing
// it to be resized.
struct NonResizableDecorImpl
{
// We don't actually read/write from this, so use a dummy functor:
struct Functor
{
VTKM_EXEC_CONT vtkm::Id operator()(vtkm::Id) const { return 0; }
};
template <typename... PortalTs>
VTKM_CONT Functor CreateFunctor(PortalTs...) const
{
return Functor{};
}
};
void ResizeTest()
{
{
vtkm::cont::ArrayHandle<vtkm::Id> a1;
vtkm::cont::ArrayHandle<vtkm::Id> a2;
ResizableDecorImpl impl;
auto decor = vtkm::cont::make_ArrayHandleDecorator(5, impl, a1, a2);
VTKM_TEST_ASSERT(decor.GetNumberOfValues() == 5);
decor.Allocate(10); // Should allocate a1&a2 to have 30 values:
VTKM_TEST_ASSERT(a1.GetNumberOfValues() == 30);
VTKM_TEST_ASSERT(a2.GetNumberOfValues() == 30);
VTKM_TEST_ASSERT(decor.GetNumberOfValues() == 10);
decor.Allocate(3, vtkm::CopyFlag::On); // Should resize a1&a2 to have 6 values:
VTKM_TEST_ASSERT(a1.GetNumberOfValues() == 6);
VTKM_TEST_ASSERT(a2.GetNumberOfValues() == 6);
VTKM_TEST_ASSERT(decor.GetNumberOfValues() == 3);
}
{
vtkm::cont::ArrayHandle<vtkm::Id> a1;
a1.Allocate(20);
vtkm::cont::ArrayHandle<vtkm::Id> a2;
a2.Allocate(20);
NonResizableDecorImpl impl;
auto decor = vtkm::cont::make_ArrayHandleDecorator(5, impl, a1, a2);
VTKM_TEST_ASSERT(decor.GetNumberOfValues() == 5);
// Allocate and Shrink should throw an ErrorBadType:
bool threw = false;
try
{
decor.Allocate(10);
}
catch (vtkm::cont::ErrorBadType& e)
{
std::cerr << "Caught expected exception: " << e.what() << "\n";
threw = true;
}
VTKM_TEST_ASSERT(threw, "Allocate did not throw as expected.");
VTKM_TEST_ASSERT(decor.GetNumberOfValues() == 5);
threw = false;
try
{
decor.Allocate(3, vtkm::CopyFlag::On);
}
catch (vtkm::cont::ErrorBadType& e)
{
std::cerr << "Caught expected exception: " << e.what() << "\n";
threw = true;
}
VTKM_TEST_ASSERT(threw, "Allocate did not throw as expected.");
VTKM_TEST_ASSERT(decor.GetNumberOfValues() == 5);
}
}
void TestArrayHandleDecorator()
{
vtkm::testing::Testing::TryTypes(DecoratorTests{}, vtkm::TypeListScalarAll{});
ResizeTest();
}
} // namespace ut_decorator
int UnitTestArrayHandleDecorator(int argc, char* argv[])
{
return vtkm::cont::testing::Testing::Run(ut_decorator::TestArrayHandleDecorator, argc, argv);
}