vtk-m2/vtkm/cont/testing/UnitTestArrayPortalToIterators.cxx
Kenneth Moreland d1a4aecc59 Improvements to moving data into ArrayHandle
We have made several improvements to adding data into an `ArrayHandle`.

## Moving data from an `std::vector`

For numerous reasons, it is convenient to define data in a `std::vector`
and then wrap that into an `ArrayHandle`. It is often the case that an
`std::vector` is filled and then becomes unused once it is converted to an
`ArrayHandle`. In this case, what we really want is to pass the data off to
the `ArrayHandle` so that the `ArrayHandle` is now managing the data and
not the `std::vector`.

C++11 has a mechanism to do this: move semantics. You can now pass
variables to functions as an "rvalue" (right-hand value). When something is
passed as an rvalue, it can pull state out of that variable and move it
somewhere else. `std::vector` implements this movement so that an rvalue
can be moved to another `std::vector` without actually copying the data.
`make_ArrayHandle` now also takes advantage of this feature to move rvalue
`std::vector`s.

There is a special form of `make_ArrayHandle` named `make_ArrayHandleMove`
that takes an rvalue. There is also a special overload of
`make_ArrayHandle` itself that handles an rvalue `vector`. (However, using
the explicit move version is better if you want to make sure the data is
actually moved.)

## Make `ArrayHandle` from initalizer list

A common use case for using `std::vector` (particularly in our unit tests)
is to quickly add an initalizer list into an `ArrayHandle`. Now you can
by simply passing an initializer list to `make_ArrayHandle`.

## Deprecated `make_ArrayHandle` with default shallow copy

For historical reasons, passing an `std::vector` or a pointer to
`make_ArrayHandle` does a shallow copy (i.e. `CopyFlag` defaults to `Off`).
Although more efficient, this mode is inherintly unsafe, and making it the
default is asking for trouble.

To combat this, calling `make_ArrayHandle` without a copy flag is
deprecated. In this way, if you wish to do the faster but more unsafe
creation of an `ArrayHandle` you should explicitly express that.

This requried quite a few changes through the VTK-m source (particularly in
the tests).

## Similar changes to `Field`

`vtkm::cont::Field` has a `make_Field` helper function that is similar to
`make_ArrayHandle`. It also features the ability to create fields from
`std::vector`s and C arrays. It also likewise had the same unsafe behavior
by default of not copying from the source of the arrays.

That behavior has similarly been depreciated. You now have to specify a
copy flag.

The ability to construct a `Field` from an initializer list of values has
also been added.
2020-07-23 10:53:38 -06:00

