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