1 /*========================================================================= 2 3 Program: Visualization Toolkit 4 Module: vtkDataArrayRange.h 5 6 Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen 7 All rights reserved. 8 See Copyright.txt or http://www.kitware.com/Copyright.htm for details. 9 10 This software is distributed WITHOUT ANY WARRANTY; without even 11 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 12 PURPOSE. See the above copyright notice for more information. 13 14 =========================================================================*/ 15 16 /** 17 * @file vtkDataArrayRange.h 18 * STL-compatible iterable ranges that provide access to vtkDataArray elements. 19 * 20 * @note Since the term 'range' is overloaded, it's worth pointing out that to 21 * determine the value-range of an array's elements (an unrelated concept to 22 * the Range objects defined here), see the vtkDataArray::GetRange and 23 * vtkGenericDataArray::GetValueRange methods. 24 */ 25 26 #ifndef vtkDataArrayRange_h 27 #define vtkDataArrayRange_h 28 29 #include "vtkAOSDataArrayTemplate.h" 30 #include "vtkDataArray.h" 31 #include "vtkDataArrayMeta.h" 32 #include "vtkDataArrayTupleRange_AOS.h" 33 #include "vtkDataArrayTupleRange_Generic.h" 34 #include "vtkDataArrayValueRange_AOS.h" 35 #include "vtkDataArrayValueRange_Generic.h" 36 #include "vtkMeta.h" 37 #include "vtkSmartPointer.h" 38 39 #include <cassert> 40 #include <iterator> 41 #include <type_traits> 42 43 /** 44 * @file vtkDataArrayRange.h 45 * 46 * The vtkDataArrayRange.h header provides utilities to convert vtkDataArrays 47 * into "range" objects that behave like STL ranges. There are two types of 48 * ranges: TupleRange and ValueRange. 49 * 50 * See Testing/Cxx/ExampleDataArrayRangeAPI.cxx for an illustrative example of 51 * how these ranges and their associated iterators and references are used. 52 * 53 * These ranges unify the different memory layouts supported by VTK and provide 54 * a consistent interface to processing them with high efficiency. Whether a 55 * range is constructed from a vtkDataArray, vtkFloatArray, or even 56 * vtkScaledSOADataArrayTemplate, the same range-based algorithm implementation 57 * can be used to provide the best performance possible using the input array's 58 * API. 59 * 60 * Constructing a range using a derived subclass of vtkDataArray (such as 61 * vtkFloatArray) will always give better performance than a range constructed 62 * from a vtkDataArray pointer, since the vtkDataArray API requires virtual 63 * calls and type conversion. Using a more derived type generally allows the 64 * compiler to optimize out any function calls and emit assembly that directly 65 * operates on the array's raw memory buffer(s). See vtkArrayDispatch for 66 * utilities to convert an unknown vtkDataArray into a more derived type. 67 * Testing/Cxx/ExampleDataArrayRangeDispatch.cxx demonstrates how ranges may 68 * be used with the dispatcher system. 69 * 70 * # TupleRanges 71 * 72 * A TupleRange traverses a vtkDataArray tuple-by-tuple, providing iterators 73 * and reference objects that refer to conceptual tuples. The tuple references 74 * themselves may be iterated upon to access individual components. 75 * 76 * TupleRanges are created via the function vtk::DataArrayTupleRange. See 77 * that function's documentation for more information about creating 78 * TupleRanges. 79 * 80 * # ValueRanges 81 * 82 * A ValueRange will traverse a vtkDataArray in "value index" order, e.g. as 83 * if walking a pointer into an AOS layout array: 84 * 85 * ``` 86 * Array: {X, X, X}, {X, X, X}, {X, X, X}, ... 87 * TupleIdx: 0 0 0 1 1 1 2 2 2 88 * CompIdx: 0 1 2 0 1 2 0 1 2 89 * ValueIdx: 0 1 2 3 4 5 6 7 8 90 * ``` 91 * 92 * ValueRanges are created via the function vtk::DataArrayValueRange. See that 93 * function's documentation for more information about creating ValueRanges. 94 */ 95 96 VTK_ITER_OPTIMIZE_START 97 98 namespace vtk 99 { 100 101 namespace detail 102 { 103 104 // Internal detail: This utility is not directly needed by users of 105 // DataArrayRange. 106 // 107 // These classes are used to detect when specializations exist for a given 108 // array type. They are necessary because given: 109 // 110 // template <typename ArrayType> class SomeTemplateClass; 111 // template <typename T> class SomeTemplateClass<vtkAOSDataArrayTemplate<T>>; 112 // 113 // SomeTemplateClass<vtkFloatArray> will pick the generic version, as ArrayType 114 // is a better match than vtkAOSDataArrayTemplate<T>. This class works around 115 // that by using Declare[Tuple|Value]RangeSpecialization functions that map an 116 // input ArrayTypePtr and tuple size to a specific version of the appropriate 117 // Range. 118 template <typename ArrayTypePtr, ComponentIdType TupleSize> 119 struct SelectTupleRange 120 { 121 private: 122 // Allow this to work with vtkNew, vtkSmartPointer, etc. 123 using ArrayType = typename detail::StripPointers<ArrayTypePtr>::type; 124 125 static_assert(detail::IsValidTupleSize<TupleSize>::value, "Invalid tuple size."); 126 static_assert(detail::IsVtkDataArray<ArrayType>::value, "Invalid array type."); 127 128 public: 129 using type = 130 typename std::decay<decltype(vtk::detail::DeclareTupleRangeSpecialization<ArrayType, TupleSize>( 131 std::declval<ArrayType*>()))>::type; 132 }; 133 134 template <typename ArrayTypePtr, ComponentIdType TupleSize> 135 struct SelectValueRange 136 { 137 private: 138 // Allow this to work with vtkNew, vtkSmartPointer, etc. 139 using ArrayType = typename detail::StripPointers<ArrayTypePtr>::type; 140 141 static_assert(detail::IsValidTupleSize<TupleSize>::value, "Invalid tuple size."); 142 static_assert(detail::IsVtkDataArray<ArrayType>::value, "Invalid array type."); 143 144 public: 145 using type = 146 typename std::remove_reference<decltype(vtk::detail::DeclareValueRangeSpecialization<ArrayType, 147 TupleSize>(std::declval<ArrayType*>()))>::type; 148 }; 149 150 } // end namespace detail 151 152 /** 153 * @brief Generate an stl and for-range compatible range of tuple iterators 154 * from a vtkDataArray. 155 * 156 * This function returns a TupleRange object that is compatible with C++11 157 * for-range syntax. As an example usage, consider a function that takes some 158 * instance of vtkDataArray (or a subclass) and prints the magnitude of each 159 * tuple: 160 * 161 * ``` 162 * template <typename ArrayType> 163 * void PrintMagnitudes(ArrayType *array) 164 * { 165 * using T = vtk::GetAPIType<ArrayType>; 166 * 167 * for (const auto tuple : vtk::DataArrayTupleRange(array)) 168 * { 169 * double mag = 0.; 170 * for (const T comp : tuple) 171 * { 172 * mag += static_cast<double>(comp) * static_cast<double>(comp); 173 * } 174 * mag = std::sqrt(mag); 175 * std::cerr << mag < "\n"; 176 * } 177 * } 178 * ``` 179 * 180 * Note that `ArrayType` is generic in the above function. When 181 * `vtk::DataArrayTupleRange` is given a `vtkDataArray` pointer, the generated 182 * code produces iterators and reference proxies that rely on the `vtkDataArray` 183 * API. However, when a more derived `ArrayType` is passed in (for example, 184 * `vtkFloatArray`), specialized implementations are used that generate highly 185 * optimized code. 186 * 187 * Performance can be further improved when the number of components in the 188 * array is known. By passing a compile-time-constant integer as a template 189 * parameter, e.g. `vtk::DataArrayTupleRange<3>(array)`, specializations are 190 * enabled that allow the compiler to perform additional optimizations. 191 * 192 * `vtk::DataArrayTupleRange` takes an additional two arguments that can be used 193 * to restrict the range of tuples to [start, end). 194 * 195 * There is a compiler definition / CMake option called 196 * `VTK_DEBUG_RANGE_ITERATORS` that enables checks for proper usage of the 197 * range/iterator/reference classes. This slows things down significantly, but 198 * is useful for diagnosing problems. 199 * 200 * In some situations, developers may want to build in Debug mode while still 201 * maintaining decent performance for data-heavy computations. For these 202 * usecases, an additional CMake option `VTK_ALWAYS_OPTIMIZE_ARRAY_ITERATORS` 203 * may be enabled to force optimization of code using these iterators. This 204 * option will force inlining and enable -O3 (or equivalent) optimization level 205 * for iterator code when compiling on platforms that support these features. 206 * This option has no effect when `VTK_DEBUG_RANGE_ITERATORS` is enabled. 207 * 208 * @warning Use caution when using `auto` to hold values or references obtained 209 * from iterators, as they may not behave as expected. This is a deficiency in 210 * C++ that affects all proxy iterators (such as those from `vector<bool>`) 211 * that use a reference object instead of an actual C++ reference type. When in 212 * doubt, use `std::iterator_traits` (along with decltype) or the typedefs 213 * listed below to determine the proper value/reference type to use. The 214 * examples below show how these may be used. 215 * 216 * 217 * To mitigate this, the following types are defined on the range object: 218 * - `Range::TupleIteratorType`: Iterator that visits tuples. 219 * - `Range::ConstTupleIteratorType`: Const iterator that visits tuples. 220 * - `Range::TupleReferenceType`: Mutable tuple proxy reference. 221 * - `Range::ConstTupleReferenceType`: Const tuple proxy reference. 222 * - `Range::ComponentIteratorType`: Iterator that visits components in a tuple. 223 * - `Range::ConstComponentIteratorType`: Const iterator that visits tuple components. 224 * - `Range::ComponentReferenceType`: Reference proxy to a single tuple component. 225 * - `Range::ConstComponentReferenceType`: Const reference proxy to a single tuple component. 226 * - `Range::ComponentType`: `ValueType` of components. 227 * 228 * These can be accessed via the range objects, e.g.: 229 * 230 * ``` 231 * auto range = vtk::DataArrayTupleRange(array); 232 * 233 * using TupleRef = typename decltype(range)::TupleReferenceType; 234 * using ComponentRef = typename decltype(range)::ComponentReferenceType; 235 * 236 * for (TupleRef tuple : range) 237 * { 238 * for (ComponentRef comp : tuple) 239 * { 240 * comp = comp - 1; // Array is modified. 241 * } 242 * } 243 * 244 * using ConstTupleRef = typename decltype(range)::ConstTupleReferenceType; 245 * using ComponentType = typename decltype(range)::ComponentType; 246 * 247 * for (ConstTupleRef tuple : range) 248 * { 249 * for (ComponentType comp : tuple) 250 * { 251 * comp = comp - 1; // Array is not modified. 252 * } 253 * } 254 * ``` 255 */ 256 template <ComponentIdType TupleSize = detail::DynamicTupleSize, 257 typename ArrayTypePtr = vtkDataArray*> 258 VTK_ITER_INLINE auto DataArrayTupleRange(const ArrayTypePtr& array, TupleIdType start = -1, 259 TupleIdType end = -1) -> typename detail::SelectTupleRange<ArrayTypePtr, TupleSize>::type 260 { 261 // Lookup specializations: 262 using RangeType = typename detail::SelectTupleRange<ArrayTypePtr, TupleSize>::type; 263 264 assert(array); 265 266 return RangeType(array, start < 0 ? 0 : start, end < 0 ? array->GetNumberOfTuples() : end); 267 } 268 269 /** 270 * @brief Generate an stl and for-range compatible range of flat AOS iterators 271 * from a vtkDataArray. 272 * 273 * This function returns a ValueRange object that is compatible with C++11 274 * for-range syntax. The array is traversed as if calling 275 * vtkGenericDataArray::GetValue with consecutive, increasing indices. As an 276 * example usage, consider a function that takes some instance of vtkDataArray 277 * (or a subclass) and sums the values it contains: 278 * 279 * ``` 280 * template <typename ArrayType> 281 * auto ComputeSum(ArrayType *array) -> vtk::GetAPIType<ArrayType> 282 * { 283 * using T = vtk::GetAPIType<ArrayType>; 284 * 285 * T sum = 0.; 286 * for (const T val : vtk::DataArrayValueRange(array)) 287 * { 288 * sum += val; 289 * } 290 * return sum; 291 * } 292 * ``` 293 * 294 * These ranges may also be used with STL algorithms: 295 * 296 * ``` 297 * template <typename ArrayType> 298 * auto ComputeSum(ArrayType *array) -> vtk::GetAPIType<ArrayType> 299 * { 300 * const auto range = vtk::DataArrayValueRange(array); 301 * return std::accumulate(range.begin(), range.end(), 0); 302 * } 303 * ``` 304 * 305 * Note that `ArrayType` is generic in the above function. When 306 * `vtk::DataArrayValueRange` is given a `vtkDataArray` pointer, the generated 307 * code produces iterators and reference proxies that rely on the `vtkDataArray` 308 * API. However, when a more derived `ArrayType` is passed in (for example, 309 * `vtkFloatArray`), specialized implementations are used that generate highly 310 * optimized code. 311 * 312 * Performance can be further improved when the number of components in the 313 * array is known. By passing a compile-time-constant integer as a template 314 * parameter, e.g. `vtk::DataArrayValueRange<3>(array)`, specializations are 315 * enabled that allow the compiler to perform additional optimizations. 316 * 317 * `vtk::DataArrayValueRange` takes an additional two arguments that can be used 318 * to restrict the range of values to [start, end). 319 * 320 * There is a compiler definition / CMake option called 321 * `VTK_DEBUG_RANGE_ITERATORS` that enables checks for proper usage of the 322 * range/iterator/reference classes. This slows things down significantly, but 323 * is useful for diagnosing problems. 324 * 325 * In some situations, developers may want to build in Debug mode while still 326 * maintaining decent performance for data-heavy computations. For these 327 * usecases, an additional CMake option `VTK_ALWAYS_OPTIMIZE_ARRAY_ITERATORS` 328 * may be enabled to force optimization of code using these iterators. This 329 * option will force inlining and enable -O3 (or equivalent) optimization level 330 * for iterator code when compiling on platforms that support these features. 331 * This option has no effect when `VTK_DEBUG_RANGE_ITERATORS` is enabled. 332 * 333 * @warning Use caution when using `auto` to hold values or references obtained 334 * from iterators, as they may not behave as expected. This is a deficiency in 335 * C++ that affects all proxy iterators (such as those from `vector<bool>`) 336 * that use a reference object instead of an actual C++ reference type. When in 337 * doubt, use `std::iterator_traits` (along with decltype) or the typedefs 338 * listed below to determine the proper value/reference type to use. The 339 * examples below show how these may be used. 340 * 341 * To mitigate this, the following types are defined on the range object: 342 * - `Range::IteratorType`: Iterator that visits values in AOS order. 343 * - `Range::ConstIteratorType`: Const iterator that visits values in AOS order. 344 * - `Range::ReferenceType`: Mutable value proxy reference. 345 * - `Range::ConstReferenceType`: Const value proxy reference. 346 * - `Range::ValueType`: `ValueType` of array's API. 347 * 348 * These can be accessed via the range objects, e.g.: 349 * 350 * ``` 351 * auto range = vtk::DataArrayValueRange(array); 352 * 353 * using RefType = typename decltype(range)::ReferenceType; 354 * for (RefType ref : range) 355 * { // `ref` is a reference (or reference proxy) to the data held by the array. 356 * ref -= 1; // Array is modified. 357 * } 358 * 359 * using ValueType = typename decltype(range)::ValueType; 360 * for (ValueType value : range) 361 * { // implicitly converts from a reference (or proxy) to a local lvalue `value` 362 * value -= 1; // Array is not modified. 363 * } 364 * ``` 365 */ 366 template <ComponentIdType TupleSize = detail::DynamicTupleSize, 367 typename ArrayTypePtr = vtkDataArray*> 368 VTK_ITER_INLINE auto DataArrayValueRange(const ArrayTypePtr& array, ValueIdType start = -1, 369 ValueIdType end = -1) -> typename detail::SelectValueRange<ArrayTypePtr, TupleSize>::type 370 { 371 using RangeType = typename detail::SelectValueRange<ArrayTypePtr, TupleSize>::type; 372 373 assert(array); 374 375 return RangeType(array, start < 0 ? 0 : start, end < 0 ? array->GetNumberOfValues() : end); 376 } 377 378 } // end namespace vtk 379 380 VTK_ITER_OPTIMIZE_END 381 382 #endif // vtkDataArrayRange_h 383 384 // VTK-HeaderTest-Exclude: vtkDataArrayRange.h 385