277 lines
8.9 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/ArrayPortalToIterators.h>
#include <vtkm/cont/Logging.h>
#include <vtkm/cont/internal/ArrayPortalFromIterators.h>
#include <vtkm/VecTraits.h>
#include <vtkm/cont/testing/Testing.h>
namespace
{
template <typename T>
struct TemplatedTests
{
static constexpr vtkm::Id ARRAY_SIZE = 10;
using ValueType = T;
using ComponentType = typename vtkm::VecTraits<ValueType>::ComponentType;
static ValueType ExpectedValue(vtkm::Id index, ComponentType value)
{
return ValueType(static_cast<ComponentType>(index + static_cast<vtkm::Id>(value)));
}
class ReadOnlyArrayPortal
{
public:
using ValueType = T;
VTKM_CONT
ReadOnlyArrayPortal(ComponentType value)
: Value(value)
{
}
VTKM_CONT
vtkm::Id GetNumberOfValues() const { return ARRAY_SIZE; }
VTKM_CONT
ValueType Get(vtkm::Id index) const { return ExpectedValue(index, this->Value); }
private:
ComponentType Value;
};
class WriteOnlyArrayPortal
{
public:
using ValueType = T;
VTKM_CONT
WriteOnlyArrayPortal(ComponentType value)
: Value(value)
{
}
VTKM_CONT
vtkm::Id GetNumberOfValues() const { return ARRAY_SIZE; }
VTKM_CONT
void Set(vtkm::Id index, const ValueType& value) const
{
VTKM_TEST_ASSERT(value == ExpectedValue(index, this->Value),
"Set unexpected value in array portal.");
}
private:
ComponentType Value;
};
template <class IteratorType>
void FillIterator(IteratorType begin, IteratorType end, ComponentType value)
{
std::cout << " Check distance" << std::endl;
VTKM_TEST_ASSERT(std::distance(begin, end) == ARRAY_SIZE,
"Distance between begin and end incorrect.");
std::cout << " Write expected value in iterator." << std::endl;
vtkm::Id index = 0;
for (IteratorType iter = begin; iter != end; iter++)
{
*iter = ExpectedValue(index, value);
index++;
}
}
template <class IteratorType>
bool CheckIterator(IteratorType begin, IteratorType end, ComponentType value)
{
std::cout << " Check distance" << std::endl;
VTKM_TEST_ASSERT(std::distance(begin, end) == ARRAY_SIZE,
"Distance between begin and end incorrect.");
std::cout << " Read expected value from iterator." << std::endl;
vtkm::Id index = 0;
for (IteratorType iter = begin; iter != end; iter++)
{
VTKM_TEST_ASSERT(ValueType(*iter) == ExpectedValue(index, value),
"Got bad value from iterator.");
index++;
}
return true;
}
void TestIteratorRead()
{
using ArrayPortalType = ReadOnlyArrayPortal;
using GetIteratorsType = vtkm::cont::ArrayPortalToIterators<ArrayPortalType>;
static const ComponentType READ_VALUE = 23;
ArrayPortalType portal(READ_VALUE);
std::cout << " Testing read-only iterators with ArrayPortalToIterators." << std::endl;
GetIteratorsType iterators(portal);
CheckIterator(iterators.GetBegin(), iterators.GetEnd(), READ_VALUE);
std::cout << " Testing read-only iterators with convenience functions." << std::endl;
CheckIterator(vtkm::cont::ArrayPortalToIteratorBegin(portal),
vtkm::cont::ArrayPortalToIteratorEnd(portal),
READ_VALUE);
}
void TestIteratorWrite()
{
using ArrayPortalType = WriteOnlyArrayPortal;
using GetIteratorsType = vtkm::cont::ArrayPortalToIterators<ArrayPortalType>;
static const ComponentType WRITE_VALUE = 63;
ArrayPortalType portal(WRITE_VALUE);
std::cout << " Testing write-only iterators with ArrayPortalToIterators." << std::endl;
GetIteratorsType iterators(portal);
FillIterator(iterators.GetBegin(), iterators.GetEnd(), WRITE_VALUE);
std::cout << " Testing write-only iterators with convenience functions." << std::endl;
FillIterator(vtkm::cont::ArrayPortalToIteratorBegin(portal),
vtkm::cont::ArrayPortalToIteratorEnd(portal),
WRITE_VALUE);
}
void TestSimpleIterators()
{
std::array<T, ARRAY_SIZE> array;
T* begin = array.data();
T* end = begin + ARRAY_SIZE;
const T* cbegin = begin;
const T* cend = end;
vtkm::cont::ArrayHandle<T> arrayHandle =
vtkm::cont::make_ArrayHandle(begin, ARRAY_SIZE, vtkm::CopyFlag::Off);
std::cout
<< " Testing ArrayPortalToIterators(ArrayPortalFromIterators) gets back simple iterator."
<< std::endl;
{
auto portal = vtkm::cont::internal::ArrayPortalFromIterators<T*>(begin, end);
auto iter = vtkm::cont::ArrayPortalToIteratorBegin(portal);
VTKM_TEST_ASSERT(vtkm::cont::TypeToString(begin) == vtkm::cont::TypeToString(iter),
"Expected iterator type ",
vtkm::cont::TypeToString(begin),
" but got ",
vtkm::cont::TypeToString(iter));
VTKM_STATIC_ASSERT((std::is_same<T*, decltype(iter)>::value));
}
{
auto portal = vtkm::cont::internal::ArrayPortalFromIterators<const T*>(cbegin, cend);
auto iter = vtkm::cont::ArrayPortalToIteratorBegin(portal);
VTKM_TEST_ASSERT(vtkm::cont::TypeToString(cbegin) == vtkm::cont::TypeToString(iter),
"Expected iterator type ",
vtkm::cont::TypeToString(cbegin),
" but got ",
vtkm::cont::TypeToString(iter));
VTKM_STATIC_ASSERT((std::is_same<const T*, decltype(iter)>::value));
}
std::cout << " Testing that basic ArrayHandle has simple iterators." << std::endl;
{
auto portal = arrayHandle.WritePortal();
auto iter = vtkm::cont::ArrayPortalToIteratorBegin(portal);
VTKM_TEST_ASSERT(vtkm::cont::TypeToString(begin) == vtkm::cont::TypeToString(iter),
"Expected iterator type ",
vtkm::cont::TypeToString(begin),
" but got ",
vtkm::cont::TypeToString(iter));
VTKM_STATIC_ASSERT((std::is_same<T*, decltype(iter)>::value));
}
{
auto portal = arrayHandle.ReadPortal();
auto iter = vtkm::cont::ArrayPortalToIteratorBegin(portal);
VTKM_TEST_ASSERT(vtkm::cont::TypeToString(cbegin) == vtkm::cont::TypeToString(iter),
"Expected iterator type ",
vtkm::cont::TypeToString(cbegin),
" but got ",
vtkm::cont::TypeToString(iter));
VTKM_STATIC_ASSERT((std::is_same<const T*, decltype(iter)>::value));
}
}
void operator()()
{
TestIteratorRead();
TestIteratorWrite();
TestSimpleIterators();
}
};
struct TestFunctor
{
template <typename T>
void operator()(T) const
{
TemplatedTests<T> tests;
tests();
}
};
// Defines minimal API needed for ArrayPortalToIterators to detect and
// use custom iterators:
struct SpecializedIteratorAPITestPortal
{
using IteratorType = int;
IteratorType GetIteratorBegin() const { return 32; }
IteratorType GetIteratorEnd() const { return 13; }
};
void TestCustomIterator()
{
std::cout << " Testing custom iterator detection." << std::endl;
// Dummy portal type for this test:
using PortalType = SpecializedIteratorAPITestPortal;
using ItersType = vtkm::cont::ArrayPortalToIterators<PortalType>;
PortalType portal;
ItersType iters{ portal };
VTKM_TEST_ASSERT(
std::is_same<typename ItersType::IteratorType, typename PortalType::IteratorType>::value);
VTKM_TEST_ASSERT(
std::is_same<decltype(iters.GetBegin()), typename PortalType::IteratorType>::value);
VTKM_TEST_ASSERT(
std::is_same<decltype(iters.GetEnd()), typename PortalType::IteratorType>::value);
VTKM_TEST_ASSERT(iters.GetBegin() == 32);
VTKM_TEST_ASSERT(iters.GetEnd() == 13);
// Convenience API, too:
VTKM_TEST_ASSERT(std::is_same<decltype(vtkm::cont::ArrayPortalToIteratorBegin(portal)),
typename PortalType::IteratorType>::value);
VTKM_TEST_ASSERT(std::is_same<decltype(vtkm::cont::ArrayPortalToIteratorEnd(portal)),
typename PortalType::IteratorType>::value);
VTKM_TEST_ASSERT(vtkm::cont::ArrayPortalToIteratorBegin(portal) == 32);
VTKM_TEST_ASSERT(vtkm::cont::ArrayPortalToIteratorEnd(portal) == 13);
}
void TestArrayPortalToIterators()
{
vtkm::testing::Testing::TryTypes(TestFunctor());
TestCustomIterator();
}
} // Anonymous namespace
int UnitTestArrayPortalToIterators(int argc, char* argv[])
{
return vtkm::cont::testing::Testing::Run(TestArrayPortalToIterators, argc, argv);
}