// 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 "vtkDebugRangeIterators.h" #include "vtkFloatArray.h" #include "vtkSOADataArrayTemplate.h" #include "vtkScaledSOADataArrayTemplate.h" #include "vtkVector.h" #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) \ 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 destructable at " LOCATION()) #define LOG_ERROR(message) \ ++NumErrors; \ std::cerr << NumErrors << ": " << message << "\n" #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 FillTupleRangeIota(Range range) { using ComponentType = typename Range::ComponentType; ComponentType value{ 1 }; for (typename Range::reference tuple : range) { std::iota(tuple.begin(), tuple.end(), value); value += static_cast(tuple.size()); } } template void TestIota(Range range) { using ComponentType = typename Range::ComponentType; auto numComps = range.GetTupleSize(); auto beginTuple = range.GetBeginTupleId(); auto endTuple = range.GetEndTupleId(); auto startValue = static_cast(beginTuple * numComps) + 1; auto endValue = static_cast(endTuple * numComps) + 1; auto value = startValue; for (typename Range::const_reference tuple : range) { for (ComponentType comp : tuple) { CHECK_EQUAL(value, comp); ++value; } } CHECK_EQUAL(value, endValue); } //============================================================================== //============================================================================== // TupleRange: //============================================================================== //============================================================================== template struct UnitTestTupleRangeAPI { static constexpr vtk::ComponentIdType NumComps = 3; static constexpr vtk::TupleIdType NumTuples = 12; void operator()() { vtkNew array; auto da = static_cast(array); array->SetNumberOfComponents(NumComps); this->TestEmptyRange(vtk::DataArrayTupleRange(array)); this->TestEmptyRange(vtk::DataArrayTupleRange(da)); this->TestEmptyRange(vtk::DataArrayTupleRange(array)); this->TestEmptyRange(vtk::DataArrayTupleRange(da)); array->SetNumberOfTuples(this->NumTuples); this->TestEmptyRange(vtk::DataArrayTupleRange(array, 4, 4)); this->TestEmptyRange(vtk::DataArrayTupleRange(da, 4, 4)); this->TestEmptyRange(vtk::DataArrayTupleRange(array, 4, 4)); this->TestEmptyRange(vtk::DataArrayTupleRange(da, 4, 4)); FillTupleRangeIota(vtk::DataArrayTupleRange(array)); auto pStart = NumTuples / 4; auto pEnd = NumTuples / 4 * 3; { // Full, dynamic-size, real typed range auto range = vtk::DataArrayTupleRange(array); DispatchRangeTests(range, array, 0, NumTuples); } { // Full, dynamic-size, generic-typed range auto range = vtk::DataArrayTupleRange(da); DispatchRangeTests(range, array, 0, NumTuples); } { // Full, fixed-size, real typed range auto range = vtk::DataArrayTupleRange(array); DispatchRangeTests(range, array, 0, NumTuples); } { // Full, fixed-size, generic-typed range auto range = vtk::DataArrayTupleRange(da); DispatchRangeTests(range, array, 0, NumTuples); } { // Partial, dynamic-size, real typed range auto range = vtk::DataArrayTupleRange(array, pStart, pEnd); DispatchRangeTests(range, array, pStart, pEnd); } { // Partial, dynamic-size, generic-typed range auto range = vtk::DataArrayTupleRange(da, pStart, pEnd); DispatchRangeTests(range, array, pStart, pEnd); } { // Partial, fixed-size, real typed range auto range = vtk::DataArrayTupleRange(array, pStart, pEnd); DispatchRangeTests(range, array, pStart, pEnd); } { // Partial, fixed-size, generic-typed range auto range = vtk::DataArrayTupleRange(da, pStart, pEnd); DispatchRangeTests(range, array, pStart, pEnd); } } template void TestEmptyRange(Range range) { for (typename Range::const_reference tuple : range) { for (typename Range::ComponentType comp : tuple) { (void)comp; CHECK_TRUE(false && "This should not execute."); } } } template void DispatchRangeTests( Range range, RangeArrayType* array, vtk::TupleIdType start, vtk::TupleIdType 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::TupleIdType start, vtk::TupleIdType end) { TestTypes(range); CHECK_EQUAL(range.GetArray(), array); CHECK_EQUAL(range.GetTupleSize(), NumComps); CHECK_EQUAL(range.GetBeginTupleId(), start); CHECK_EQUAL(range.GetEndTupleId(), 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; (void)range; // decltype doesn't actually use the value. CHECK_TYPEDEF( typename Range::TupleIteratorType, decltype(std::declval().begin())); CHECK_TYPEDEF( typename Range::ConstTupleIteratorType, decltype(std::declval().cbegin())); CHECK_TYPEDEF( typename Range::ConstTupleIteratorType, decltype(std::declval().begin())); CHECK_TYPEDEF( typename Range::ConstTupleIteratorType, decltype(std::declval().cbegin())); CHECK_TYPEDEF( typename Range::TupleReferenceType, decltype(*std::declval().begin())); CHECK_TYPEDEF(typename Range::TupleReferenceType, decltype(std::declval()[0])); CHECK_TYPEDEF( typename Range::ConstTupleReferenceType, decltype(*std::declval().cbegin())); CHECK_TYPEDEF(typename Range::ConstTupleReferenceType, decltype(std::declval()[0])); CHECK_TYPEDEF(typename Range::ComponentIteratorType, decltype(std::declval().begin()->begin())); CHECK_TYPEDEF(typename Range::ConstComponentIteratorType, decltype(std::declval().cbegin()->begin())); CHECK_TYPEDEF(typename Range::ConstComponentIteratorType, decltype(std::declval().begin()->cbegin())); CHECK_TYPEDEF(typename Range::ComponentReferenceType, decltype(*std::declval().begin()->begin())); CHECK_TYPEDEF( typename Range::ComponentReferenceType, decltype(std::declval()[0][0])); CHECK_TYPEDEF(typename Range::ConstComponentReferenceType, decltype(*std::declval().cbegin()->begin())); CHECK_TYPEDEF(typename Range::ConstComponentReferenceType, decltype(*std::declval().begin()->cbegin())); CHECK_TYPEDEF( typename Range::ConstComponentReferenceType, decltype(std::declval()[0][0])); CHECK_IS_BASE_TYPE_OF(typename Range::ArrayType, RangeArrayType); CHECK_TYPEDEF(typename Range::ComponentType, vtk::GetAPIType); CHECK_TYPEDEF(typename Range::size_type, vtk::TupleIdType); CHECK_TYPEDEF(typename Range::size_type, decltype(range.size())); CHECK_TYPEDEF(typename Range::reference, decltype(*std::declval())); CHECK_TYPEDEF( typename Range::const_reference, decltype(*std::declval())); CHECK_TYPEDEF(typename Range::reference, decltype(std::declval()[0])); CHECK_TYPEDEF(typename Range::const_reference, decltype(std::declval()[0])); 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::ArrayType, decltype(*range.GetArray())); CHECK_TYPEDEF(vtk::TupleIdType, decltype(range.GetBeginTupleId())); CHECK_TYPEDEF(vtk::TupleIdType, decltype(range.GetEndTupleId())); static_assert(Range::TupleSizeTag == RangeTupleSize, "Range::TupleSizeTag incorrect."); } template void TestSubRange(Range& range) { auto range1 = range.GetSubRange(3, 9); CHECK_EQUAL(range1.GetBeginTupleId(), range.GetBeginTupleId() + 3); CHECK_EQUAL(range1.GetEndTupleId(), range.GetBeginTupleId() + 9); { auto subRange = range1.GetSubRange(); CHECK_EQUAL(subRange.GetBeginTupleId(), range1.GetBeginTupleId()); CHECK_EQUAL(subRange.GetEndTupleId(), range1.GetEndTupleId()); } { auto subRange = range1.GetSubRange(2, 4); CHECK_EQUAL(subRange.GetBeginTupleId(), range1.GetBeginTupleId() + 2); CHECK_EQUAL(subRange.GetEndTupleId(), range1.GetBeginTupleId() + 4); } { auto subRange = range1.GetSubRange(1); CHECK_EQUAL(subRange.GetBeginTupleId(), range1.GetBeginTupleId() + 1); CHECK_EQUAL(subRange.GetEndTupleId(), range1.GetEndTupleId()); } { auto subRange = range1.GetSubRange(0, 5); CHECK_EQUAL(subRange.GetBeginTupleId(), range1.GetBeginTupleId()); CHECK_EQUAL(subRange.GetEndTupleId(), range1.GetBeginTupleId() + 5); } { auto subRange = range1.GetSubRange(0, 0); CHECK_EQUAL(subRange.GetBeginTupleId(), range1.GetBeginTupleId()); CHECK_EQUAL(subRange.GetEndTupleId(), range1.GetBeginTupleId()); } } template void TestDefaultInit(Range& range) { auto range1 = Range{}; range1 = range; (void)range; } }; //============================================================================== //============================================================================== // TupleIterators: //============================================================================== //============================================================================== template struct UnitTestTupleIteratorAPI { static constexpr vtk::ComponentIdType NumComps = 3; static constexpr vtk::TupleIdType NumTuples = 12; void operator()() { vtkNew array; array->SetNumberOfComponents(NumComps); array->SetNumberOfTuples(NumTuples); FillTupleRangeIota(vtk::DataArrayTupleRange(array)); auto da = static_cast(array); { // Full, dynamic-size, real typed range auto range = vtk::DataArrayTupleRange(array); DispatchRangeTests(range); } { // Full, dynamic-size, generic-typed range auto range = vtk::DataArrayTupleRange(da); DispatchRangeTests(range); } { // Full, fixed-size, real typed range auto range = vtk::DataArrayTupleRange(array); DispatchRangeTests(range); } { // Full, fixed-size, generic-typed range auto range = vtk::DataArrayTupleRange(da); DispatchRangeTests(range); } } template void DispatchRangeTests(Range range) { { TestTupleIterator(range); TestConstTupleIterator(range); } { const Range& crange = range; TestConstTupleIterator(crange); } } template void TestTupleIterator(Range& range) { TestConstCopy(range); TestConstAssign(range); TestIterSwap(range); } template void TestConstTupleIterator(Range& range) { TestTypes(range); TestCopy(range); TestAssign(range); TestTraversal(range); TestIndexing(range); TestDeref(range); TestComparison(range); TestSwap(range); } template void TestTypes(Range& range) { using Iter = decltype(range.begin()); (void)range; // decltype doesn't actually use values CHECK_TYPEDEF(typename Iter::iterator_category, std::random_access_iterator_tag); CHECK_TYPEDEF(typename Iter::reference, decltype(*std::declval())); CHECK_TYPEDEF(typename Iter::reference, decltype(std::declval()[0])); CHECK_TYPEDEF(typename Iter::pointer, decltype(std::declval().operator->())); CHECK_TYPEDEF( typename Iter::difference_type, decltype(std::declval() - std::declval())); // Check requirements of random-access iterators: CHECK_ITER_TYPE(Iter); } 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 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 TestCopy(Range& range) { auto iter = this->GetTestingIter(range); using IterType = decltype(iter); IterType iter2(iter); CHECK_EQUAL_NODUMP(iter, iter2); CHECK_EQUAL_NODUMP(*iter, *iter2); } template void TestAssign(Range& range) { auto iter = this->GetTestingIter(range); using IterType = decltype(iter); IterType iter2 = range.begin(); CHECK_NOT_EQUAL_NODUMP(iter, iter2); CHECK_NOT_EQUAL_NODUMP(*iter, *iter2); IterType iter3 = iter2 = iter; CHECK_EQUAL_NODUMP(iter, iter2); CHECK_EQUAL_NODUMP(*iter, *iter2); CHECK_EQUAL_NODUMP(iter, iter3); CHECK_EQUAL_NODUMP(*iter, *iter3); } 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); auto iter2 = iter1; iter2 += 2; CHECK_NOT_EQUAL_NODUMP(iter1, iter2); CHECK_EQUAL(iter2 - iter1, 2); } { // operator -= auto iter1 = this->GetTestingIter(range); auto iter2 = iter1; iter2 -= 2; CHECK_NOT_EQUAL_NODUMP(iter1, iter2); CHECK_EQUAL(iter2 - iter1, -2); } { // operator + (it, off) auto iter1 = this->GetTestingIter(range); auto iter2 = iter1 + 2; CHECK_NOT_EQUAL_NODUMP(iter1, iter2); CHECK_EQUAL(iter2 - iter1, 2); } { // operator + (off, it) auto iter1 = this->GetTestingIter(range); auto iter2 = 2 + iter1; CHECK_NOT_EQUAL_NODUMP(iter1, iter2); CHECK_EQUAL(iter2 - iter1, 2); } { // operator - (it, off) auto iter1 = this->GetTestingIter(range); 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); iter2 += 3; CHECK_EQUAL(iter2 - iter1, 2); } } template void TestIndexing(Range& range) { auto beginIter = range.begin(); // Always points to the first tuple auto lagIter = beginIter; // Points to the i-1 tuple using IndexT = typename Range::size_type; IndexT i = 0; for (auto tupleRef : range) { if (i == 0) { CHECK_EQUAL_NODUMP(tupleRef, lagIter[0]); } else { CHECK_EQUAL_NODUMP(tupleRef, lagIter[1]); ++lagIter; } CHECK_EQUAL_NODUMP(tupleRef, beginIter[i]); ++i; } } template void TestDeref(Range& range) { { // operator* auto iter = this->GetTestingIter(range); auto iter2 = iter; CHECK_EQUAL_NODUMP((*iter), (*iter2)); ++iter2; CHECK_NOT_EQUAL_NODUMP((*iter), (*iter2)); } { // operator-> auto iter = this->GetTestingIter(range); auto iter2 = iter; CHECK_EQUAL_NODUMP(iter->begin(), iter2->begin()); CHECK_EQUAL_NODUMP(*iter, *iter2); ++iter2; CHECK_NOT_EQUAL_NODUMP(*iter, *iter2); } } 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 TestSwap(Range& range) { auto iter1 = this->GetTestingIter(range); auto iter2 = iter1 + 1; auto iterA = iter1; auto iterB = iter2; CHECK_NOT_EQUAL_NODUMP(iterA, iterB); CHECK_EQUAL_NODUMP(iterA, iter1); CHECK_EQUAL_NODUMP(iterB, iter2); { // ADL swap: using std::swap; swap(iterA, iterB); } CHECK_NOT_EQUAL_NODUMP(iterA, iter1); CHECK_NOT_EQUAL_NODUMP(iterB, iter2); CHECK_EQUAL_NODUMP(iterA, iter2); CHECK_EQUAL_NODUMP(iterB, iter1); } template void TestIterSwap(Range& range) { using ComponentType = typename Range::ComponentType; auto iter1 = this->GetTestingIter(range); auto iter2 = iter1 + 1; CHECK_FALSE(std::equal(iter1->begin(), iter1->end(), iter2->begin())); std::vector tuple1{ iter1->begin(), iter1->end() }; std::vector tuple2{ iter2->begin(), iter2->end() }; CHECK_TRUE(std::equal(iter1->begin(), iter1->end(), tuple1.begin())); CHECK_TRUE(std::equal(iter2->begin(), iter2->end(), tuple2.begin())); std::iter_swap(iter1, iter2); CHECK_TRUE(std::equal(iter1->begin(), iter1->end(), tuple2.begin())); CHECK_TRUE(std::equal(iter2->begin(), iter2->end(), tuple1.begin())); // Put things back how we found them: std::iter_swap(iter1, iter2); CHECK_TRUE(std::equal(iter1->begin(), iter1->end(), tuple1.begin())); CHECK_TRUE(std::equal(iter2->begin(), iter2->end(), tuple2.begin())); } // Guaranteed to be valid and surrounded by at least 2 other valid iterators // in each direction. template static auto GetTestingIter(Range& range) -> decltype(range.begin()) { return range.begin() + (range.size() / 2); } }; //============================================================================== //============================================================================== // TupleReference: //============================================================================== //============================================================================== template struct UnitTestTupleReferenceAPI { static constexpr vtk::ComponentIdType NumComps = 3; static constexpr vtk::TupleIdType NumTuples = 12; void operator()() { vtkNew array; array->SetNumberOfComponents(NumComps); array->SetNumberOfTuples(NumTuples); FillTupleRangeIota(vtk::DataArrayTupleRange(array)); auto da = static_cast(array); { // Full, dynamic-size, real typed range auto range = vtk::DataArrayTupleRange(array); DispatchRangeTests(range); } { // Full, dynamic-size, generic-typed range auto range = vtk::DataArrayTupleRange(da); DispatchRangeTests(range); } { // Full, fixed-size, real typed range auto range = vtk::DataArrayTupleRange(array); DispatchRangeTests(range); } { // Full, fixed-size, generic-typed range auto range = vtk::DataArrayTupleRange(da); DispatchRangeTests(range); } } template void DispatchRangeTests(Range range) { { TestTupleReference(range); TestConstTupleReference(range); TestMutableTypes(range); // This test is ONLY for non-const types. } { const Range& crange = range; TestConstTupleReference(crange); TestConstTypes(crange); // This test is ONLY for const types. } } template void TestTupleReference(Range& range) { TestConstCopy(range); TestConstAssign(range); TestAssign(range); TestIndexing(range); TestSwap(range); TestFill(range); TestIters(range); TestArrayAccess(range); } template void TestConstTupleReference(Range& range) { TestTypes(range); TestComparison(range); TestIndexingConst(range); TestSize(range); TestItersConst(range); TestArrayAccessConst(range); } template void TestTypes(Range& range) { using RangeArrayType = typename Range::ArrayType; using Ref = typename std::decayGetTestingIterator(range))>::type; (void)range; // decltype doesn't actually use values CHECK_TYPEDEF(vtk::GetAPIType, typename Ref::value_type); CHECK_TYPEDEF(typename Ref::size_type, vtk::ComponentIdType); CHECK_TYPEDEF(typename Ref::iterator, decltype(std::declval().begin())); CHECK_TYPEDEF(typename Ref::iterator, decltype(std::declval().end())); CHECK_TYPEDEF(typename Ref::const_iterator, decltype(std::declval().cbegin())); CHECK_TYPEDEF(typename Ref::const_iterator, decltype(std::declval().cend())); CHECK_TYPEDEF(typename Ref::size_type, decltype(std::declval().size())); } template void TestMutableTypes(Range& range) { using Ref = typename std::decayGetTestingIterator(range))>::type; using MutableRef = typename std::remove_const::type; using ConstRef = typename std::add_const::type; (void)range; // decltype doesn't actually use values CHECK_TYPEDEF(typename Ref::reference, decltype(std::declval()[0])); CHECK_TYPEDEF(typename Ref::const_reference, decltype(std::declval()[0])); } template void TestConstTypes(Range& range) { using Ref = typename std::decayGetTestingIterator(range))>::type; (void)range; // decltype doesn't actually use values CHECK_TYPEDEF(typename Ref::const_reference, decltype(std::declval()[0])); } 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::reference ref{ range[0] }; typename Range::const_reference cref{ ref }; CHECK_EQUAL_NODUMP(ref, cref); } 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 iterators to // const ones: typename Range::reference ref{ range[0] }; typename Range::const_reference cref{ range[1] }; auto data = this->StoreTuple(ref); ref = cref; CHECK_EQUAL_NODUMP(ref, cref); this->RestoreTuple(ref, data); } template void TestAssign(Range& range) { typename Range::reference tuple1 = range[0]; typename Range::reference tuple2 = range[1]; const typename Range::const_reference tuple3 = range[2]; auto data1 = this->StoreTuple(tuple1); auto data2 = this->StoreTuple(tuple2); auto data3 = this->StoreTuple(tuple3); CHECK_TRUE(this->CompareTuple(tuple1, data1)); CHECK_TRUE(this->CompareTuple(tuple2, data2)); CHECK_TRUE(this->CompareTuple(tuple3, data3)); CHECK_FALSE(tuple1 == tuple2); CHECK_FALSE(tuple2 == tuple3); CHECK_FALSE(tuple1 == tuple3); tuple1 = tuple2 = tuple3; CHECK_TRUE(tuple1 == tuple2); CHECK_TRUE(tuple2 == tuple3); CHECK_TRUE(tuple1 == tuple3); CHECK_TRUE(this->CompareTuple(tuple1, data3)); CHECK_TRUE(this->CompareTuple(tuple2, data3)); CHECK_TRUE(this->CompareTuple(tuple3, data3)); this->RestoreTuple(tuple1, data1); this->RestoreTuple(tuple2, data2); CHECK_TRUE(this->CompareTuple(tuple1, data1)); CHECK_TRUE(this->CompareTuple(tuple2, data2)); CHECK_TRUE(this->CompareTuple(tuple3, data3)); } template void TestSwap(Range& range) { typename Range::reference tuple1 = range[0]; typename Range::reference tuple2 = range[1]; auto data1 = this->StoreTuple(tuple1); auto data2 = this->StoreTuple(tuple2); CHECK_TRUE(this->CompareTuple(tuple1, data1)); CHECK_TRUE(this->CompareTuple(tuple2, data2)); CHECK_FALSE(this->CompareTuple(tuple1, data2)); CHECK_FALSE(this->CompareTuple(tuple2, data1)); tuple1.swap(tuple2); CHECK_FALSE(this->CompareTuple(tuple1, data1)); CHECK_FALSE(this->CompareTuple(tuple2, data2)); CHECK_TRUE(this->CompareTuple(tuple1, data2)); CHECK_TRUE(this->CompareTuple(tuple2, data1)); tuple2.swap(tuple1); CHECK_TRUE(this->CompareTuple(tuple1, data1)); CHECK_TRUE(this->CompareTuple(tuple2, data2)); CHECK_FALSE(this->CompareTuple(tuple1, data2)); CHECK_FALSE(this->CompareTuple(tuple2, data1)); { // ADL swap: using std::swap; swap(tuple1, tuple2); } CHECK_FALSE(this->CompareTuple(tuple1, data1)); CHECK_FALSE(this->CompareTuple(tuple2, data2)); CHECK_TRUE(this->CompareTuple(tuple1, data2)); CHECK_TRUE(this->CompareTuple(tuple2, data1)); { // ADL swap: using std::swap; swap(tuple1, tuple2); } CHECK_TRUE(this->CompareTuple(tuple1, data1)); CHECK_TRUE(this->CompareTuple(tuple2, data2)); CHECK_FALSE(this->CompareTuple(tuple1, data2)); CHECK_FALSE(this->CompareTuple(tuple2, data1)); this->RestoreTuple(tuple1, data1); this->RestoreTuple(tuple2, data2); CHECK_TRUE(this->CompareTuple(tuple1, data1)); CHECK_TRUE(this->CompareTuple(tuple2, data2)); } template void TestFill(Range& range) { using ComponentType = typename Range::ComponentType; auto iter = this->GetTestingIterator(range); typename Range::reference tuple = *iter; auto data = this->StoreTuple(tuple); ComponentType fillValue = static_cast(0); auto isFilled = [&](ComponentType val) -> bool { return val == fillValue; }; do { tuple.fill(fillValue); CHECK_TRUE(std::all_of(tuple.begin(), tuple.end(), isFilled)); ++fillValue; } while (fillValue < 32); this->RestoreTuple(tuple, data); } template void TestComparison(Range& range) { auto iter1a = this->GetTestingIterator(range, 0); auto iter1b = this->GetTestingIterator(range, 0); auto iter2 = this->GetTestingIterator(range, 1); auto tuple1a = *iter1a; auto tuple1b = *iter1b; auto tuple2 = *iter2; CHECK_EQUAL_NODUMP(tuple1a, tuple1b); CHECK_EQUAL_NODUMP(tuple1b, tuple1a); CHECK_NOT_EQUAL_NODUMP(tuple1a, tuple2); CHECK_NOT_EQUAL_NODUMP(tuple1b, tuple2); CHECK_NOT_EQUAL_NODUMP(tuple2, tuple1a); CHECK_NOT_EQUAL_NODUMP(tuple2, tuple1b); } template void TestIndexing(Range& range) { // NOTE Since these are proxy iterators, they don't play nicely with // auto/auto&. Similar to vector, the actual value types may not // exist in memory -- when the ArrayType is vtkDataArray, the doubles // returns are usually cast from the underlying ValueTypes on the fly. // // This means we must explicitly use the value_type, reference or // const_reference typedefs on containers, or // std::iterator_traits::value_type / // std::iterator_traits::reference on iterators when calling // operator[]. using ConstRange = typename std::add_const::type; auto iter = this->GetTestingIterator(range, 0); auto citer = this->GetTestingIterator(const_cast(range), 1); auto tuple = *iter; auto ctuple = *citer; auto data1 = this->StoreTuple(tuple); using TupleT = decltype(tuple); using CTupleT = decltype(ctuple); using TIndex = typename TupleT::size_type; CHECK_NOT_EQUAL(tuple.size(), 0); CHECK_EQUAL(tuple.size(), ctuple.size()); { // Read non-const: TIndex i = 0; for (typename TupleT::reference ref : tuple) { CHECK_EQUAL(tuple[i++], ref); } CHECK_EQUAL(i, tuple.size()); i = 0; for (typename TupleT::const_reference cref : tuple) { CHECK_EQUAL(tuple[i++], cref); } CHECK_EQUAL(i, tuple.size()); i = 0; for (typename TupleT::value_type val : tuple) { CHECK_EQUAL(tuple[i++], val); } CHECK_EQUAL(i, tuple.size()); } { // Read const: TIndex i = 0; for (typename CTupleT::const_reference cref : ctuple) { CHECK_EQUAL(ctuple[i++], cref); } CHECK_EQUAL(i, ctuple.size()); i = 0; for (typename CTupleT::value_type val : ctuple) { CHECK_EQUAL(ctuple[i++], val); } CHECK_EQUAL(i, ctuple.size()); } { // Write: TIndex i = 0; for (typename TupleT::const_reference cref : ctuple) { tuple[i++] = cref; } CHECK_EQUAL(i, ctuple.size()); CHECK_EQUAL_NODUMP(tuple, ctuple); this->RestoreTuple(tuple, data1); } this->RestoreTuple(tuple, data1); } template void TestIndexingConst(Range& range) { // NOTE Since these are proxy iterators, they don't play nicely with // auto/auto&. Similar to vector, the actual value types may not // exist in memory -- when the ArrayType is vtkDataArray, the doubles // returns are usually cast from the underlying ValueTypes on the fly. // // This means we must explicitly use the value_type, reference or // const_reference typedefs on containers, or // std::iterator_traits::value_type / // std::iterator_traits::reference on iterators when calling // operator[]. auto iter = this->GetTestingIterator(range, 0); auto tuple = *iter; using TupleT = decltype(tuple); using TIndex = typename TupleT::size_type; CHECK_NOT_EQUAL(tuple.size(), 0); { // Read: TIndex i = 0; for (typename TupleT::const_reference cref : tuple) { CHECK_EQUAL(tuple[i++], cref); } CHECK_EQUAL(i, tuple.size()); i = 0; for (typename TupleT::value_type val : tuple) { CHECK_EQUAL(tuple[i++], val); } CHECK_EQUAL(i, tuple.size()); } } template void TestSize(Range& range) { CHECK_EQUAL(range.size(), range.end() - range.begin()); } template void TestIters(Range& range) { using ComponentType = typename Range::ComponentType; auto iter1 = this->GetTestingIterator(range, 0); auto iter2 = this->GetTestingIterator(range, 1); typename Range::reference tuple1 = *iter1; const typename Range::const_reference tuple2 = *iter2; auto data1 = this->StoreTuple(tuple1); const ComponentType startValue1 = *tuple1.begin(); const ComponentType startValue2 = *tuple2.begin(); const ComponentType endValue1 = startValue1 + range.GetTupleSize(); const ComponentType endValue2 = startValue2 + range.GetTupleSize(); using TupleT = typename std::decay::type; using CTupleT = typename std::decay::type; typename TupleT::iterator b1 = tuple1.begin(); typename TupleT::iterator e1 = tuple1.end(); typename TupleT::const_iterator b1c = tuple1.cbegin(); typename TupleT::const_iterator e1c = tuple1.cend(); typename CTupleT::const_iterator b2 = tuple2.begin(); typename CTupleT::const_iterator e2 = tuple2.end(); typename CTupleT::const_iterator b2c = tuple2.cbegin(); typename CTupleT::const_iterator e2c = tuple2.cend(); CHECK_NOT_EQUAL(tuple1.size(), 0); CHECK_EQUAL(tuple1.size(), tuple2.size()); CHECK_EQUAL(tuple1.size(), e1 - b1); CHECK_EQUAL(tuple1.size(), e1c - b1c); CHECK_EQUAL(tuple2.size(), e2 - b2); CHECK_EQUAL(tuple2.size(), e2c - b2c); // Read: { ComponentType value = startValue1; for (auto it = b1; it != e1; ++it) { CHECK_EQUAL(*it, value); ++value; } CHECK_EQUAL(value, endValue1); } { ComponentType value = startValue1; for (auto it = b1c; it != e1c; ++it) { CHECK_EQUAL(*it, value); ++value; } CHECK_EQUAL(value, endValue1); } { ComponentType value = startValue2; for (auto it = b2; it != e2; ++it) { CHECK_EQUAL(*it, value); ++value; } CHECK_EQUAL(value, endValue2); } { ComponentType value = startValue2; for (auto it = b2c; it != e2c; ++it) { CHECK_EQUAL(*it, value); ++value; } CHECK_EQUAL(value, endValue2); } // Write: { auto in = b2; auto out = b1; while (in < e2 && out < e1) { *out++ = *in++; } CHECK_EQUAL_NODUMP(tuple1, tuple2); this->RestoreTuple(tuple1, data1); } { auto in = b2c; auto out = b1; while (in < e2c && out < e1) { *out++ = *in++; } CHECK_EQUAL_NODUMP(tuple1, tuple2); this->RestoreTuple(tuple1, data1); } this->RestoreTuple(tuple1, data1); } template void TestItersConst(Range& range) { using ComponentType = typename Range::ComponentType; auto iter1 = this->GetTestingIterator(range, 0); auto iter2 = this->GetTestingIterator(range, 1); typename Range::const_reference tuple1 = *iter1; const typename Range::const_reference tuple2 = *iter2; const ComponentType startValue1 = *tuple1.begin(); const ComponentType startValue2 = *tuple2.begin(); const ComponentType endValue1 = startValue1 + range.GetTupleSize(); const ComponentType endValue2 = startValue2 + range.GetTupleSize(); using TupleT = typename std::decay::type; using CTupleT = typename std::decay::type; typename TupleT::iterator b1 = tuple1.begin(); typename TupleT::iterator e1 = tuple1.end(); typename TupleT::const_iterator b1c = tuple1.cbegin(); typename TupleT::const_iterator e1c = tuple1.cend(); typename CTupleT::const_iterator b2 = tuple2.begin(); typename CTupleT::const_iterator e2 = tuple2.end(); typename CTupleT::const_iterator b2c = tuple2.cbegin(); typename CTupleT::const_iterator e2c = tuple2.cend(); CHECK_NOT_EQUAL(tuple1.size(), 0); CHECK_EQUAL(tuple1.size(), tuple2.size()); CHECK_EQUAL(tuple1.size(), e1 - b1); CHECK_EQUAL(tuple1.size(), e1c - b1c); CHECK_EQUAL(tuple2.size(), e2 - b2); CHECK_EQUAL(tuple2.size(), e2c - b2c); // Read: { ComponentType value = startValue1; for (auto it = b1; it != e1; ++it) { CHECK_EQUAL(*it, value); ++value; } CHECK_EQUAL(value, endValue1); } { ComponentType value = startValue1; for (auto it = b1c; it != e1c; ++it) { CHECK_EQUAL(*it, value); ++value; } CHECK_EQUAL(value, endValue1); } { ComponentType value = startValue2; for (auto it = b2; it != e2; ++it) { CHECK_EQUAL(*it, value); ++value; } CHECK_EQUAL(value, endValue2); } { ComponentType value = startValue2; for (auto it = b2c; it != e2c; ++it) { CHECK_EQUAL(*it, value); ++value; } CHECK_EQUAL(value, endValue2); } } template void TestArrayAccess(Range& range) { auto iter1 = this->GetTestingIterator(range, 0); auto iter2 = this->GetTestingIterator(range, 1); auto iter3 = this->GetTestingIterator(range, 2); typename Range::reference tuple1Ref = *iter1; typename Range::reference tuple2Ref = *iter2; const typename Range::const_reference tuple3Ref = *iter3; auto d1 = this->StoreTuple(tuple1Ref); auto d2 = this->StoreTuple(tuple2Ref); auto d3 = this->StoreTuple(tuple3Ref); using VectorType = vtkVector; VectorType v1; VectorType v2; VectorType v3; tuple1Ref.GetTuple(v1.GetData()); tuple2Ref.GetTuple(v2.GetData()); tuple3Ref.GetTuple(v3.GetData()); CHECK_TRUE(this->CompareTuple(v1.GetData(), v1.GetData() + NumComps, d1)); CHECK_TRUE(this->CompareTuple(v2.GetData(), v2.GetData() + NumComps, d2)); CHECK_TRUE(this->CompareTuple(v3.GetData(), v3.GetData() + NumComps, d3)); CHECK_FALSE(v1 == v2); CHECK_FALSE(v2 == v3); CHECK_FALSE(v1 == v3); v1 = v2 = v3; // TupleValues are equal: CHECK_TRUE(v1 == v2); CHECK_TRUE(v2 == v3); CHECK_TRUE(v1 == v3); CHECK_TRUE(this->CompareTuple(v1.GetData(), v1.GetData() + NumComps, d3)); CHECK_TRUE(this->CompareTuple(v2.GetData(), v2.GetData() + NumComps, d3)); CHECK_TRUE(this->CompareTuple(v3.GetData(), v3.GetData() + NumComps, d3)); // But references are not CHECK_TRUE(this->CompareTuple(tuple1Ref, d1)); CHECK_TRUE(this->CompareTuple(tuple2Ref, d2)); CHECK_TRUE(this->CompareTuple(tuple3Ref, d3)); // Write a tuple to the references to modify the array: tuple1Ref.SetTuple(v3.GetData()); tuple2Ref.SetTuple(v3.GetData()); // References are now equal CHECK_TRUE(this->CompareTuple(tuple1Ref, d3)); CHECK_TRUE(this->CompareTuple(tuple2Ref, d3)); CHECK_TRUE(this->CompareTuple(tuple3Ref, d3)); this->RestoreTuple(tuple1Ref, d1); this->RestoreTuple(tuple2Ref, d2); CHECK_TRUE(this->CompareTuple(tuple1Ref, d1)); CHECK_TRUE(this->CompareTuple(tuple2Ref, d2)); CHECK_TRUE(this->CompareTuple(tuple3Ref, d3)); } template void TestArrayAccessConst(Range& range) { auto iter1 = this->GetTestingIterator(range, 0); auto iter2 = this->GetTestingIterator(range, 1); auto iter3 = this->GetTestingIterator(range, 2); typename Range::const_reference tuple1Ref = *iter1; typename Range::const_reference tuple2Ref = *iter2; const typename Range::const_reference tuple3Ref = *iter3; auto d1 = this->StoreTuple(tuple1Ref); auto d2 = this->StoreTuple(tuple2Ref); auto d3 = this->StoreTuple(tuple3Ref); using VectorType = vtkVector; VectorType v1; VectorType v2; VectorType v3; tuple1Ref.GetTuple(v1.GetData()); tuple2Ref.GetTuple(v2.GetData()); tuple3Ref.GetTuple(v3.GetData()); CHECK_TRUE(this->CompareTuple(v1.GetData(), v1.GetData() + NumComps, d1)); CHECK_TRUE(this->CompareTuple(v2.GetData(), v2.GetData() + NumComps, d2)); CHECK_TRUE(this->CompareTuple(v3.GetData(), v3.GetData() + NumComps, d3)); CHECK_FALSE(v1 == v2); CHECK_FALSE(v2 == v3); CHECK_FALSE(v1 == v3); v1 = v2 = v3; // TupleValues are equal: CHECK_TRUE(v1 == v2); CHECK_TRUE(v2 == v3); CHECK_TRUE(v1 == v3); CHECK_TRUE(this->CompareTuple(v1.GetData(), v1.GetData() + NumComps, d3)); CHECK_TRUE(this->CompareTuple(v2.GetData(), v2.GetData() + NumComps, d3)); CHECK_TRUE(this->CompareTuple(v3.GetData(), v3.GetData() + NumComps, d3)); // But references are not CHECK_TRUE(this->CompareTuple(tuple1Ref, d1)); CHECK_TRUE(this->CompareTuple(tuple2Ref, d2)); CHECK_TRUE(this->CompareTuple(tuple3Ref, d3)); } // Returns a valid reference. tupleOffset allows different tuples to be // obtained. The valid range for offset is (-2, 2). template static auto GetTestingIterator(Range& range, vtk::TupleIdType tupleOffset = 0) -> decltype(range.begin()) { return range.begin() + (range.size() / 2) + tupleOffset; } template static auto StoreTuple(const RefType& ref) -> std::vector { return std::vector{ ref.begin(), ref.end() }; } template static void RestoreTuple(RefType& ref, const std::vector& data) { std::copy(data.begin(), data.end(), ref.begin()); } template static bool CompareTuple( const RefType& ref, const std::vector& data) { return static_cast(ref.size()) == data.size() && std::equal(data.begin(), data.end(), ref.begin()); } template static bool CompareTuple(IterType begin, IterType end, const std::vector::value_type>& data) { return static_cast(std::distance(begin, end)) == data.size() && std::equal(data.begin(), data.end(), begin); } }; template struct UnitTestComponentIteratorAPI { static constexpr vtk::ComponentIdType NumComps = 3; static constexpr vtk::TupleIdType NumTuples = 12; void operator()() { vtkNew array; array->SetNumberOfComponents(NumComps); array->SetNumberOfTuples(NumTuples); FillTupleRangeIota(vtk::DataArrayTupleRange(array)); auto da = static_cast(array); { // Full, dynamic-size, real typed range auto range = vtk::DataArrayTupleRange(array); DispatchRangeTests(range); } { // Full, dynamic-size, generic-typed range auto range = vtk::DataArrayTupleRange(da); DispatchRangeTests(range); } { // Full, fixed-size, real typed range auto range = vtk::DataArrayTupleRange(array); DispatchRangeTests(range); } { // Full, fixed-size, generic-typed range auto range = vtk::DataArrayTupleRange(da); DispatchRangeTests(range); } } template void DispatchRangeTests(Range range) { { TestComponentIterator(range); TestConstComponentIterator(range); } { const Range& crange = range; TestConstComponentIterator(crange); } } template void TestTypes(Range& range) { using Iter = decltype(this->GetTestingIterRange(range)->begin()); (void)range; CHECK_ITER_TYPE(Iter); } template void TestComponentIterator(Range& range) { TestConstCopy(range); TestConstAssign(range); TestDeref(range); TestIndexing(range); TestIterSwap(range); } template void TestConstComponentIterator(Range& range) { TestTypes(range); TestCopy(range); TestAssign(range); TestTraversal(range); TestDerefConst(range); TestComparison(range); TestIndexingConst(range); TestSwap(range); } template void TestConstCopy(Range& range) { // This should only get called with non-const ranges: static_assert(!std::is_const::value, "Expected mutable range."); const Range crange = range; auto tuple = this->GetTestingIterRange(range); auto ctuple = this->GetTestingIterRange(crange); using IterT = decltype(tuple->begin()); using CIterT = decltype(ctuple->begin()); // We should be able to implicitly cast and compare mutable objects to // const ones: IterT iter{ tuple->begin() }; CIterT citer{ iter }; CHECK_EQUAL_NODUMP(iter, citer); } template void TestConstAssign(Range& range) { // This should only get called with non-const ranges: static_assert(!std::is_const::value, "Expected mutable range."); const Range crange = range; auto tuple = this->GetTestingIterRange(range); auto ctuple = this->GetTestingIterRange(crange); using IterT = decltype(tuple->begin()); using CIterT = decltype(ctuple->begin()); // We should be able to implicitly cast and compare mutable objects to // const ones: IterT iter{ tuple->begin() }; CIterT citer{ ctuple->cend() }; citer = iter; CHECK_EQUAL_NODUMP(iter, citer); } template void TestCopy(Range& range) { auto tuple = this->GetTestingIterRange(range); auto iter = tuple->begin(); using IterType = decltype(iter); IterType iter2{ iter }; CHECK_EQUAL_NODUMP(iter, iter2); } template void TestAssign(Range& range) { auto tuple = this->GetTestingIterRange(range); auto iter = tuple->begin(); 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 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 ComponentType = typename Range::ComponentType; auto tuple = this->GetTestingIterRange(range); ComponentType value = *tuple->begin(); for (auto it = tuple->begin(); it < tuple->end(); ++it) { CHECK_EQUAL(value++, *it); } } template void TestDeref(Range& range) { using ComponentType = typename Range::ComponentType; auto tupleIter = this->GetTestingIterRange(range); auto tuple = *tupleIter; ComponentType initialValue = *tuple.begin(); for (auto it = tuple.begin(); it < tuple.end(); ++it) { *it = 10; } using TupleType = decltype(tuple); for (typename TupleType::reference comp : tuple) { CHECK_EQUAL(comp, 10); } for (typename TupleType::value_type comp : tuple) { comp = 16; (void)comp; // silence set-but-not-used } for (auto it = tuple.begin(); it < tuple.end(); ++it) { CHECK_EQUAL(*it, 10); // Still 10 } for (typename TupleType::reference comp : tuple) { comp = 16; } for (auto it = tuple.begin(); it < tuple.end(); ++it) { CHECK_EQUAL(*it, 16); } // Restore: for (auto it = tuple.begin(); it < tuple.end(); ++it) { *it = initialValue++; } } template void TestIndexingConst(Range& range) { using ComponentType = typename Range::ComponentType; auto tupleIter = this->GetTestingIterRange(range); auto tuple = *tupleIter; using TupleType = decltype(tuple); using IndexType = typename TupleType::size_type; auto compIter = tuple.begin(); ComponentType comp = *compIter; for (IndexType i = 0; i < tuple.size(); ++i) { typename TupleType::value_type val = compIter[i]; typename TupleType::const_reference cref = compIter[i]; CHECK_EQUAL(comp, val); CHECK_EQUAL(comp, cref); ++comp; } } template void TestIndexing(Range& range) { using ComponentType = typename Range::ComponentType; auto tupleIter = this->GetTestingIterRange(range); auto tuple = *tupleIter; using TupleType = decltype(tuple); using IndexType = typename TupleType::size_type; auto compIter = tuple.begin(); ComponentType compSave = *compIter; ComponentType comp = *compIter; for (IndexType i = 0; i < tuple.size(); ++i) { typename TupleType::value_type val = compIter[i]; typename TupleType::reference ref = compIter[i]; typename TupleType::const_reference cref = compIter[i]; CHECK_EQUAL(comp, val); CHECK_EQUAL(comp, ref); CHECK_EQUAL(comp, cref); ++comp; // Set the ref to 10 to check that assignment works: ref = 10; } // Check that the assignment worked: for (IndexType i = 0; i < tuple.size(); ++i) { typename TupleType::value_type val = compIter[i]; typename TupleType::reference ref = compIter[i]; typename TupleType::const_reference cref = compIter[i]; CHECK_EQUAL(10, val); CHECK_EQUAL(10, ref); CHECK_EQUAL(10, cref); // Restore the component: ref = compSave++; } } 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 ComponentType = typename Range::ComponentType; auto iter = this->GetTestingIter(range); auto iter1 = iter; auto iter2 = iter1 + 1; ComponentType val1 = *iter1; ComponentType 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 range for a single tuple's components. tupleOffset // allows different tuples to be obtained. The valid range for offset is // (-2, 2). template static auto GetTestingIterRange(Range& range, vtk::TupleIdType tupleOffset = 0) -> decltype(range.begin()) { return (range.begin() + (range.size() / 2)) + tupleOffset; } // Returns an iterator. tupleOffset allows iterators from different tuples to // be obtained. The returned iterator +/- 1 position is guaranteed valid. template static auto GetTestingIter(Range& range, vtk::TupleIdType tupleOffset = 0) -> decltype(range.begin()->begin()) { return (*(range.begin() + (range.size() / 2) + tupleOffset)).begin() + 1; } }; template struct UnitTestComponentReferenceAPI { static constexpr vtk::ComponentIdType NumComps = 9; static constexpr vtk::TupleIdType NumTuples = 5; void operator()() { vtkNew array; array->SetNumberOfComponents(NumComps); array->SetNumberOfTuples(NumTuples); FillTupleRangeIota(vtk::DataArrayTupleRange(array)); auto da = static_cast(array); { // Full, dynamic-size, real typed range auto range = vtk::DataArrayTupleRange(array); DispatchRangeTests(range); } { // Full, dynamic-size, generic-typed range auto range = vtk::DataArrayTupleRange(da); DispatchRangeTests(range); } { // Full, fixed-size, real typed range auto range = vtk::DataArrayTupleRange(array); DispatchRangeTests(range); } { // Full, fixed-size, generic-typed range auto range = vtk::DataArrayTupleRange(da); DispatchRangeTests(range); } } template void DispatchRangeTests(Range range) { { TestComponentReference(range); TestConstComponentReference(range); } { const Range& crange = range; TestConstComponentReference(crange); } } template void TestComponentReference(Range& range) { TestCopy(range); TestAssign(range); TestSwap(range); TestMath(range); } template void TestConstComponentReference(Range& range) { TestComparison(range); TestConstMath(range); } template void TestCopy(Range& range) { using APIType = typename Range::ComponentType; using RefType = typename Range::ComponentReferenceType; RefType ref1 = this->GetTestCompRef(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::ComponentType; using RefType = typename Range::ComponentReferenceType; RefType ref1 = this->GetTestCompRef(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->GetTestCompRef(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::ComponentType; auto ref1 = this->GetTestCompRef(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->GetTestCompRef(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::ComponentType; using RefType = typename Range::ComponentReferenceType; RefType ref1 = this->GetTestCompRef(range, 0); RefType ref2 = this->GetTestCompRef(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::ComponentType; auto ref1 = this->GetTestCompRef(range, 0); auto refTmp = this->GetTestCompRef(range, 0); // same as ref1 auto ref2 = this->GetTestCompRef(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::ComponentType; using CRefType = typename Range::ConstComponentReferenceType; const CRefType ref1 = this->GetTestCompRef(range, 0); const CRefType ref2 = this->GetTestCompRef(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 component reference. Valid offsets range from (-4, 4), and // values increase with offset. template auto GetTestCompRef(Range& range, vtk::ComponentIdType offset) -> decltype(std::declval()[0][0]) { assert(offset >= -4 && offset <= 4); return range[3][4 + offset]; } }; struct UnitTestEdgeCases { static constexpr vtk::ComponentIdType NumComps = 3; static constexpr vtk::TupleIdType NumTuples = 12; void operator()() { TestSpecializations(); std::cerr << "SOA <--> AOS\n"; DispatchTupleCompat, vtkAOSDataArrayTemplate>(); std::cerr << "AOS <--> SOA\n"; DispatchTupleCompat, vtkSOADataArrayTemplate>(); std::cerr << "SOA <--> AOS\n"; DispatchTupleCompat, vtkAOSDataArrayTemplate>(); std::cerr << "AOS <--> SOA\n"; DispatchTupleCompat, vtkSOADataArrayTemplate>(); std::cerr << "SOA <--> AOS\n"; DispatchTupleCompat, vtkAOSDataArrayTemplate>(); std::cerr << "AOS <--> SOA\n"; DispatchTupleCompat, vtkSOADataArrayTemplate>(); std::cerr << "ScaleSOA <--> AOS\n"; DispatchTupleCompat, vtkAOSDataArrayTemplate>(); std::cerr << "AOS <--> ScaleSOA\n"; DispatchTupleCompat, vtkScaledSOADataArrayTemplate>(); std::cerr << "ScaleSOA <--> AOS\n"; DispatchTupleCompat, vtkAOSDataArrayTemplate>(); std::cerr << "AOS <--> ScaleSOA\n"; DispatchTupleCompat, vtkScaledSOADataArrayTemplate>(); std::cerr << "ScaleSOA <--> AOS\n"; DispatchTupleCompat, vtkAOSDataArrayTemplate>(); std::cerr << "AOS <--> ScaleSOA\n"; DispatchTupleCompat, 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::DataArrayTupleRange(std::declval())); using CompIterType = decltype(std::declval().begin()->begin()); static_assert(std::is_same::type>::value, "AOS specialization not used!"); } #endif template static void PrepArray(ArrayType* array) { array->SetNumberOfComponents(NumComps); array->SetNumberOfTuples(NumTuples); FillTupleRangeIota(vtk::DataArrayTupleRange(array)); } template void DispatchTupleCompat() { 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::DataArrayTupleRange(a1); auto aRange2 = vtk::DataArrayTupleRange(a2); auto daRange1 = vtk::DataArrayTupleRange(da1); auto daRange2 = vtk::DataArrayTupleRange(da2); auto aFixedRange1 = vtk::DataArrayTupleRange(a1); auto aFixedRange2 = vtk::DataArrayTupleRange(a2); auto daFixedRange1 = vtk::DataArrayTupleRange(da1); auto daFixedRange2 = vtk::DataArrayTupleRange(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 SameComponentType = 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 EnableIfSameComponentType = 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->TestTupleEquality(r1, r2); this->TestTupleInequality(r1, r2); this->TestCompCompare(r1, r2); } // range1 is not const: template void LaunchTests(Range1& r1, Range2& r2, EnableIfRangeIsMutable = nullptr) { this->TestTupleAssignment(r1, r2); this->TestTupleEquality(r1, r2); this->TestTupleInequality(r1, r2); this->TestTupleSwap(r1, r2); this->TestCompAssign(r1, r2); this->TestCompCompare(r1, r2); this->TestCompSwap(r1, r2); } template void TestTupleAssignment(Range1& r1, Range2& r2) { static_assert(IsMutable{}, "r1 must be mutable."); typename Range1::reference ref1 = r1[3]; typename Range2::const_reference ref2 = r2[4]; auto data1 = this->StoreTuple(ref1); auto data2 = this->StoreTuple(ref2); CHECK_TRUE(this->CompareTuple(ref1, data1)); CHECK_TRUE(this->CompareTuple(ref2, data2)); CHECK_FALSE(this->CompareTuple(ref1, data2)); ref1 = ref2; CHECK_TRUE(this->CompareTuple(ref1, data2)); CHECK_TRUE(this->CompareTuple(*(r1.begin() + 3), data2)); CHECK_TRUE(this->CompareTuple(ref2, data2)); this->RestoreTuple(ref1, data1); } template void TestTupleEquality(Range1& r1, Range2& r2) { auto i1 = r1.begin(); auto i2 = r2.begin(); auto end1 = r1.end(); auto end2 = r2.end(); while (i1 < end1 && i2 < end2) { CHECK_TRUE(*i1++ == *i2++); } } template void TestTupleInequality(Range1& r1, Range2& r2) { auto i1 = r1.begin(); auto i2 = r2.begin(); auto end1 = r1.end() - 1; auto end2 = r2.end(); while (i1 < end1 && i2 < end2) { CHECK_TRUE(*++i1 != *i2++); } } template EnableIfNotSwappable TestTupleSwap(Range1&, Range2&) { // no-op, ranges aren't swappable. } template EnableIfSwappable TestTupleSwap(Range1& r1, Range2& r2) { static_assert(SameComponentType::value, "Mismatched ComponentTypes."); static_assert(IsMutable::value, "r1 must be mutable."); static_assert(IsMutable::value, "r2 must be mutable."); typename Range1::iterator iter1 = r1.begin() + 3; typename Range2::iterator iter2 = r2.begin() + 4; typename Range1::reference ref1 = *iter1; typename Range2::reference ref2 = *iter2; auto data1 = this->StoreTuple(ref1); auto data2 = this->StoreTuple(ref2); CHECK_TRUE(this->CompareTuple(ref1, data1)); CHECK_TRUE(this->CompareTuple(ref2, data2)); CHECK_FALSE(this->CompareTuple(ref1, data2)); CHECK_FALSE(this->CompareTuple(ref2, data1)); { // ADL swap: using std::swap; swap(ref1, ref2); } CHECK_TRUE(this->CompareTuple(ref1, data2)); CHECK_TRUE(this->CompareTuple(*(r1.begin() + 3), data2)); CHECK_TRUE(this->CompareTuple(ref2, data1)); CHECK_TRUE(this->CompareTuple(*(r2.begin() + 4), data1)); ref1.swap(ref2); CHECK_TRUE(this->CompareTuple(ref1, data1)); CHECK_TRUE(this->CompareTuple(*(r1.begin() + 3), data1)); CHECK_TRUE(this->CompareTuple(ref2, data2)); CHECK_TRUE(this->CompareTuple(*(r2.begin() + 4), data2)); { std::iter_swap(iter1, iter2); } CHECK_TRUE(this->CompareTuple(ref1, data2)); CHECK_TRUE(this->CompareTuple(*(r1.begin() + 3), data2)); CHECK_TRUE(this->CompareTuple(ref2, data1)); CHECK_TRUE(this->CompareTuple(*(r2.begin() + 4), data1)); this->RestoreTuple(ref1, data1); this->RestoreTuple(ref2, data2); } template void TestCompAssign(Range1& r1, Range2& r2) { static_assert(IsMutable{}, "r1 must be mutable."); typename Range1::reference tref1 = r1[3]; typename Range2::const_reference tref2 = r2[4]; auto data1 = this->StoreTuple(tref1); auto data2 = this->StoreTuple(tref2); CHECK_FALSE(this->CompareTuple(tref1, data2)); // Use iterator_traits to find a proper reference type: using IterT = decltype(tref1.begin()); using RefT = typename std::iterator_traits::reference; auto iter2 = tref2.begin(); for (RefT comp : tref1) { comp = *iter2++; } CHECK_TRUE(this->CompareTuple(tref1, data2)); CHECK_TRUE(this->CompareTuple(tref2, data2)); this->RestoreTuple(tref1, data1); } template void TestCompCompare(Range1& r1, Range2& r2) { typename Range1::const_reference tref1 = r1[3]; typename Range2::const_reference tref2 = r2[3]; auto iter1 = tref1.begin(); auto iter2 = tref2.begin(); 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 TestCompSwap(Range1&, Range2&) { // no-op, ranges aren't swappable. } template EnableIfSwappable TestCompSwap(Range1& r1, Range2& r2) { static_assert(SameComponentType::value, "Mismatched ComponentTypes."); static_assert(IsMutable::value, "r1 must be mutable."); static_assert(IsMutable::value, "r2 must be mutable."); typename Range1::reference tref1 = r1[3]; typename Range2::reference tref2 = r2[4]; auto data1 = this->StoreTuple(tref1); auto data2 = this->StoreTuple(tref2); CHECK_FALSE(this->CompareTuple(tref1, data2)); { auto iter2 = tref2.begin(); for (typename decltype(tref1)::reference comp : tref1) { // ADL swap: using std::swap; swap(comp, *iter2++); } CHECK_TRUE(iter2 == tref2.end()); } CHECK_TRUE(this->CompareTuple(tref1, data2)); CHECK_TRUE(this->CompareTuple(*(r1.begin() + 3), data2)); CHECK_TRUE(this->CompareTuple(tref2, data1)); CHECK_TRUE(this->CompareTuple(*(r2.begin() + 4), data1)); { auto iter1 = tref1.begin(); auto iter2 = tref2.begin(); while (iter1 < tref1.end()) { std::iter_swap(iter1++, iter2++); } CHECK_TRUE(iter2 == tref2.end()); } CHECK_TRUE(this->CompareTuple(tref1, data1)); CHECK_TRUE(this->CompareTuple(*(r1.begin() + 3), data1)); CHECK_TRUE(this->CompareTuple(tref2, data2)); CHECK_TRUE(this->CompareTuple(*(r2.begin() + 4), data2)); this->RestoreTuple(tref1, data1); this->RestoreTuple(tref2, data2); } template static auto StoreTuple(const RefType& ref) -> std::vector { return std::vector{ ref.begin(), ref.end() }; } template static void RestoreTuple(RefType& ref, const std::vector& data) { std::copy(data.begin(), data.end(), ref.begin()); } template static bool CompareTuple(const RefType& ref, const std::vector& data) { return static_cast(ref.size()) == data.size() && std::equal(data.begin(), data.end(), ref.begin()); } }; template void RunTestsForArray() { std::cerr << "TupleRangeAPI:\n"; UnitTestTupleRangeAPI{}(); std::cerr << "TupleIteratorAPI:\n"; UnitTestTupleIteratorAPI{}(); std::cerr << "TupleReferenceAPI:\n"; UnitTestTupleReferenceAPI{}(); std::cerr << "ComponentIteratorAPI:\n"; UnitTestComponentIteratorAPI{}(); std::cerr << "ComponentReferenceAPI:\n"; UnitTestComponentReferenceAPI{}(); } } // end anon namespace int TestDataArrayTupleRange(int, char*[]) { std::cerr << "AOS:\n"; RunTestsForArray>(); std::cerr << "SOA:\n"; RunTestsForArray>(); std::cerr << "ScaleSOA:\n"; RunTestsForArray>(); std::cerr << "vtkFloatArray:\n"; RunTestsForArray(); std::cerr << "\nEdgeCases:\n"; UnitTestEdgeCases{}(); return NumErrors != 0; }