// SPDX-FileCopyrightText: Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen // SPDX-License-Identifier: BSD-3-Clause #include "vtkDataArrayRange.h" #include "vtkAOSDataArrayTemplate.h" #include "vtkDataArray.h" #include "vtkFloatArray.h" #include "vtkSOADataArrayTemplate.h" #include "vtkScaledSOADataArrayTemplate.h" #include "vtkSmartPointer.h" #include #include #include #include #include #include namespace { std::size_t NumErrors = 0; #define TO_STRING(x) TO_STRING2(x) #define TO_STRING2(x) #x #define LOCATION() "line " TO_STRING(__LINE__) "" #define CHECK_TYPEDEF(t1, t2) \ static_assert(std::is_same::type, typename std::decay::type>{}, \ "Type mismatch: '" #t1 "' not same as '" #t2 "' in " LOCATION()) #define CHECK_IS_BASE_TYPE_OF(t1, t2) \ static_assert(std::is_base_of::type, typename std::decay::type>{}, \ "Type mismatch: '" #t1 "' not same as '" #t2 "' in " LOCATION()) // Various properties required by random access iterators: #define CHECK_ITER_TYPE(type) \ do \ { \ static_assert(std::is_default_constructible::value, \ "Iterator types must be default constructable at " LOCATION()); \ static_assert(std::is_copy_constructible::value, \ "Iterator types must be copy constructible at " LOCATION()); \ static_assert(std::is_copy_assignable::value, \ "Iterator types must be copy assignable at " LOCATION()); \ static_assert( \ std::is_destructible::value, "Iterator types must be destructible at " LOCATION()); \ } while (false) #define LOG_ERROR(message) \ do \ { \ ++NumErrors; \ std::cerr << NumErrors << ": " << message << "\n"; \ } while (false) #define CHECK_TRUE(expr) \ do \ { \ if (!(expr)) \ { \ LOG_ERROR("Expression not true: '" #expr << "' at " LOCATION()); \ } \ } while (false) #define CHECK_FALSE(expr) \ do \ { \ if ((expr)) \ { \ LOG_ERROR("Expression expected to be false but is true: '" #expr << "' at " LOCATION()); \ } \ } while (false) #define CHECK_EQUAL(v1, v2) \ do \ { \ if (!(v1 == v2)) \ { \ LOG_ERROR("Expressions not equal: '" #v1 "' (" << v1 << ") and '" #v2 "' (" << v2 \ << ") in " LOCATION()); \ } \ } while (false) #define CHECK_NOT_EQUAL(v1, v2) \ do \ { \ if (!(v1 != v2)) \ { \ LOG_ERROR("Expressions not equal: '" #v1 "' (" << v1 << ") and '" #v2 "' (" << v2 \ << ") in " LOCATION()); \ } \ } while (false) #define CHECK_EQUAL_NODUMP(v1, v2) \ do \ { \ if (!(v1 == v2)) \ { \ LOG_ERROR("Expressions not equal: '" #v1 "' and '" #v2 "' in " LOCATION()); \ } \ } while (false) #define CHECK_NOT_EQUAL_NODUMP(v1, v2) \ do \ { \ if (!(v1 != v2)) \ { \ LOG_ERROR( \ "Expressions should be unequal but aren't: '" #v1 "' and '" #v2 "' in " LOCATION()); \ } \ } while (false) //============================================================================== //============================================================================== // Helpers: //============================================================================== //============================================================================== template void FillValueRangeIota(Range range) { using ValueType = typename Range::ValueType; ValueType value{ 1 }; std::iota(range.begin(), range.end(), value); } template void TestIota(Range range) { auto startValue = range.GetBeginValueId() + 1; auto endValue = range.GetEndValueId() + 1; auto value = startValue; for (auto comp : range) { CHECK_EQUAL(value, comp); ++value; } CHECK_EQUAL(value, endValue); } //============================================================================== //============================================================================== // TupleRange: //============================================================================== //============================================================================== template struct UnitTestValueRangeAPI { static constexpr vtk::ComponentIdType NumComps = 3; static constexpr vtk::TupleIdType NumTuples = 12; static constexpr vtk::ValueIdType NumValues = NumTuples * NumComps; void operator()() { vtkNew array; auto da = static_cast(array); array->SetNumberOfComponents(NumComps); this->TestEmptyRange(vtk::DataArrayValueRange(array)); this->TestEmptyRange( vtk::DataArrayValueRange(da)); this->TestEmptyRange(vtk::DataArrayValueRange(array)); this->TestEmptyRange(vtk::DataArrayValueRange(da)); array->SetNumberOfTuples(this->NumTuples); this->TestEmptyRange(vtk::DataArrayValueRange(array, 4, 4)); this->TestEmptyRange( vtk::DataArrayValueRange( da, 4, 4)); this->TestEmptyRange(vtk::DataArrayValueRange(array, 4, 4)); this->TestEmptyRange( vtk::DataArrayValueRange(da, 4, 4)); FillValueRangeIota(vtk::DataArrayValueRange(array)); auto pStart = static_cast(NumTuples / 4 * NumComps + 1); auto pEnd = static_cast(3 * NumTuples / 4 * NumComps + 2); { // Full, dynamic-size, real typed range auto range = vtk::DataArrayValueRange(array); DispatchRangeTests(range, array, 0, NumValues); } { // Full, dynamic-size, generic-typed range auto range = vtk::DataArrayValueRange(da); DispatchRangeTests(range, array, 0, NumValues); } { // Full, fixed-size, real typed range auto range = vtk::DataArrayValueRange(array); DispatchRangeTests(range, array, 0, NumValues); } { // Full, fixed-size, generic-typed range auto range = vtk::DataArrayValueRange(da); DispatchRangeTests(range, array, 0, NumValues); } { // Partial, dynamic-size, real typed range auto range = vtk::DataArrayValueRange(array, pStart, pEnd); DispatchRangeTests(range, array, pStart, pEnd); } { // Partial, dynamic-size, generic-typed range auto range = vtk::DataArrayValueRange( da, pStart, pEnd); DispatchRangeTests(range, array, pStart, pEnd); } { // Partial, fixed-size, real typed range auto range = vtk::DataArrayValueRange(array, pStart, pEnd); DispatchRangeTests(range, array, pStart, pEnd); } { // Partial, fixed-size, generic-typed range auto range = vtk::DataArrayValueRange(da, pStart, pEnd); DispatchRangeTests(range, array, pStart, pEnd); } } template void TestEmptyRange(Range range) { for (auto value : range) { (void)value; CHECK_TRUE(false && "This should not execute."); } } template void DispatchRangeTests( Range range, RangeArrayType* array, vtk::ValueIdType start, vtk::ValueIdType end) { { TestRange(range, array, start, end); TestSubRange(range); TestDefaultInit(range); } { const Range& crange = range; TestRange(crange, array, start, end); TestSubRange(crange); } } template void TestRange(Range& range, RangeArrayType* array, vtk::ValueIdType start, vtk::ValueIdType end) { TestTypes(range); CHECK_EQUAL(range.GetArray(), array); CHECK_EQUAL(range.GetTupleSize(), NumComps); CHECK_EQUAL(range.GetBeginValueId(), start); CHECK_EQUAL(range.GetEndValueId(), end); CHECK_EQUAL(range.size(), end - start); CHECK_EQUAL(range.end() - range.begin(), range.size()); CHECK_EQUAL(range.cend() - range.cbegin(), range.size()); CHECK_EQUAL_NODUMP(*range.begin(), range[0]); CHECK_EQUAL_NODUMP(*(range.begin() + 1), range[1]); TestIota(range); } template void TestTypes(Range& range) { using ConstRange = typename std::add_const::type; using MutableRange = typename std::remove_const::type; using ActualValueType = vtk::GetAPIType; (void)range; // decltype doesn't actually count as a usage. CHECK_IS_BASE_TYPE_OF(typename Range::ArrayType, RangeArrayType); CHECK_TYPEDEF(typename Range::ValueType, ActualValueType); CHECK_TYPEDEF(typename Range::size_type, vtk::ValueIdType); CHECK_TYPEDEF(typename Range::size_type, decltype(range.size())); CHECK_TYPEDEF(typename Range::iterator, decltype(std::declval().begin())); CHECK_TYPEDEF(typename Range::iterator, decltype(std::declval().end())); CHECK_TYPEDEF(typename Range::const_iterator, decltype(std::declval().begin())); CHECK_TYPEDEF(typename Range::const_iterator, decltype(std::declval().end())); CHECK_TYPEDEF(typename Range::const_iterator, decltype(range.cbegin())); CHECK_TYPEDEF(typename Range::const_iterator, decltype(range.cend())); CHECK_TYPEDEF(typename Range::reference, decltype(std::declval()[0])); CHECK_TYPEDEF(typename Range::const_reference, decltype(std::declval()[0])); CHECK_TYPEDEF(typename Range::ArrayType, decltype(*range.GetArray())); CHECK_TYPEDEF(vtk::ValueIdType, decltype(range.GetBeginValueId())); CHECK_TYPEDEF(vtk::ValueIdType, decltype(range.GetEndValueId())); static_assert(Range::TupleSizeTag == RangeTupleSize, "Range::TupleSizeTag incorrect."); } template void TestSubRange(Range& range) { auto range1 = range.GetSubRange(3, 9); CHECK_EQUAL(range1.GetBeginValueId(), range.GetBeginValueId() + 3); CHECK_EQUAL(range1.GetEndValueId(), range.GetBeginValueId() + 9); { auto subRange = range1.GetSubRange(); CHECK_EQUAL(subRange.GetBeginValueId(), range1.GetBeginValueId()); CHECK_EQUAL(subRange.GetEndValueId(), range1.GetEndValueId()); } { auto subRange = range1.GetSubRange(2, 4); CHECK_EQUAL(subRange.GetBeginValueId(), range1.GetBeginValueId() + 2); CHECK_EQUAL(subRange.GetEndValueId(), range1.GetBeginValueId() + 4); } { auto subRange = range1.GetSubRange(1); CHECK_EQUAL(subRange.GetBeginValueId(), range1.GetBeginValueId() + 1); CHECK_EQUAL(subRange.GetEndValueId(), range1.GetEndValueId()); } { auto subRange = range1.GetSubRange(0, 5); CHECK_EQUAL(subRange.GetBeginValueId(), range1.GetBeginValueId()); CHECK_EQUAL(subRange.GetEndValueId(), range1.GetBeginValueId() + 5); } { auto subRange = range1.GetSubRange(0, 0); CHECK_EQUAL(subRange.GetBeginValueId(), range1.GetBeginValueId()); CHECK_EQUAL(subRange.GetEndValueId(), range1.GetBeginValueId()); } } template void TestDefaultInit(Range& range) { auto range1 = Range{}; range1 = range; (void)range; } }; template struct UnitTestValueIteratorAPI { static constexpr vtk::ComponentIdType NumComps = 3; static constexpr vtk::TupleIdType NumTuples = 12; static constexpr vtk::ValueIdType NumValues = NumComps * NumTuples; void operator()() { vtkNew array; array->SetNumberOfComponents(NumComps); array->SetNumberOfTuples(NumTuples); FillValueRangeIota(vtk::DataArrayValueRange(array)); auto da = static_cast(array); { // Full, dynamic-size, real typed range auto range = vtk::DataArrayValueRange(array); DispatchRangeTests(range); } { // Full, dynamic-size, generic-typed range auto range = vtk::DataArrayValueRange(da); DispatchRangeTests(range); } { // Full, fixed-size, real typed range auto range = vtk::DataArrayValueRange(array); DispatchRangeTests(range); } { // Full, fixed-size, generic-typed range auto range = vtk::DataArrayValueRange(da); DispatchRangeTests(range); } } template void DispatchRangeTests(Range range) { { TestTypes(range); TestValueIterator(range); TestConstValueIterator(range); } { const Range& crange = range; TestTypes(crange); TestConstValueIterator(crange); } } template void TestTypes(Range& range) { using Iter = decltype(this->GetTestingIter(range)); (void)range; CHECK_ITER_TYPE(Iter); CHECK_TYPEDEF( typename std::iterator_traits::reference, decltype(std::declval()[0])); } template void TestValueIterator(Range& range) { TestDeref(range); TestIndexing(range); TestIterSwap(range); TestConstCopy(range); TestConstAssign(range); } template void TestConstValueIterator(Range& range) { TestCopy(range); TestAssign(range); TestTraversal(range); TestDerefConst(range); TestComparison(range); TestIndexingConst(range); TestSwap(range); } template void TestCopy(Range& range) { auto iter = this->GetTestingIter(range); using IterType = decltype(iter); IterType iter2{ iter }; CHECK_EQUAL_NODUMP(iter, iter2); } template void TestConstCopy(Range& range) { // This should only get called with non-const ranges: static_assert(!std::is_const::value, "Expected mutable range."); // We should be able to implicitly cast and compare mutable iterators to // const ones: typename Range::iterator iter{ range.begin() }; typename Range::const_iterator citer{ iter }; CHECK_EQUAL_NODUMP(iter, citer); } template void TestAssign(Range& range) { auto iter = this->GetTestingIter(range); auto iter2 = iter + 1; auto iter3 = iter + 2; CHECK_NOT_EQUAL_NODUMP(iter, iter2); CHECK_NOT_EQUAL_NODUMP(iter, iter3); CHECK_NOT_EQUAL_NODUMP(iter2, iter3); iter2 = iter3 = iter; CHECK_EQUAL_NODUMP(iter, iter2); CHECK_EQUAL_NODUMP(iter, iter3); CHECK_EQUAL_NODUMP(iter2, iter3); } template void TestConstAssign(Range& range) { // This should only get called with non-const ranges: static_assert(!std::is_const::value, "Expected mutable range."); // We should be able to implicitly cast and compare mutable objects to // const ones: typename Range::iterator iter{ range.begin() }; typename Range::const_iterator citer{ range.cend() }; citer = iter; CHECK_EQUAL_NODUMP(iter, citer); } template void TestTraversal(Range& range) { { // operator++ (prefix) auto iter1 = this->GetTestingIter(range); auto iter2 = iter1; auto iter3 = ++iter2; CHECK_NOT_EQUAL_NODUMP(iter1, iter2); CHECK_NOT_EQUAL_NODUMP(iter1, iter3); CHECK_EQUAL_NODUMP(iter2, iter3); CHECK_EQUAL(iter2 - iter1, 1); } { // operator++ (postfix) auto iter1 = this->GetTestingIter(range); auto iter2 = iter1; auto iter3 = iter2++; CHECK_NOT_EQUAL_NODUMP(iter1, iter2); CHECK_EQUAL_NODUMP(iter1, iter3); CHECK_NOT_EQUAL_NODUMP(iter2, iter3); CHECK_EQUAL(iter2 - iter1, 1); } { // operator-- (prefix) auto iter1 = this->GetTestingIter(range); auto iter2 = iter1; auto iter3 = --iter2; CHECK_NOT_EQUAL_NODUMP(iter1, iter2); CHECK_NOT_EQUAL_NODUMP(iter1, iter3); CHECK_EQUAL_NODUMP(iter2, iter3); CHECK_EQUAL(iter2 - iter1, -1); } { // operator-- (postfix) auto iter1 = this->GetTestingIter(range); auto iter2 = iter1; auto iter3 = iter2--; CHECK_NOT_EQUAL_NODUMP(iter1, iter2); CHECK_EQUAL_NODUMP(iter1, iter3); CHECK_NOT_EQUAL_NODUMP(iter2, iter3); CHECK_EQUAL(iter2 - iter1, -1); } { // operator += auto iter1 = this->GetTestingIter(range) - 1; auto iter2 = iter1 + 2; CHECK_NOT_EQUAL_NODUMP(iter1, iter2); CHECK_EQUAL(iter2 - iter1, 2); iter1 += 2; CHECK_EQUAL_NODUMP(iter1, iter2); } { // operator -= auto iter1 = this->GetTestingIter(range) + 1; auto iter2 = iter1 - 2; CHECK_NOT_EQUAL_NODUMP(iter1, iter2); CHECK_EQUAL(iter2 - iter1, -2); iter1 -= 2; CHECK_EQUAL_NODUMP(iter1, iter2); } { // operator + (it, off) auto iter1 = this->GetTestingIter(range) - 1; auto iter2 = iter1 + 2; CHECK_NOT_EQUAL_NODUMP(iter1, iter2); CHECK_EQUAL(iter2 - iter1, 2); } { // operator + (off, it) auto iter1 = this->GetTestingIter(range) - 1; auto iter2 = 2 + iter1; CHECK_NOT_EQUAL_NODUMP(iter1, iter2); CHECK_EQUAL(iter2 - iter1, 2); } { // operator - (it, off) auto iter1 = this->GetTestingIter(range) + 1; auto iter2 = iter1 - 2; CHECK_NOT_EQUAL_NODUMP(iter1, iter2); CHECK_EQUAL(iter2 - iter1, -2); } { // operator - (it, it) auto iter1 = this->GetTestingIter(range); auto iter2 = iter1; CHECK_EQUAL(iter2 - iter1, 0); iter2++; CHECK_EQUAL(iter2 - iter1, 1); iter2--; CHECK_EQUAL(iter2 - iter1, 0); --iter2; CHECK_EQUAL(iter2 - iter1, -1); iter1++; CHECK_EQUAL(iter2 - iter1, -2); } } template void TestComparison(Range& range) { { // operator == auto iter1 = this->GetTestingIter(range); auto iter2 = iter1; CHECK_TRUE(iter1 == iter2); ++iter2; CHECK_FALSE(iter1 == iter2); } { // operator != auto iter1 = this->GetTestingIter(range); auto iter2 = iter1; CHECK_FALSE(iter1 != iter2); ++iter2; CHECK_TRUE(iter1 != iter2); } { // operator < auto iter1 = this->GetTestingIter(range); auto iter2 = iter1 + 1; CHECK_TRUE(iter1 < iter2); CHECK_FALSE(iter2 < iter1); } { // operator > auto iter1 = this->GetTestingIter(range); auto iter2 = iter1 - 1; CHECK_TRUE(iter1 > iter2); CHECK_FALSE(iter2 > iter1); } { // operator <= auto iter1 = this->GetTestingIter(range); auto iter2 = iter1; CHECK_TRUE(iter1 <= iter2); CHECK_TRUE(iter2 <= iter1); ++iter2; CHECK_TRUE(iter1 <= iter2); CHECK_FALSE(iter2 <= iter1); } { // operator >= auto iter1 = this->GetTestingIter(range); auto iter2 = iter1; CHECK_TRUE(iter1 >= iter2); CHECK_TRUE(iter2 >= iter1); --iter2; CHECK_TRUE(iter1 >= iter2); CHECK_FALSE(iter2 >= iter1); } } template void TestDerefConst(Range& range) { using ValueType = typename Range::ValueType; auto start = this->GetTestingIter(range); auto end = start + 4; ValueType value = *start; for (auto it = start; it < end; ++it) { CHECK_EQUAL(value++, *it); } } template void TestDeref(Range& range) { using ValueType = typename Range::ValueType; auto start = this->GetTestingIter(range); auto end = start + 4; ValueType initialValue = *start; for (auto it = start; it < end; ++it) { *it = 10; } for (auto it = start; it < end; ++it) { CHECK_EQUAL(*it, 10); } // Modifying value types shouldn't modify underlying storage: for (auto it = start; it < end; ++it) { ValueType comp = *it; comp = 16; (void)comp; // Silence set-but-not-used warning } for (auto it = start; it < end; ++it) { CHECK_EQUAL(*it, 10); // Still 10 } // Modifying reference should modify underlying storage: for (auto it = start; it < end; ++it) { using RefType = typename Range::reference; RefType comp = *it; comp = 16; } for (auto it = start; it < end; ++it) { CHECK_EQUAL(*it, 16); } // Restore: for (auto it = start; it < end; ++it) { *it = initialValue++; } } template void TestIndexingConst(Range& range) { using ValueType = typename Range::value_type; using IndexType = typename Range::size_type; auto iter = this->GetTestingIter(range); ValueType value = *iter; for (IndexType i = 0; i < 4; ++i) { CHECK_EQUAL(value++, iter[i]); } } template void TestIndexing(Range& range) { using IndexType = typename Range::size_type; using ValueType = typename Range::value_type; auto iter = this->GetTestingIter(range); ValueType initialValue = *iter; for (IndexType i = 0; i < 4; ++i) { iter[i] = 19; } for (auto it = iter; it < iter + 4; ++it) { CHECK_EQUAL(*it, 19); } // Restore: for (IndexType i = 0; i < 4; ++i) { iter[i] = initialValue++; } } template void TestSwap(Range& range) { auto iter = this->GetTestingIter(range); auto iter1 = iter; auto iter2 = iter1 + 1; CHECK_TRUE(iter1 < iter2); CHECK_FALSE(iter2 < iter1); CHECK_TRUE(iter1 + 1 == iter2); CHECK_TRUE(iter == iter1); { // ADL swap: using std::swap; swap(iter1, iter2); } CHECK_FALSE(iter1 < iter2); CHECK_TRUE(iter2 < iter1); CHECK_TRUE(iter2 + 1 == iter1); CHECK_TRUE(iter == iter2); { // ADL swap: using std::swap; swap(iter1, iter2); } CHECK_TRUE(iter1 < iter2); CHECK_FALSE(iter2 < iter1); CHECK_TRUE(iter1 + 1 == iter2); CHECK_TRUE(iter == iter1); } template void TestIterSwap(Range& range) { using ValueType = typename Range::ValueType; auto iter = this->GetTestingIter(range); auto iter1 = iter; auto iter2 = iter1 + 1; ValueType val1 = *iter1; ValueType val2 = *iter2; CHECK_TRUE(iter1 < iter2); CHECK_FALSE(iter2 < iter1); CHECK_TRUE(iter1 + 1 == iter2); CHECK_TRUE(iter == iter1); CHECK_EQUAL(val1, *iter1); CHECK_EQUAL(val2, *iter2); std::iter_swap(iter1, iter2); CHECK_TRUE(iter1 < iter2); CHECK_FALSE(iter2 < iter1); CHECK_TRUE(iter1 + 1 == iter2); CHECK_TRUE(iter == iter1); CHECK_EQUAL(val1, *iter2); CHECK_EQUAL(val2, *iter1); std::iter_swap(iter1, iter2); CHECK_TRUE(iter1 < iter2); CHECK_FALSE(iter2 < iter1); CHECK_TRUE(iter1 + 1 == iter2); CHECK_TRUE(iter == iter1); CHECK_EQUAL(val1, *iter1); CHECK_EQUAL(val2, *iter2); } // Returns an iterator. tupleOffset allows iterators from different tuples to // be obtained. The returned iterator +/- 4 are guaranteed valid. template static auto GetTestingIter(Range& range) -> decltype(range.begin()) { return range.begin() + NumValues / 2; } }; template struct UnitTestValueReferenceAPI { static constexpr vtk::ComponentIdType NumComps = 9; static constexpr vtk::TupleIdType NumTuples = 5; void operator()() { vtkNew array; array->SetNumberOfComponents(NumComps); array->SetNumberOfTuples(NumTuples); FillValueRangeIota(vtk::DataArrayValueRange(array)); auto da = static_cast(array); { // Full, dynamic-size, real typed range auto range = vtk::DataArrayValueRange(array); DispatchRangeTests(range); } { // Full, dynamic-size, generic-typed range auto range = vtk::DataArrayValueRange(da); DispatchRangeTests(range); } { // Full, fixed-size, real typed range auto range = vtk::DataArrayValueRange(array); DispatchRangeTests(range); } { // Full, fixed-size, generic-typed range auto range = vtk::DataArrayValueRange(da); DispatchRangeTests(range); } } template void DispatchRangeTests(Range range) { { TestValueReference(range); TestConstValueReference(range); } { const Range& crange = range; TestConstValueReference(crange); } } template void TestValueReference(Range& range) { TestCopy(range); TestAssign(range); TestSwap(range); TestMath(range); } template void TestConstValueReference(Range& range) { TestComparison(range); TestConstMath(range); } template void TestCopy(Range& range) { using APIType = typename Range::value_type; using RefType = typename Range::reference; RefType ref1 = this->GetTestRef(range, 0); const APIType val = ref1; RefType ref1Copy{ ref1 }; CHECK_EQUAL_NODUMP(ref1, ref1Copy); CHECK_EQUAL_NODUMP(val, ref1Copy); ref1Copy = val - 1; CHECK_EQUAL_NODUMP(ref1, ref1Copy); CHECK_EQUAL_NODUMP(ref1Copy, val - 1); CHECK_EQUAL_NODUMP(ref1, val - 1); ref1 = val; CHECK_EQUAL_NODUMP(ref1, ref1Copy); CHECK_EQUAL_NODUMP(ref1Copy, val); CHECK_EQUAL_NODUMP(ref1, val); } template void TestAssign(Range& range) { using APIType = typename Range::value_type; using RefType = typename Range::reference; RefType ref1 = this->GetTestRef(range, 0); const APIType val = ref1; RefType ref1Copy{ ref1 }; CHECK_EQUAL_NODUMP(ref1, ref1Copy); CHECK_EQUAL_NODUMP(val, ref1Copy); ref1Copy = val - 1; CHECK_EQUAL_NODUMP(ref1, ref1Copy); CHECK_EQUAL_NODUMP(ref1Copy, val - 1); CHECK_EQUAL_NODUMP(ref1, val - 1); ref1 = val; CHECK_EQUAL_NODUMP(ref1, ref1Copy); CHECK_EQUAL_NODUMP(ref1Copy, val); CHECK_EQUAL_NODUMP(ref1, val); auto ref2 = this->GetTestRef(range, 1); CHECK_EQUAL_NODUMP(ref2, val + 1); CHECK_NOT_EQUAL_NODUMP(ref1, ref2); CHECK_NOT_EQUAL_NODUMP(ref1Copy, ref2); CHECK_NOT_EQUAL_NODUMP(val, ref2); ref1 = ref2; CHECK_EQUAL_NODUMP(ref1, ref2); CHECK_EQUAL_NODUMP(ref1Copy, ref2); CHECK_EQUAL_NODUMP(ref1, val + 1); CHECK_EQUAL_NODUMP(ref1Copy, val + 1); ref1 = val; CHECK_EQUAL_NODUMP(ref1, ref1Copy); CHECK_EQUAL_NODUMP(ref1Copy, val); CHECK_EQUAL_NODUMP(ref1, val); CHECK_EQUAL_NODUMP(ref2, val + 1); } template void TestSwap(Range& range) { using APIType = typename Range::value_type; auto ref1 = this->GetTestRef(range, 0); const APIType val1 = ref1; APIType val2 = val1 + 1; // ADL swap: using std::swap; swap(ref1, val2); CHECK_EQUAL_NODUMP(ref1, val1 + 1); CHECK_EQUAL_NODUMP(val1, val2); swap(val2, ref1); CHECK_EQUAL_NODUMP(ref1, val1); CHECK_EQUAL_NODUMP(val2, val1 + 1); auto ref2 = this->GetTestRef(range, 1); CHECK_EQUAL_NODUMP(ref2, val2); swap(ref1, ref2); CHECK_EQUAL_NODUMP(ref1, val2); CHECK_EQUAL_NODUMP(ref2, val1); swap(ref2, ref1); CHECK_EQUAL_NODUMP(ref1, val1); CHECK_EQUAL_NODUMP(ref2, val2); } template void TestMath(Range& range) { // Testing mutable math only. Const math is tested in TestConstMath using APIType = typename Range::value_type; using RefType = typename Range::reference; RefType ref1 = this->GetTestRef(range, 0); RefType ref2 = this->GetTestRef(range, 1); const APIType val1 = ref1; const APIType val2 = ref2; constexpr APIType one = static_cast(1); constexpr APIType two = static_cast(2); constexpr APIType bignum = static_cast(120); // must fit in int8 // += { auto v = (ref1 += one); CHECK_EQUAL_NODUMP(ref1, v); CHECK_EQUAL_NODUMP(ref1, val1 + one); ref1 = val1; } { APIType tmp = one; auto v = (tmp += ref1); CHECK_EQUAL_NODUMP(ref1, val1); CHECK_EQUAL_NODUMP(tmp, val1 + one); CHECK_EQUAL_NODUMP(v, val1 + one); } { auto v = (ref1 += ref2); CHECK_EQUAL_NODUMP(ref1, val1 + val2); CHECK_EQUAL_NODUMP(ref2, val2); CHECK_EQUAL_NODUMP(v, val1 + val2); ref1 = val1; } // -= { auto v = (ref1 -= one); CHECK_EQUAL_NODUMP(ref1, v); CHECK_EQUAL_NODUMP(ref1, val1 - one); ref1 = val1; } { APIType tmp = bignum; auto v = (tmp -= ref1); CHECK_EQUAL_NODUMP(ref1, val1); CHECK_EQUAL_NODUMP(tmp, bignum - val1); CHECK_EQUAL_NODUMP(v, bignum - val1); } { auto v = (ref1 -= ref2); CHECK_EQUAL_NODUMP(ref1, val1 - val2); CHECK_EQUAL_NODUMP(ref2, val2); CHECK_EQUAL_NODUMP(v, val1 - val2); ref1 = val1; } // *= { auto v = (ref1 *= two); CHECK_EQUAL_NODUMP(ref1, v); CHECK_EQUAL_NODUMP(ref1, val1 * two); ref1 = val1; } { APIType tmp = two; auto v = (tmp *= ref1); CHECK_EQUAL_NODUMP(ref1, val1); CHECK_EQUAL_NODUMP(tmp, val1 * two); CHECK_EQUAL_NODUMP(v, val1 * two); } { auto v = (ref1 *= ref2); CHECK_EQUAL_NODUMP(ref1, val1 * val2); CHECK_EQUAL_NODUMP(ref2, val2); CHECK_EQUAL_NODUMP(v, val1 * val2); ref1 = val1; } // /= { auto v = (ref1 /= two); CHECK_EQUAL_NODUMP(ref1, v); CHECK_EQUAL_NODUMP(ref1, val1 / two); ref1 = val1; } { APIType tmp = bignum; auto v = (tmp /= ref1); CHECK_EQUAL_NODUMP(ref1, val1); CHECK_EQUAL_NODUMP(tmp, bignum / val1); CHECK_EQUAL_NODUMP(v, bignum / val1); } { auto v = (ref1 /= ref2); // Use a tolerance test to account for rounding errors. CHECK_TRUE(std::fabs(ref1 - APIType{ val1 / val2 }) < 1e-5); CHECK_EQUAL_NODUMP(ref2, val2); CHECK_TRUE(std::fabs(v - APIType{ val1 / val2 }) < 1e-5); ref1 = val1; } // ++ (pre) { auto v = ++ref1; CHECK_EQUAL_NODUMP(ref1, val1 + one); CHECK_EQUAL_NODUMP(v, val1 + one); ref1 = val1; } // ++ (post) { auto v = ref1++; CHECK_EQUAL_NODUMP(ref1, val1 + one); CHECK_EQUAL_NODUMP(v, val1); ref1 = val1; } // -- (pre) { auto v = --ref1; CHECK_EQUAL_NODUMP(ref1, val1 - one); CHECK_EQUAL_NODUMP(v, val1 - one); ref1 = val1; } // -- (post) { auto v = ref1--; CHECK_EQUAL_NODUMP(ref1, val1 - one); CHECK_EQUAL_NODUMP(v, val1); ref1 = val1; } } template void TestComparison(Range& range) { using APIType = typename Range::value_type; auto ref1 = this->GetTestRef(range, 0); auto refTmp = this->GetTestRef(range, 0); // same as ref1 auto ref2 = this->GetTestRef(range, 1); const APIType val1 = ref1; const APIType val2 = ref2; constexpr APIType one = static_cast(1); constexpr APIType bignum = static_cast(120); // must fit in int8 // == CHECK_TRUE(ref1 == val1); CHECK_TRUE(ref1 == refTmp); CHECK_FALSE(ref1 == val2); CHECK_FALSE(ref2 == refTmp); // != CHECK_FALSE(ref1 != val1); CHECK_FALSE(ref1 != refTmp); CHECK_TRUE(ref1 != val2); CHECK_TRUE(ref2 != refTmp); // < CHECK_TRUE(ref1 < bignum); CHECK_TRUE(one < ref1); CHECK_TRUE(ref1 < ref2); CHECK_TRUE(refTmp < ref2); CHECK_FALSE(bignum < ref1); CHECK_FALSE(ref1 < one); CHECK_FALSE(ref2 < ref1); CHECK_FALSE(ref2 < refTmp); CHECK_FALSE(ref1 < refTmp); CHECK_FALSE(ref1 < val1); CHECK_FALSE(val1 < ref1); // > CHECK_FALSE(ref1 > bignum); CHECK_FALSE(one > ref1); CHECK_FALSE(ref1 > ref2); CHECK_FALSE(refTmp > ref2); CHECK_TRUE(bignum > ref1); CHECK_TRUE(ref1 > one); CHECK_TRUE(ref2 > ref1); CHECK_TRUE(ref2 > refTmp); CHECK_FALSE(ref1 > refTmp); CHECK_FALSE(ref1 > val1); CHECK_FALSE(val1 > ref1); // <= CHECK_TRUE(ref1 <= bignum); CHECK_TRUE(one <= ref1); CHECK_TRUE(ref1 <= ref2); CHECK_TRUE(refTmp <= ref2); CHECK_FALSE(bignum <= ref1); CHECK_FALSE(ref1 <= one); CHECK_FALSE(ref2 <= ref1); CHECK_FALSE(ref2 <= refTmp); CHECK_TRUE(ref1 <= refTmp); CHECK_TRUE(ref1 <= val1); CHECK_TRUE(val1 <= ref1); // >= CHECK_FALSE(ref1 >= bignum); CHECK_FALSE(one >= ref1); CHECK_FALSE(ref1 >= ref2); CHECK_FALSE(refTmp >= ref2); CHECK_TRUE(bignum >= ref1); CHECK_TRUE(ref1 >= one); CHECK_TRUE(ref2 >= ref1); CHECK_TRUE(ref2 >= refTmp); CHECK_TRUE(ref1 >= refTmp); CHECK_TRUE(ref1 >= val1); CHECK_TRUE(val1 >= ref1); } template void TestConstMath(Range& range) { // Testing const math only. Mutable math is tested in TestMath using APIType = typename Range::value_type; using CRefType = typename Range::const_reference; const CRefType ref1 = this->GetTestRef(range, 0); const CRefType ref2 = this->GetTestRef(range, 1); const APIType val1 = ref1; const APIType val2 = ref2; constexpr APIType one = static_cast(1); constexpr APIType two = static_cast(2); constexpr APIType bignum = static_cast(120); // must fit in int8 // + { auto v = (ref1 + one); CHECK_EQUAL_NODUMP(ref1, val1); CHECK_EQUAL_NODUMP(v, val1 + one); } { auto v = (one + ref1); CHECK_EQUAL_NODUMP(ref1, val1); CHECK_EQUAL_NODUMP(v, val1 + one); } { auto v = (ref1 + ref2); CHECK_EQUAL_NODUMP(ref1, val1); CHECK_EQUAL_NODUMP(ref2, val2); CHECK_EQUAL_NODUMP(v, val1 + val2); } // - { auto v = (ref1 - one); CHECK_EQUAL_NODUMP(ref1, val1); CHECK_EQUAL_NODUMP(v, val1 - one); } { auto v = (bignum - ref1); CHECK_EQUAL_NODUMP(ref1, val1); CHECK_EQUAL_NODUMP(v, bignum - val1); } { auto v = (ref1 - ref2); CHECK_EQUAL_NODUMP(ref1, val1); CHECK_EQUAL_NODUMP(ref2, val2); CHECK_EQUAL_NODUMP(v, val1 - val2); } // * { auto v = (ref1 * two); CHECK_EQUAL_NODUMP(ref1, val1); CHECK_EQUAL_NODUMP(v, val1 * two); } { auto v = (two * ref1); CHECK_EQUAL_NODUMP(ref1, val1); CHECK_EQUAL_NODUMP(v, val1 * two); } { auto v = (ref1 * ref2); CHECK_EQUAL_NODUMP(ref1, val1); CHECK_EQUAL_NODUMP(ref2, val2); CHECK_EQUAL_NODUMP(v, val1 * val2); } // / { auto v = (ref1 / two); CHECK_EQUAL_NODUMP(ref1, val1); CHECK_EQUAL_NODUMP(v, val1 / two); } { auto v = (bignum / ref1); CHECK_EQUAL_NODUMP(ref1, val1); CHECK_EQUAL_NODUMP(v, bignum / val1); } { auto v = (ref1 / ref2); CHECK_EQUAL_NODUMP(ref1, val1); CHECK_EQUAL_NODUMP(ref2, val2); CHECK_EQUAL_NODUMP(v, val1 / val2); } } // Return a reference. Valid offsets range from (-4, 4), and // values increase with offset. template auto GetTestRef(Range& range, vtk::ValueIdType offset) -> decltype(std::declval()[0]) { assert(offset >= -4 && offset <= 4); return range[6 + offset]; } }; struct UnitTestEdgeCases { static constexpr vtk::ComponentIdType NumComps = 3; static constexpr vtk::TupleIdType NumTuples = 12; void operator()() { TestSpecializations(); std::cerr << "SOA <--> AOS\n"; DispatchValueCompat, vtkAOSDataArrayTemplate>(); std::cerr << "AOS <--> SOA\n"; DispatchValueCompat, vtkSOADataArrayTemplate>(); std::cerr << "SOA <--> AOS\n"; DispatchValueCompat, vtkAOSDataArrayTemplate>(); std::cerr << "AOS <--> SOA\n"; DispatchValueCompat, vtkSOADataArrayTemplate>(); std::cerr << "SOA <--> AOS\n"; DispatchValueCompat, vtkAOSDataArrayTemplate>(); std::cerr << "AOS <--> SOA\n"; DispatchValueCompat, vtkSOADataArrayTemplate>(); std::cerr << "ScaleSOA <--> AOS\n"; DispatchValueCompat, vtkAOSDataArrayTemplate>(); std::cerr << "AOS <--> ScaleSOA\n"; DispatchValueCompat, vtkScaledSOADataArrayTemplate>(); std::cerr << "ScaleSOA <--> AOS\n"; DispatchValueCompat, vtkAOSDataArrayTemplate>(); std::cerr << "AOS <--> ScaleSOA\n"; DispatchValueCompat, vtkScaledSOADataArrayTemplate>(); std::cerr << "ScaleSOA <--> AOS\n"; DispatchValueCompat, vtkAOSDataArrayTemplate>(); std::cerr << "AOS <--> ScaleSOA\n"; DispatchValueCompat, vtkScaledSOADataArrayTemplate>(); } static void TestSpecializations() { // Specializations are disabled when iterator debugging is enabled: #ifndef VTK_DEBUG_RANGE_ITERATORS // These should use the objects in vtkDataArrayTupleRange_AOS.h, which // end up using ValueType* pointers for component iterators. TestAOSSpecialization>(); TestAOSSpecialization(); #endif } #ifndef VTK_DEBUG_RANGE_ITERATORS template static void TestAOSSpecialization() { using ValueType = vtk::GetAPIType; using RangeType = decltype(vtk::DataArrayValueRange(std::declval())); using ValueIterType = decltype(std::declval().begin()); // Sanity Check: static_assert(vtk::IsAOSDataArray::value, "Not AOS type?"); // Ensure that iterator type is just a pointer: static_assert(std::is_same::type>::value, "AOS specialization not used!"); } #endif template static void PrepArray(ArrayType* array) { array->SetNumberOfComponents(NumComps); array->SetNumberOfTuples(NumTuples); FillValueRangeIota(vtk::DataArrayValueRange(array)); } template void DispatchValueCompat() { vtkNew storage1; vtkNew storage2; this->PrepArray(static_cast(storage1)); this->PrepArray(static_cast(storage2)); ArrayT1* a1 = storage1; ArrayT2* a2 = storage2; vtkDataArray* da1 = a1; vtkDataArray* da2 = a2; // Generate ranges: // - derived and vtkDataArray pointers // - dynamic and fixed tuple sizes // - mutable and const tuple sizes auto aRange1 = vtk::DataArrayValueRange(a1); auto aRange2 = vtk::DataArrayValueRange(a2); auto daRange1 = vtk::DataArrayValueRange(da1); auto daRange2 = vtk::DataArrayValueRange(da2); auto aFixedRange1 = vtk::DataArrayValueRange(a1); auto aFixedRange2 = vtk::DataArrayValueRange(a2); auto daFixedRange1 = vtk::DataArrayValueRange(da1); auto daFixedRange2 = vtk::DataArrayValueRange(da2); using ARange1Type = decltype(aRange1); using ARange2Type = decltype(aRange2); using DARange1Type = decltype(daRange1); using DARange2Type = decltype(daRange2); using AFixedRange1Type = decltype(aFixedRange1); using AFixedRange2Type = decltype(aFixedRange2); using DAFixedRange1Type = decltype(daFixedRange1); using DAFixedRange2Type = decltype(daFixedRange2); const ARange1Type caRange1 = aRange1; const ARange2Type caRange2 = aRange2; const DARange1Type cdaRange1 = daRange1; const DARange2Type cdaRange2 = daRange2; const AFixedRange1Type caFixedRange1 = aFixedRange1; const AFixedRange2Type caFixedRange2 = aFixedRange2; const DAFixedRange1Type cdaFixedRange1 = daFixedRange1; const DAFixedRange2Type cdaFixedRange2 = daFixedRange2; this->LaunchTests(aRange1, aRange2); this->LaunchTests(aRange1, daRange2); this->LaunchTests(aRange1, aFixedRange2); this->LaunchTests(aRange1, daFixedRange2); this->LaunchTests(aRange1, caRange2); this->LaunchTests(aRange1, cdaRange2); this->LaunchTests(aRange1, caFixedRange2); this->LaunchTests(aRange1, cdaFixedRange2); this->LaunchTests(daRange1, aRange2); this->LaunchTests(daRange1, daRange2); this->LaunchTests(daRange1, aFixedRange2); this->LaunchTests(daRange1, daFixedRange2); this->LaunchTests(daRange1, caRange2); this->LaunchTests(daRange1, cdaRange2); this->LaunchTests(daRange1, caFixedRange2); this->LaunchTests(daRange1, cdaFixedRange2); this->LaunchTests(aFixedRange1, aRange2); this->LaunchTests(aFixedRange1, daRange2); this->LaunchTests(aFixedRange1, aFixedRange2); this->LaunchTests(aFixedRange1, daFixedRange2); this->LaunchTests(aFixedRange1, caRange2); this->LaunchTests(aFixedRange1, cdaRange2); this->LaunchTests(aFixedRange1, caFixedRange2); this->LaunchTests(aFixedRange1, cdaFixedRange2); this->LaunchTests(daFixedRange1, aRange2); this->LaunchTests(daFixedRange1, daRange2); this->LaunchTests(daFixedRange1, aFixedRange2); this->LaunchTests(daFixedRange1, daFixedRange2); this->LaunchTests(daFixedRange1, caRange2); this->LaunchTests(daFixedRange1, cdaRange2); this->LaunchTests(daFixedRange1, caFixedRange2); this->LaunchTests(daFixedRange1, cdaFixedRange2); this->LaunchTests(caRange1, aRange2); this->LaunchTests(caRange1, daRange2); this->LaunchTests(caRange1, aFixedRange2); this->LaunchTests(caRange1, daFixedRange2); this->LaunchTests(caRange1, caRange2); this->LaunchTests(caRange1, cdaRange2); this->LaunchTests(caRange1, caFixedRange2); this->LaunchTests(caRange1, cdaFixedRange2); this->LaunchTests(cdaRange1, aRange2); this->LaunchTests(cdaRange1, daRange2); this->LaunchTests(cdaRange1, aFixedRange2); this->LaunchTests(cdaRange1, daFixedRange2); this->LaunchTests(cdaRange1, caRange2); this->LaunchTests(cdaRange1, cdaRange2); this->LaunchTests(cdaRange1, caFixedRange2); this->LaunchTests(cdaRange1, cdaFixedRange2); this->LaunchTests(caFixedRange1, aRange2); this->LaunchTests(caFixedRange1, daRange2); this->LaunchTests(caFixedRange1, aFixedRange2); this->LaunchTests(caFixedRange1, daFixedRange2); this->LaunchTests(caFixedRange1, caRange2); this->LaunchTests(caFixedRange1, cdaRange2); this->LaunchTests(caFixedRange1, caFixedRange2); this->LaunchTests(caFixedRange1, cdaFixedRange2); this->LaunchTests(cdaFixedRange1, aRange2); this->LaunchTests(cdaFixedRange1, daRange2); this->LaunchTests(cdaFixedRange1, aFixedRange2); this->LaunchTests(cdaFixedRange1, daFixedRange2); this->LaunchTests(cdaFixedRange1, caRange2); this->LaunchTests(cdaFixedRange1, cdaRange2); this->LaunchTests(cdaFixedRange1, caFixedRange2); this->LaunchTests(cdaFixedRange1, cdaFixedRange2); } template using IsConst = std::is_const; template using IsMutable = std::integral_constant::value>; template using SameValueType = std::integral_constant::value>; template using IsSwappable = std::integral_constant::value && IsMutable::value && IsMutable::value)>; template using IsNotSwappable = std::integral_constant::value>; template using EnableIfRangeIsConst = typename std::enable_if::value, T>::type; template using EnableIfRangeIsMutable = typename std::enable_if::value, T>::type; template using EnableIfSameValueType = typename std::enable_if::value, T>::type; template using EnableIfSwappable = typename std::enable_if::value, T>::type; template using EnableIfNotSwappable = typename std::enable_if::value, T>::type; // range1 is const: template void LaunchTests(Range1& r1, Range2& r2, EnableIfRangeIsConst = nullptr) { this->TestValueCompare(r1, r2); } // range1 is not const: template void LaunchTests(Range1& r1, Range2& r2, EnableIfRangeIsMutable = nullptr) { this->TestValueAssign(r1, r2); this->TestValueCompare(r1, r2); this->TestValueSwap(r1, r2); } template void TestValueAssign(Range1& r1, Range2& r2) { static_assert(IsMutable{}, "r1 must be mutable."); auto start1 = r1.begin() + 2; auto end1 = start1 + 4; auto start2 = r2.begin() + 6; auto end2 = start2 + 4; auto data1 = this->StoreRange(start1, end1); auto data2 = this->StoreRange(start2, end2); CHECK_FALSE(this->CompareRange(start1, end1, data2)); auto iter2 = start2; for (auto it = start1; it < end1; ++it) { *it = *iter2++; } CHECK_TRUE(this->CompareRange(start1, end1, data2)); CHECK_TRUE(this->CompareRange(start2, end2, data2)); this->RestoreRange(start1, end1, data1); } template void TestValueCompare(Range1& r1, Range2& r2) { auto iter1 = r1.begin() + 7; auto iter2 = r2.begin() + 7; CHECK_TRUE(*iter1 == *iter2); CHECK_FALSE(*iter1 != *iter2); CHECK_FALSE(*iter1 < *iter2); CHECK_FALSE(*iter1 > *iter2); CHECK_TRUE(*iter1 <= *iter2); CHECK_TRUE(*iter1 >= *iter2); ++iter2; CHECK_FALSE(*iter1 == *iter2); CHECK_TRUE(*iter1 != *iter2); CHECK_TRUE(*iter1 < *iter2); CHECK_FALSE(*iter1 > *iter2); CHECK_TRUE(*iter1 <= *iter2); CHECK_FALSE(*iter1 >= *iter2); iter1 += 2; CHECK_FALSE(*iter1 == *iter2); CHECK_TRUE(*iter1 != *iter2); CHECK_FALSE(*iter1 < *iter2); CHECK_TRUE(*iter1 > *iter2); CHECK_FALSE(*iter1 <= *iter2); CHECK_TRUE(*iter1 >= *iter2); } template EnableIfNotSwappable TestValueSwap(Range1&, Range2&) { // no-op, ranges aren't swappable. } template EnableIfSwappable TestValueSwap(Range1& r1, Range2& r2) { static_assert(SameValueType::value, "Mismatched value_types."); static_assert(IsMutable::value, "r1 must be mutable."); static_assert(IsMutable::value, "r2 must be mutable."); auto start1 = r1.begin() + 2; auto end1 = start1 + 4; auto start2 = r2.begin() + 6; auto end2 = start2 + 4; auto data1 = this->StoreRange(start1, end1); auto data2 = this->StoreRange(start2, end2); CHECK_TRUE(this->CompareRange(start1, end1, data1)); CHECK_TRUE(this->CompareRange(start2, end2, data2)); CHECK_TRUE(this->CompareRange(r1.begin() + 2, r1.begin() + 6, data1)); CHECK_TRUE(this->CompareRange(r2.begin() + 6, r2.begin() + 10, data2)); CHECK_FALSE(this->CompareRange(start1, end1, data2)); CHECK_FALSE(this->CompareRange(start2, end2, data1)); CHECK_FALSE(this->CompareRange(r1.begin() + 2, r1.begin() + 6, data2)); CHECK_FALSE(this->CompareRange(r2.begin() + 6, r2.begin() + 10, data1)); { auto it2 = start2; for (auto it1 = start1; it1 < end1; ++it1) { // ADL swap: using std::swap; swap(*it1, *it2++); } CHECK_TRUE(it2 == end2); } CHECK_TRUE(this->CompareRange(start1, end1, data2)); CHECK_TRUE(this->CompareRange(r1.begin() + 2, r1.begin() + 6, data2)); CHECK_TRUE(this->CompareRange(start2, end2, data1)); CHECK_TRUE(this->CompareRange(r2.begin() + 6, r2.begin() + 10, data1)); { auto it2 = start2; for (auto it1 = start1; it1 < end1; ++it1) { std::iter_swap(it1, it2++); } CHECK_TRUE(it2 == end2); } CHECK_TRUE(this->CompareRange(start1, end1, data1)); CHECK_TRUE(this->CompareRange(r1.begin() + 2, r1.begin() + 6, data1)); CHECK_TRUE(this->CompareRange(start2, end2, data2)); CHECK_TRUE(this->CompareRange(r2.begin() + 6, r2.begin() + 10, data2)); this->RestoreRange(start1, end1, data1); this->RestoreRange(start2, end2, data2); } template static auto StoreRange(IterType start, IterType end) -> std::vector::value_type> { using T = typename std::iterator_traits::value_type; return std::vector{ start, end }; } template static void RestoreRange(IterType start, IterType end, const VectorType& data) { static_assert(std::is_same::value_type, typename VectorType::value_type>::value, "Mismatched value types."); CHECK_EQUAL(data.size(), static_cast(end - start)); std::copy(data.begin(), data.end(), start); } template static bool CompareRange(IterType start, IterType end, const VectorType& data) { static_assert(std::is_convertible::value_type, typename VectorType::value_type>::value, "Mismatched value types."); return static_cast(end - start) == data.size() && std::equal(data.begin(), data.end(), start); } }; template void RunTestsForArray() { std::cerr << "ValueRangeAPI:\n"; UnitTestValueRangeAPI{}(); std::cerr << "ValueIteratorAPI:\n"; UnitTestValueIteratorAPI{}(); std::cerr << "ValueReferenceAPI:\n"; UnitTestValueReferenceAPI{}(); } // Exercise DataArrayValueRange for vtkGenericDataArray. template class MockDataArray : public vtkGenericDataArray, ValueT> { using GenericDataArrayType = vtkGenericDataArray, ValueT>; public: vtkTemplateTypeMacro(MockDataArray, GenericDataArrayType); using ValueType = typename Superclass::ValueType; static MockDataArray* New() { VTK_STANDARD_NEW_BODY(MockDataArray); } void* GetVoidPointer(vtkIdType idx) override { return this->Buffer->GetBuffer() + idx; } ValueType GetValue(vtkIdType valueIdx) const { return this->Buffer->GetBuffer()[valueIdx]; } void SetValue(vtkIdType valueIdx, ValueType value) { this->Buffer->GetBuffer()[valueIdx] = value; } void GetTypedTuple(vtkIdType tupleIdx, ValueType* tuple) const { const vtkIdType valueIdx = tupleIdx * this->NumberOfComponents; std::copy(this->Buffer->GetBuffer() + valueIdx, this->Buffer->GetBuffer() + valueIdx + this->NumberOfComponents, tuple); } void SetTypedTuple(vtkIdType tupleIdx, const ValueType* tuple) { const vtkIdType valueIdx = tupleIdx * this->NumberOfComponents; std::copy(tuple, tuple + this->NumberOfComponents, this->Buffer->GetBuffer() + valueIdx); } ValueType GetTypedComponent(vtkIdType tupleIdx, int compIdx) const { return this->Buffer->GetBuffer()[this->NumberOfComponents * tupleIdx + compIdx]; } void SetTypedComponent(vtkIdType tupleIdx, int compIdx, ValueType value) { const vtkIdType valueIdx = tupleIdx * this->NumberOfComponents + compIdx; this->SetValue(valueIdx, value); } protected: vtkNew> Buffer; bool AllocateTuples(vtkIdType numTuples) { vtkIdType numValues = numTuples * this->GetNumberOfComponents(); if (this->Buffer->Allocate(numValues)) { this->Size = this->Buffer->GetSize(); return true; } return false; } bool ReallocateTuples(vtkIdType numTuples) { if (this->Buffer->Reallocate(numTuples * this->GetNumberOfComponents())) { this->Size = this->Buffer->GetSize(); return true; } return false; } friend class vtkGenericDataArray, ValueT>; }; } // end anon namespace int TestDataArrayValueRange(int, char*[]) { std::cerr << "AOS:\n"; RunTestsForArray>(); std::cerr << "SOA:\n"; RunTestsForArray>(); std::cerr << "ScaleSOA:\n"; RunTestsForArray>(); std::cerr << "vtkFloatArray:\n"; RunTestsForArray(); std::cerr << "MockDataArray:\n"; RunTestsForArray, /*ForceValueTypeForVtkDataArray=*/vtkTypeInt32>(); std::cerr << "\nEdgeCases:\n"; UnitTestEdgeCases{}(); return NumErrors != 0; }