OpenVDB  11.0.0
TypeList.h
Go to the documentation of this file.
1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3 
4 /// @file TypeList.h
5 ///
6 /// @brief A TypeList provides a compile time sequence of heterogeneous types
7 /// which can be accessed, transformed and executed over in various ways.
8 /// It incorporates a subset of functionality similar to boost::mpl::vector
9 /// however provides most of its content through using declarations rather
10 /// than additional typed classes.
11 
12 #ifndef OPENVDB_TYPELIST_HAS_BEEN_INCLUDED
13 #define OPENVDB_TYPELIST_HAS_BEEN_INCLUDED
14 
15 #include "version.h"
16 
17 #include <tuple>
18 #include <type_traits>
19 
20 /// We should usually not be decorating public API functions with attributes
21 /// such as always_inline. However many compilers are notoriously bad at
22 /// inlining recursive template loops with default inline settings. The
23 /// TypeList and TupleList metaprogram constructs heavily use this C++ feature
24 /// and the performance difference can be substantial, even for very small
25 /// lists. You can disable this behaviour by setting the define:
26 /// OPENVDB_TYPELIST_NO_FORCE_INLINE
27 /// This will disable the force inling on public API methods in this file.
28 #ifdef OPENVDB_TYPELIST_NO_FORCE_INLINE
29 #define OPENVDB_TYPELIST_FORCE_INLINE inline
30 #else
31 #define OPENVDB_TYPELIST_FORCE_INLINE OPENVDB_FORCE_INLINE
32 #endif
33 
34 namespace openvdb {
36 namespace OPENVDB_VERSION_NAME {
37 
38 /// @cond OPENVDB_DOCS_INTERNAL
39 
40 // forward declarations
41 template<typename... Ts> struct TypeList;
42 template<typename... Ts> struct TupleList;
43 
44 namespace typelist_internal {
45 
46 // Implementation details of @c TypeList
47 
48 /// @brief Dummy struct, used as the return type from invalid or out-of-range
49 /// @c TypeList queries.
50 struct NullType {};
51 
52 
53 /// @brief Type resolver for index queries
54 /// @details Defines a type at a given location within a @c TypeList or the
55 /// @c NullType if the index is out-of-range. The last template
56 /// parameter is used to determine if the index is in range.
57 /// @tparam ListT The @c TypeList
58 /// @tparam Idx The index of the type to get
59 template<typename ListT, size_t Idx, typename = void> struct TSGetElementImpl;
60 
61 /// @brief Partial specialization for valid (in range) index queries.
62 /// @tparam Ts Unpacked types from a @c TypeList
63 /// @tparam Idx The index of the type to get
64 template<typename... Ts, size_t Idx>
65 struct TSGetElementImpl<TypeList<Ts...>, Idx,
66  typename std::enable_if<(Idx < sizeof...(Ts) && sizeof...(Ts))>::type> {
67  using type = typename std::tuple_element<Idx, std::tuple<Ts...>>::type;
68 };
69 
70 /// @brief Partial specialization for invalid index queries (i.e. out-of-range
71 /// indices such as @c TypeList<Int32>::Get<1>). Defines the NullType.
72 /// @tparam Ts Unpacked types from a @c TypeList
73 /// @tparam Idx The index of the type to get
74 template<typename... Ts, size_t Idx>
75 struct TSGetElementImpl<TypeList<Ts...>, Idx,
76  typename std::enable_if<!(Idx < sizeof...(Ts) && sizeof...(Ts))>::type> {
77  using type = NullType;
78 };
79 
80 
81 /// @brief Search for a given type within a @c TypeList.
82 /// @details If the type is found, a @c bool constant @c Value is set to true
83 /// and an @c int64_t @c Index points to the location of the type. If
84 /// multiple versions of the types exist, the value of @c Index is
85 /// always the location of the first matching type. If the type is not
86 /// found, @c Value is set to false and @c Index is set to -1.
87 /// @note This implementation is recursively defined until the type is found
88 /// or until the end of the list is reached. The last template argument
89 /// is used as an internal counter to track the current index being
90 /// evaluated.
91 /// @tparam ListT The @c TypeList
92 /// @tparam T The type to find
93 template <typename ListT, typename T, size_t=0>
94 struct TSHasTypeImpl;
95 
96 /// @brief Partial specialization on an empty @c TypeList, instantiated when
97 /// @c TSHasTypeImpl has been invoked with an empty @c TypeList or when
98 /// a recursive search reaches the end of a @c TypeList.
99 /// @tparam T The type to find
100 /// @tparam Idx Current index
101 template <typename T, size_t Idx>
102 struct TSHasTypeImpl<TypeList<>, T, Idx> {
103  static constexpr bool Value = false;
104  static constexpr int64_t Index = -1;
105 };
106 
107 /// @brief Partial specialization on a @c TypeList which still contains types,
108 /// but the current type being evaluated @c U does not match the given
109 /// type @C T.
110 /// @tparam U The current type being evaluated within the @c TypeList
111 /// @tparam T The type to find
112 /// @tparam Ts Remaining types
113 /// @tparam Idx Current index
114 template <typename U, typename T, typename... Ts, size_t Idx>
115 struct TSHasTypeImpl<TypeList<U, Ts...>, T, Idx> :
116  TSHasTypeImpl<TypeList<Ts...>, T, Idx+1> {};
117 
118 /// @brief Partial specialization on a @c TypeList where @c T matches the
119 /// current type (i.e. the type has been found).
120 /// @tparam T The type to find
121 /// @tparam Ts Remaining types
122 /// @tparam Idx Current index
123 template <typename T, typename... Ts, size_t Idx>
124 struct TSHasTypeImpl<TypeList<T, Ts...>, T, Idx>
125 {
126  static constexpr bool Value = true;
127  static constexpr int64_t Index = static_cast<int64_t>(Idx);
128 };
129 
130 
131 /// @brief Similar to TsAppendImpl but only appends types to a list if the
132 /// type does not alreay exist in the list.
133 /// @details Defines a new @c TypeList with non-unique types appended
134 /// @tparam U Type to append
135 /// @tparam ListT The @c TypeList to append to
136 template <typename U, typename ListT,
137  bool ListContainsType = TSHasTypeImpl<ListT, U>::Value>
138 struct TSAppendUniqueImpl;
139 
140 /// @brief Partial specialization where the currently evaluating type @c U in
141 /// a @c TypeList already exists in the list. Returns the unmodified list.
142 /// @tparam U Type to append
143 /// @tparam Ts Other types within the @c TypeList
144 template <typename U, typename... Ts>
145 struct TSAppendUniqueImpl<U, TypeList<Ts...>, true> {
146 private:
147  using RemovedU = typename TypeList<Ts...>::template Remove<U>;
148 public:
149  /// @note It's simpler to remove the current type U and append the rest by
150  /// just having "using type = TypeList<Ts...>". However this ends up with
151  /// with keeping the last seen type rather than the first which this
152  /// method historically did. e.g:
153  /// TypeList<float, int, float>::Unique<> can become:
154  /// a) TypeList<float, int> currently
155  /// b) TypeList<int, float> if we used the afformentioned technique
156  /// Might be useful to have both? Complexity in (a) is currently linear so
157  /// this shouldn't be a problem, but be careful this doesn't change.
158  //using type = TypeList<Ts...>;
159  using type = typename TypeList<U>::template Append<RemovedU>;
160 };
161 
162 /// @brief Partial specialization where the currently evaluating type @c U in
163 /// a @c TypeList does not exists in the list. Returns the appended list.
164 /// @tparam U Type to append
165 /// @tparam Ts Other types within the @c TypeList
166 template <typename U, typename... Ts>
167 struct TSAppendUniqueImpl<U, TypeList<Ts...>, false> {
168  using type = TypeList<U, Ts...>;
169 };
170 
171 /// @brief Reconstruct a @c TypeList containing only unique types.
172 /// @details This implementation effectively rebuilds a @c TypeList by
173 /// starting with an empty @c TypeList and recursively defining an expanded
174 /// @c TypeList for every type (first to last), only if the type does not
175 /// already exist in the new @c TypeList. This has the effect of dropping all
176 /// but the first of duplicate types.
177 /// @warning This implementation previously used an embdedded std::conditional
178 /// which resulted in drastically slow compilation times. If you're changing
179 /// this implementation make sure to profile compile times with larger lists.
180 /// @tparam Ts Types within the @c TypeList
181 template <typename... Ts>
182 struct TSRecurseAppendUniqueImpl;
183 
184 /// @brief Terminate type recursion when the end of a @c TypeList is reached.
185 template <>
186 struct TSRecurseAppendUniqueImpl<> {
187  using type = TypeList<>;
188 };
189 
190 /// @brief Merge and unpack an initial @c TypeList from the first argument if
191 /// such a @c TypeList has been provided.
192 /// @tparam Ts Types within the first @c TypeList
193 /// @tparam OtherTs Other types
194 template <typename... Ts, typename... OtherTs>
195 struct TSRecurseAppendUniqueImpl<TypeList<Ts...>, OtherTs...> {
196  using type = typename TSRecurseAppendUniqueImpl<OtherTs..., Ts...>::type;
197 };
198 
199 /// @brief Recursively call TSRecurseAppendUniqueImpl with each type in the
200 /// provided @c TypeLists, rebuilding a new list with only the unique set
201 /// of types.
202 /// @tparam U Next type to check for uniqueness and append
203 /// @tparam Ts Remaining types within the @c TypeList
204 template <typename U, typename... Ts>
205 struct TSRecurseAppendUniqueImpl<U, Ts...>
206 {
207  using type = typename TSAppendUniqueImpl<U,
208  typename TSRecurseAppendUniqueImpl<Ts...>::type
209  >::type;
210 };
211 
212 
213 /// @brief Append any number of types to a @c TypeList
214 /// @details Defines a new @c TypeList with the provided types appended
215 /// @tparam ListT The @c TypeList to append to
216 /// @tparam Ts Types to append
217 template<typename ListT, typename... Ts> struct TSAppendImpl;
218 
219 /// @brief Partial specialization for a @c TypeList with a list of zero or more
220 /// types to append
221 /// @tparam Ts Current types within the @c TypeList
222 /// @tparam OtherTs Other types to append
223 template<typename... Ts, typename... OtherTs>
224 struct TSAppendImpl<TypeList<Ts...>, OtherTs...> {
225  using type = TypeList<Ts..., OtherTs...>;
226 };
227 
228 /// @brief Partial specialization for a @c TypeList with another @c TypeList.
229 /// Appends the other TypeList's members.
230 /// @tparam Ts Types within the first @c TypeList
231 /// @tparam OtherTs Types within the second @c TypeList
232 template<typename... Ts, typename... OtherTs>
233 struct TSAppendImpl<TypeList<Ts...>, TypeList<OtherTs...>> {
234  using type = TypeList<Ts..., OtherTs...>;
235 };
236 
237 
238 /// @brief Remove all occurrences of type T from a @c TypeList
239 /// @details Defines a new @c TypeList with the provided types removed
240 /// @tparam ListT The @c TypeList
241 /// @tparam T Type to remove
242 template<typename ListT, typename T> struct TSEraseImpl;
243 
244 /// @brief Partial specialization for an empty @c TypeList
245 /// @tparam T Type to remove, has no effect
246 template<typename T>
247 struct TSEraseImpl<TypeList<>, T> { using type = TypeList<>; };
248 
249 /// @brief Partial specialization where the currently evaluating type in a
250 /// @c TypeList matches the type to remove. Recursively defines this
251 /// implementation with the remaining types.
252 /// @tparam Ts Unpacked types within the @c TypeList
253 /// @tparam T Type to remove
254 template<typename... Ts, typename T>
255 struct TSEraseImpl<TypeList<T, Ts...>, T> {
256  using type = typename TSEraseImpl<TypeList<Ts...>, T>::type;
257 };
258 
259 /// @brief Partial specialization where the currently evaluating type @c T2 in
260 /// a @c TypeList does not match the type to remove @c T. Recursively
261 /// defines this implementation with the remaining types.
262 /// @tparam T2 Current type within the @c TypeList, which does not match @c T
263 /// @tparam Ts Other types within the @c TypeList
264 /// @tparam T Type to remove
265 template<typename T2, typename... Ts, typename T>
266 struct TSEraseImpl<TypeList<T2, Ts...>, T> {
267  using type = typename TSAppendImpl<TypeList<T2>,
268  typename TSEraseImpl<TypeList<Ts...>, T>::type>::type;
269 };
270 
271 /// @brief Front end implementation to call TSEraseImpl which removes all
272 /// occurrences of a type from a @c TypeList. This struct handles the
273 /// case where the type to remove is another @c TypeList, in which case
274 /// all types in the second @c TypeList are removed from the first.
275 /// @tparam ListT The @c TypeList
276 /// @tparam Ts Types in the @c TypeList
277 template<typename ListT, typename... Ts> struct TSRemoveImpl;
278 
279 /// @brief Partial specialization when there are no types in the @c TypeList.
280 /// @tparam ListT The @c TypeList
281 template<typename ListT>
282 struct TSRemoveImpl<ListT> { using type = ListT; };
283 
284 /// @brief Partial specialization when the type to remove @c T is not another
285 /// @c TypeList. @c T is removed from the @c TypeList.
286 /// @tparam ListT The @c TypeList
287 /// @tparam T Type to remove
288 /// @tparam Ts Types in the @c TypeList
289 template<typename ListT, typename T, typename... Ts>
290 struct TSRemoveImpl<ListT, T, Ts...> {
291  using type = typename TSRemoveImpl<typename TSEraseImpl<ListT, T>::type, Ts...>::type;
292 };
293 
294 /// @brief Partial specialization when the type to remove is another
295 /// @c TypeList. All types within the other type list are removed from
296 /// the first list.
297 /// @tparam ListT The @c TypeList
298 /// @tparam Ts Types from the second @c TypeList to remove from the first
299 template<typename ListT, typename... Ts>
300 struct TSRemoveImpl<ListT, TypeList<Ts...>> {
301  using type = typename TSRemoveImpl<ListT, Ts...>::type;
302 };
303 
304 /// @brief Remove the first element of a type list. If the list is empty,
305 /// nothing is done. This base configuration handles the empty list.
306 /// @note Much cheaper to instantiate than TSRemoveIndicesImpl
307 /// @tparam T The @c TypeList
308 template<typename T>
309 struct TSRemoveFirstImpl {
310  using type = TypeList<>;
311 };
312 
313 /// @brief Partial specialization for removing the first type of a @c TypeList
314 /// when the list is not empty i.e. does that actual work.
315 /// @tparam T The first type in the @c TypeList.
316 /// @tparam Ts Remaining types in the @c TypeList
317 template<typename T, typename... Ts>
318 struct TSRemoveFirstImpl<TypeList<T, Ts...>> {
319  using type = TypeList<Ts...>;
320 };
321 
322 
323 /// @brief Remove the last element of a type list. If the list is empty,
324 /// nothing is done. This base configuration handles the empty list.
325 /// @note Cheaper to instantiate than TSRemoveIndicesImpl
326 /// @tparam T The @c TypeList
327 template<typename T>
328 struct TSRemoveLastImpl { using type = TypeList<>; };
329 
330 /// @brief Partial specialization for removing the last type of a @c TypeList.
331 /// This instance is instantiated when the @c TypeList contains a
332 /// single type, or the primary struct which recursively removes types
333 /// (see below) hits the last type. Evaluates the last type to the empty
334 /// list (see above).
335 /// @tparam T The last type in the @c TypeList
336 template<typename T>
337 struct TSRemoveLastImpl<TypeList<T>> : TSRemoveLastImpl<T> {};
338 
339 /// @brief Partial specialization for removing the last type of a @c TypeList
340 /// with a type list size of two or more. Recursively defines this
341 /// implementation with the remaining types, effectively rebuilding the
342 /// @c TypeList until the last type is hit, which is dropped.
343 /// @tparam T The current type in the @c TypeList
344 /// @tparam Ts Remaining types in the @c TypeList
345 template<typename T, typename... Ts>
346 struct TSRemoveLastImpl<TypeList<T, Ts...>>
347 {
348  using type =
349  typename TypeList<T>::template
350  Append<typename TSRemoveLastImpl<TypeList<Ts...>>::type>;
351 };
352 
353 
354 /// @brief Remove a number of types from a @c TypeList based on a @c First and
355 /// @c Last index.
356 /// @details Both indices are inclusive, such that when <tt>First == Last</tt>
357 /// a single type is removed (assuming the index exists). If
358 /// <tt>Last < First</tt>, nothing is done. Any indices which do not
359 /// exist are ignored. If @c Last is greater than the number of types
360 /// in the @c TypeList, all types from @c First to the end of the list
361 /// are dropped.
362 /// @tparam ListT The @c TypeList
363 /// @tparam First The first index
364 /// @tparam Last The last index
365 /// @tparam Idx Internal counter for the current index
366 template<typename ListT, size_t First, size_t Last, size_t Idx=0>
367 struct TSRemoveIndicesImpl;
368 
369 /// @brief Partial specialization for an empty @c TypeList
370 /// @tparam First The first index
371 /// @tparam Last The last index
372 /// @tparam Idx Internal counter for the current index
373 template<size_t First, size_t Last, size_t Idx>
374 struct TSRemoveIndicesImpl<TypeList<>, First, Last, Idx> {
375  using type = TypeList<>;
376 };
377 
378 /// @brief Partial specialization for a @c TypeList containing a single element.
379 /// @tparam T The last or only type in a @c TypeList
380 /// @tparam First The first index
381 /// @tparam Last The last index
382 /// @tparam Idx Internal counter for the current index
383 template<typename T, size_t First, size_t Last, size_t Idx>
384 struct TSRemoveIndicesImpl<TypeList<T>, First, Last, Idx>
385 {
386 private:
387  static constexpr bool Remove = Idx >= First && Idx <= Last;
388 public:
389  using type = typename std::conditional<Remove, TypeList<>, TypeList<T>>::type;
390 };
391 
392 /// @brief Partial specialization for a @c TypeList containing two or more types.
393 /// @details This implementation effectively rebuilds a @c TypeList by starting
394 /// with an empty @c TypeList and recursively defining an expanded
395 /// @c TypeList for every type (first to last), only if the type's
396 /// index does not fall within the range of indices defines by
397 /// @c First and @c Last. Recursively defines this implementation with
398 /// all but the last type.
399 /// @tparam T The currently evaluating type within a @c TypeList
400 /// @tparam Ts Remaining types in the @c TypeList
401 /// @tparam First The first index
402 /// @tparam Last The last index
403 /// @tparam Idx Internal counter for the current index
404 template<typename T, typename... Ts, size_t First, size_t Last, size_t Idx>
405 struct TSRemoveIndicesImpl<TypeList<T, Ts...>, First, Last, Idx>
406 {
407 private:
408  using ThisList = typename TSRemoveIndicesImpl<TypeList<T>, First, Last, Idx>::type;
409  using NextList = typename TSRemoveIndicesImpl<TypeList<Ts...>, First, Last, Idx+1>::type;
410 public:
411  using type = typename ThisList::template Append<NextList>;
412 };
413 
414 /// @brief Transform a @c TypeList, converting each type into a new type based
415 /// on a transformation struct @c OpT.
416 /// @details This implementation iterates through each type in a @c TypeList
417 /// and builds a new @c TypeList where each element is resolved through
418 /// a user provided converter which provides a @c Type definition.
419 /// @tparam OpT User struct to convert each type
420 /// @tparam Ts Remaining types in the @c TypeList
421 template<template <typename> class OpT, typename... Ts> struct TSTranformImpl;
422 
423 /// @brief Partial specialization for an empty @c TypeList
424 /// @tparam OpT User struct to convert each type
425 template<template <typename> class OpT>
426 struct TSTranformImpl<OpT> {
427  using type = TypeList<>;
428 };
429 
430 /// @brief Implementation of TSTranformImpl. See fwd declaration for details.
431 /// @tparam OpT User struct to convert each type
432 /// @tparam Ts Remaining types in the @c TypeList
433 /// @tparam T Current type being converted
434 template<template <typename> class OpT, typename T, typename... Ts>
435 struct TSTranformImpl<OpT, T, Ts...> {
436 private:
437  using NextList = typename TSTranformImpl<OpT, Ts...>::type;
438 public:
439  // Invoke Append for each type to match the behaviour should OpT<T> be a
440  // TypeList<>
441  using type = typename TSTranformImpl<OpT>::type::template
442  Append<OpT<T>>::template
443  Append<NextList>;
444 };
445 
446 /// @brief Partial apply specialization for an empty @c TypeList
447 /// @tparam OpT User functor to apply to the first valid type
448 /// @tparam BaseT Type of the provided obj
449 /// @tparam T Current type
450 /// @tparam Ts Remaining types
451 template<typename OpT, typename BaseT, typename T, typename ...Ts>
452 struct TSApplyImpl { static bool apply(BaseT&, OpT&) { return false; } };
453 
454 /// @brief Apply a unary functor to a provided object only if the object
455 /// satisfies the cast requirement of isType<T> for a type in a TypeList.
456 /// @note Iteration terminates immediately on the first valid type and true
457 /// is returned.
458 /// @tparam OpT User functor to apply to the first valid type
459 /// @tparam BaseT Type of the provided obj
460 /// @tparam T Current type
461 /// @tparam Ts Remaining types
462 template<typename OpT, typename BaseT, typename T, typename ...Ts>
463 struct TSApplyImpl<OpT, BaseT, TypeList<T, Ts...>>
464 {
465  using CastT =
466  typename std::conditional<std::is_const<BaseT>::value, const T, T>::type;
467 
468  static bool apply(BaseT& obj, OpT& op)
469  {
470  if (obj.template isType<T>()) {
471  op(static_cast<CastT&>(obj));
472  return true;
473  }
474  return TSApplyImpl<OpT, BaseT, TypeList<Ts...>>::apply(obj, op);
475  }
476 };
477 
478 template<template <typename> class OpT> inline void TSForEachImpl() {}
479 template<template <typename> class OpT, typename T, typename... Ts>
480 inline void TSForEachImpl() { OpT<T>()(); TSForEachImpl<OpT, Ts...>(); }
481 
482 template<typename OpT> inline void TSForEachImpl(OpT) {}
483 template<typename OpT, typename T, typename... Ts>
484 constexpr OPENVDB_FORCE_INLINE void TSForEachImpl(OpT op) {
485  op(T()); TSForEachImpl<OpT, Ts...>(op);
486 }
487 
488 ///////////////////////////////////////////////////////////////////////////////
489 
490 // Implementation details of @c TupleList
491 
492 template<size_t Iter, size_t End, typename OpT, typename TupleT>
493 constexpr OPENVDB_FORCE_INLINE void TSForEachImpl(
494  [[maybe_unused]] OpT op,
495  [[maybe_unused]] TupleT& tup)
496 {
497  if constexpr(Iter<End) {
498  op(std::get<Iter>(tup));
499  TSForEachImpl<Iter+1, End, OpT, TupleT>(op, tup);
500  }
501 }
502 
503 template<typename OpT, size_t Iter, size_t End>
504 constexpr OPENVDB_FORCE_INLINE void TSForEachIndexImpl([[maybe_unused]] OpT op)
505 {
506  if constexpr(Iter<End) {
507  op(std::integral_constant<std::size_t, Iter>());
508  TSForEachIndexImpl<OpT, Iter+1, End>(op);
509  }
510 }
511 
512 template<typename OpT, typename RetT, size_t Iter, size_t End>
513 constexpr OPENVDB_FORCE_INLINE RetT TSEvalFirstIndex([[maybe_unused]] OpT op, const RetT def)
514 {
515  if constexpr(Iter<End) {
516  if (auto ret = op(std::integral_constant<std::size_t, Iter>())) return ret;
517  return TSEvalFirstIndex<OpT, RetT, Iter+1, End>(op, def);
518  }
519  else return def;
520 }
521 
522 template<class Pred, class OpT, typename TupleT, size_t Iter, size_t End>
523 constexpr OPENVDB_FORCE_INLINE
524 void TSEvalFirstPredImpl(
525  [[maybe_unused]] Pred pred,
526  [[maybe_unused]] OpT op,
527  [[maybe_unused]] TupleT& tup)
528 {
529  if constexpr (Iter<End) {
530  constexpr auto Idx = std::integral_constant<std::size_t, Iter>();
531  if (pred(Idx)) op(std::get<Idx>(tup));
532  else TSEvalFirstPredImpl<Pred, OpT, TupleT, Iter+1, End>(pred, op, tup);
533  }
534 }
535 
536 template<class Pred, class OpT, typename TupleT, typename RetT, size_t Iter, size_t End>
537 constexpr OPENVDB_FORCE_INLINE
538 RetT TSEvalFirstPredImpl(
539  [[maybe_unused]] Pred pred,
540  [[maybe_unused]] OpT op,
541  [[maybe_unused]] TupleT& tup,
542  RetT def)
543 {
544  if constexpr (Iter<End) {
545  constexpr auto Idx = std::integral_constant<std::size_t, Iter>();
546  if (pred(Idx)) return op(std::get<Idx>(tup));
547  else return TSEvalFirstPredImpl
548  <Pred, OpT, TupleT, RetT, Iter+1, End>(pred, op, tup, def);
549  }
550  else return def;
551 }
552 
553 } // namespace internal
554 
555 /// @endcond
556 
557 
558 /// @brief
559 template<size_t Start, size_t End, typename OpT>
561 {
562  typelist_internal::TSForEachIndexImpl<OpT, Start, End>(op);
563 }
564 
565 template<size_t Start, size_t End, typename OpT, typename RetT>
566 OPENVDB_TYPELIST_FORCE_INLINE RetT evalFirstIndex(OpT op, const RetT def = RetT())
567 {
568  return typelist_internal::TSEvalFirstIndex<OpT, RetT, Start, End>(op, def);
569 }
570 
571 /// @brief A list of types (not necessarily unique)
572 /// @details Example:
573 /// @code
574 /// using MyTypes = openvdb::TypeList<int, float, int, double, float>;
575 /// @endcode
576 template<typename... Ts>
577 struct TypeList
578 {
579  /// The type of this list
580  using Self = TypeList;
581 
582  using AsTupleList = TupleList<Ts...>;
583 
584  /// @brief The number of types in the type list
585  static constexpr size_t Size = sizeof...(Ts);
586 
587  /// @brief Access a particular element of this type list. If the index
588  /// is out of range, typelist_internal::NullType is returned.
589  template<size_t N>
590  using Get = typename typelist_internal::TSGetElementImpl<Self, N>::type;
591  using Front = Get<0>;
592  using Back = Get<Size-1>;
593 
594  /// @brief True if this list contains the given type, false otherwise
595  /// @details Example:
596  /// @code
597  /// {
598  /// using IntTypes = openvdb::TypeList<Int16, Int32, Int64>;
599  /// using RealTypes = openvdb::TypeList<float, double>;
600  /// }
601  /// {
602  /// openvdb::TypeList<IntTypes>::Contains<Int32>; // true
603  /// openvdb::TypeList<RealTypes>::Contains<Int32>; // false
604  /// }
605  /// @endcode
606  template<typename T>
607  static constexpr bool Contains = typelist_internal::TSHasTypeImpl<Self, T>::Value;
608 
609  /// @brief Returns the index of the first found element of the given type, -1 if
610  /// no matching element exists.
611  /// @details Example:
612  /// @code
613  /// {
614  /// using IntTypes = openvdb::TypeList<Int16, Int32, Int64>;
615  /// using RealTypes = openvdb::TypeList<float, double>;
616  /// }
617  /// {
618  /// const int64_t L1 = openvdb::TypeList<IntTypes>::Index<Int32>; // 1
619  /// const int64_t L2 = openvdb::TypeList<RealTypes>::Index<Int32>; // -1
620  /// }
621  /// @endcode
622  template<typename T>
624 
625  /// @brief Remove any duplicate types from this TypeList by rotating the
626  /// next valid type left (maintains the order of other types). Optionally
627  /// combine the result with another TypeList.
628  /// @details Example:
629  /// @code
630  /// {
631  /// using Types = openvdb::TypeList<Int16, Int32, Int16, float, float, Int64>;
632  /// }
633  /// {
634  /// using UniqueTypes = Types::Unique<>; // <Int16, Int32, float, Int64>
635  /// }
636  /// @endcode
637  template<typename ListT = TypeList<>>
638  using Unique = typename typelist_internal::TSRecurseAppendUniqueImpl<ListT, Ts...>::type;
639 
640  /// @brief Append types, or the members of another TypeList, to this list.
641  /// @warning Appending nested TypeList<> objects causes them to expand to
642  /// their contained list of types.
643  /// @details Example:
644  /// @code
645  /// {
646  /// using IntTypes = openvdb::TypeList<Int16, Int32, Int64>;
647  /// using RealTypes = openvdb::TypeList<float, double>;
648  /// using NumericTypes = IntTypes::Append<RealTypes>;
649  /// }
650  /// {
651  /// using IntTypes = openvdb::TypeList<Int16>::Append<Int32, Int64>;
652  /// using NumericTypes = IntTypes::Append<float>::Append<double>;
653  /// }
654  /// @endcode
655  template<typename... TypesToAppend>
656  using Append = typename typelist_internal::TSAppendImpl<Self, TypesToAppend...>::type;
657 
658  /// @brief Remove all occurrences of one or more types, or the members of
659  /// another TypeList, from this list.
660  /// @details Example:
661  /// @code
662  /// {
663  /// using NumericTypes = openvdb::TypeList<float, double, Int16, Int32, Int64>;
664  /// using LongTypes = openvdb::TypeList<Int64, double>;
665  /// using ShortTypes = NumericTypes::Remove<LongTypes>; // float, Int16, Int32
666  /// }
667  /// @endcode
668  template<typename... TypesToRemove>
669  using Remove = typename typelist_internal::TSRemoveImpl<Self, TypesToRemove...>::type;
670 
671  /// @brief Remove the first element of this type list. Has no effect if the
672  /// type list is already empty.
673  /// @details Example:
674  /// @code
675  /// {
676  /// using IntTypes = openvdb::TypeList<Int16, Int32, Int64>;
677  /// using EmptyTypes = openvdb::TypeList<>;
678  /// }
679  /// {
680  /// IntTypes::PopFront; // openvdb::TypeList<Int32, Int64>;
681  /// EmptyTypes::PopFront; // openvdb::TypeList<>;
682  /// }
683  /// @endcode
684  using PopFront = typename typelist_internal::TSRemoveFirstImpl<Self>::type;
685 
686  /// @brief Remove the last element of this type list. Has no effect if the
687  /// type list is already empty.
688  /// @details Example:
689  /// @code
690  /// {
691  /// using IntTypes = openvdb::TypeList<Int16, Int32, Int64>;
692  /// using EmptyTypes = openvdb::TypeList<>;
693  /// }
694  /// {
695  /// IntTypes::PopBack; // openvdb::TypeList<Int16, Int32>;
696  /// EmptyTypes::PopBack; // openvdb::TypeList<>;
697  /// }
698  /// @endcode
699  using PopBack = typename typelist_internal::TSRemoveLastImpl<Self>::type;
700 
701  /// @brief Return a new list with types removed by their location within the list.
702  /// If First is equal to Last, a single element is removed (if it exists).
703  /// If First is greater than Last, the list remains unmodified.
704  /// @details Example:
705  /// @code
706  /// {
707  /// using NumericTypes = openvdb::TypeList<float, double, Int16, Int32, Int64>;
708  /// }
709  /// {
710  /// using IntTypes = NumericTypes::RemoveByIndex<0,1>; // openvdb::TypeList<Int16, Int32, Int64>;
711  /// using RealTypes = NumericTypes::RemoveByIndex<2,4>; // openvdb::TypeList<float, double>;
712  /// using RemoveFloat = NumericTypes::RemoveByIndex<0,0>; // openvdb::TypeList<double, Int16, Int32, Int64>;
713  /// }
714  /// @endcode
715  template <size_t First, size_t Last>
716  using RemoveByIndex = typename typelist_internal::TSRemoveIndicesImpl<Self, First, Last>::type;
717 
718  /// @brief Transform each type of this TypeList, rebuiling a new list of
719  /// converted types. This method instantiates a user provided Opt<T> to
720  /// replace each type in the current list.
721  /// @warning Transforming types to new TypeList<> objects causes them to expand to
722  /// their contained list of types.
723  /// @details Example:
724  /// @code
725  /// {
726  /// // Templated type decl, where the type T will be subsituted for each type
727  /// // in the TypeList being transformed.
728  /// template <typename T>
729  /// using ConvertedType = typename openvdb::PromoteType<T>::Next;
730  ///
731  /// // Results in: openvdb::TypeList<Int64, double>;
732  /// using PromotedType = openvdb::TypeList<Int32, float>::Transform<ConvertedType>;
733  /// }
734  /// @endcode
735  template<template <typename> class OpT>
736  using Transform = typename typelist_internal::TSTranformImpl<OpT, Ts...>::type;
737 
738  /// @brief Invoke a templated class operator on each type in this list. Use
739  /// this method if you only need access to the type for static methods.
740  /// @details Example:
741  /// @code
742  /// #include <typeinfo>
743  ///
744  /// template <typename T>
745  /// struct PintTypes() {
746  /// inline void operator()() { std::cout << typeid(T).name() << std::endl; }
747  /// };
748  ///
749  /// using MyTypes = openvdb::TypeList<int, float, double>;
750  /// MyTypes::foreach<PintTypes>(); // "i, f, d" (exact output is compiler-dependent)
751  /// @endcode
752  ///
753  /// @note OpT must be a templated class. It is created and invoked for each
754  /// type in this list.
755  template<template <typename> class OpT>
756  static OPENVDB_TYPELIST_FORCE_INLINE void foreach() {
757  typelist_internal::TSForEachImpl<OpT, Ts...>();
758  }
759 
760  /// @brief Invoke a templated, unary functor on a value of each type in this list.
761  /// @details Example:
762  /// @code
763  /// #include <typeinfo>
764  ///
765  /// template<typename ListT>
766  /// void printTypeList()
767  /// {
768  /// std::string sep;
769  /// auto op = [&](auto x) { // C++14
770  /// std::cout << sep << typeid(decltype(x)).name(); sep = ", "; };
771  /// ListT::foreach(op);
772  /// }
773  ///
774  /// using MyTypes = openvdb::TypeList<int, float, double>;
775  /// printTypeList<MyTypes>(); // "i, f, d" (exact output is compiler-dependent)
776  /// @endcode
777  ///
778  /// @note The functor object is passed by value. Wrap it with @c std::ref
779  /// to use the same object for each type.
780  template<typename OpT>
781  static OPENVDB_TYPELIST_FORCE_INLINE void foreach(OpT op) {
782  typelist_internal::TSForEachImpl<OpT, Ts...>(op);
783  }
784 
785  template<typename OpT>
787  foreachIndex<OpT, 0, Size>(op);
788  }
789 
790  template<typename OpT, typename RetT>
791  static OPENVDB_TYPELIST_FORCE_INLINE RetT foreachIndex(OpT op, RetT def) {
792  return foreachIndex<OpT, RetT, 0, Size>(op, def);
793  }
794 
795  /// @brief Invoke a templated, unary functor on a provide @c obj of type
796  /// @c BaseT only if said object is an applicable (derived) type
797  /// also contained in the current @c TypeList.
798  /// @details This method loops over every type in the type list and calls
799  /// an interface method on @c obj to check to see if the @c obj is
800  /// interpretable as the given type. If it is, the method static casts
801  /// @c obj to the type, invokes the provided functor with the casted type
802  /// and returns, stopping further list iteration. @c obj is expected to
803  /// supply an interface to validate the type which satisfies the
804  /// prototype:
805  /// @code
806  /// template <typename T> bool isType()
807  /// @endcode
808  ///
809  /// A full example (using dynamic_cast - see Grid/Tree implementations
810  /// for string based comparisons:
811  /// @code
812  /// struct Base {
813  /// virtual ~Base() = default;
814  /// template<typename T> bool isType() { return dynamic_cast<const T*>(this); }
815  /// };
816  /// struct MyType1 : public Base { void print() { std::cerr << "MyType1" << std::endl; } };
817  /// struct MyType2 : public Base { void print() { std::cerr << "MyType2" << std::endl; } };
818  ///
819  /// using MyTypeList = TypeList<MyType1, MyType2>;
820  /// Base* getObj() { return new MyType2(); }
821  ///
822  /// std::unique_ptr<Base> obj = getObj();
823  /// // Returns 'true', prints 'MyType2'
824  /// const bool success =
825  /// MyTypeList::apply([](const auto& type) { type.print(); }, *obj);
826  /// @endcode
827  ///
828  /// @note The functor object is passed by value. Wrap it with @c std::ref
829  /// pass by reference.
830  template<typename OpT, typename BaseT>
831  static OPENVDB_TYPELIST_FORCE_INLINE bool apply(OpT op, BaseT& obj) {
832  return typelist_internal::TSApplyImpl<OpT, BaseT, Self>::apply(obj, op);
833  }
834 };
835 
836 /// @brief A trivial wrapper around a std::tuple but with compatible TypeList
837 /// methods. Importantly can be instatiated from a TypeList and implements a
838 /// similar ::foreach interface
839 /// @warning Some member methods here run on actual instances of types in the
840 /// list. As such, it's unlikely that they can always be resolved at compile
841 /// time (unlike methods in TypeList). Compilers are notriously bad at
842 /// automatically inlining recursive/nested template instations (without fine
843 /// tuning inline options to the frontend) so the public API of this class is
844 /// marked as force inlined. You can disable this behaviour by defining:
845 /// OPENVDB_TYPELIST_NO_FORCE_INLINE
846 /// before including this header. Note however that the ValueAccessor uses
847 /// this API and disabling force inlining can cause significant performance
848 /// degredation.
849 template<typename... Ts>
850 struct TupleList
851 {
852  using AsTypeList = TypeList<Ts...>;
853  using TupleT = std::tuple<Ts...>;
854 
855  TupleList() = default;
856  TupleList(Ts&&... args) : mTuple(std::forward<Ts>(args)...) {}
857 
858  constexpr auto size() { return std::tuple_size_v<TupleT>; }
859  constexpr TupleT& tuple() { return mTuple; }
860  constexpr TupleT& tuple() const { return mTuple; }
861 
862  template <size_t Idx> constexpr auto& get() { return std::get<Idx>(mTuple); }
863  template <size_t Idx> constexpr auto& get() const { return std::get<Idx>(mTuple); }
864 
865  /// @brief Run a function on each type instance in the underlying
866  /// std::tuple. Effectively calls op(std::get<I>(mTuple)) where
867  /// I = [0,Size). Does not support returning a value.
868  ///
869  /// @param op Function to run on each type
870  /// @details Example:
871  /// @code
872  /// {
873  /// using Types = openvdb::TypeList<Int32, float, std::string>;
874  /// }
875  /// {
876  /// Types::AsTupleList tuple(Int32(1), float(3.3), std::string("foo"));
877  /// tuple.foreach([](auto value) { std::cout << value << ' '; }); // prints '1 3.3 foo'
878  /// }
879  /// @endcode
880  template<typename OpT>
881  OPENVDB_TYPELIST_FORCE_INLINE constexpr void foreach(OpT op) {
882  typelist_internal::TSForEachImpl<0, AsTypeList::Size>(op, mTuple);
883  }
884 
885  /// @brief Run a function on the first element in the underlying
886  /// std::tuple that satisfies the provided predicate. Effectively
887  /// calls op(std::get<I>(mTuple)) when pred(I) returns true, then exits,
888  /// where I = [0,Size). Does not support returning a value.
889  /// @note This is mainly useful to avoid the overhead of calling std::get<I>
890  /// on every element when only a single unknown element needs processing.
891  ///
892  /// @param pred Predicate to run on each index, should return true/false
893  /// @param op Function to run on the first element that satisfies pred
894  /// @details Example:
895  /// @code
896  /// {
897  /// using Types = openvdb::TypeList<Int32, float, std::string>;
898  /// }
899  /// {
900  /// Types::AsTupleList tuple(Int32(1), float(3.3), std::string("foo"));
901  /// bool runtimeFlags[tuple.size()] = { .... } // some runtime flags
902  /// tuple.foreach(
903  /// [&](auto Idx) { return runtimeFlags[Idx]; },
904  /// [](auto value) { std::cout << value << std::endl; }
905  /// );
906  /// }
907  /// @endcode
908  template<class Pred, class OpT>
910  {
911  typelist_internal::TSEvalFirstPredImpl
912  <Pred, OpT, TupleT, 0, AsTypeList::Size>
913  (pred, op, mTuple);
914  }
915 
916  /// @brief Run a function on the first element in the underlying
917  /// std::tuple that satisfies the provided predicate. Effectively
918  /// calls op(std::get<I>(mTuple)) when pred(I) returns true, then exits,
919  /// where I = [0,Size). Supports returning a value, but a default return
920  /// value must be provided.
921  ///
922  /// @param pred Predicate to run on each index, should return true/false
923  /// @param op Function to run on the first element that satisfies pred
924  /// @param def Default return value
925  /// @details Example:
926  /// @code
927  /// {
928  /// using Types = openvdb::TypeList<Int32, float, std::string>;
929  /// }
930  /// {
931  /// Types::AsTupleList tuple(Int32(1), float(3.3), std::string("foo"));
932  /// // returns 3
933  /// auto size = tuple.foreach(
934  /// [](auto Idx) { return std::is_same<std::string, Types::template Get<Idx>>::value; },
935  /// [](auto value) { return value.size(); },
936  /// -1
937  /// );
938  /// }
939  /// @endcode
940  template<class Pred, class OpT, typename RetT>
941  OPENVDB_TYPELIST_FORCE_INLINE RetT evalFirstPred(Pred pred, OpT op, RetT def)
942  {
943  return typelist_internal::TSEvalFirstPredImpl
944  <Pred, OpT, TupleT, RetT, 0, AsTypeList::Size>
945  (pred, op, mTuple, def);
946  }
947 
948 private:
949  TupleT mTuple;
950 };
951 
952 /// @brief Specilization of an empty TupleList. Required due to constructor
953 /// selection.
954 template<>
955 struct TupleList<>
956 {
958  using TupleT = std::tuple<>;
959 
960  TupleList() = default;
961 
962  constexpr auto size() { return std::tuple_size_v<TupleT>; }
963  inline TupleT& tuple() { return mTuple; }
964  inline const TupleT& tuple() const { return mTuple; }
965 
966  template <size_t Idx> inline constexpr auto& get() { return std::get<Idx>(mTuple); }
967  template <size_t Idx> inline constexpr auto& get() const { return std::get<Idx>(mTuple); }
968 
969  template<typename OpT> constexpr void foreach(OpT) {}
970  template<class Pred, class OpT> constexpr void evalFirstPred(Pred, OpT) {}
971  template<class Pred, class OpT, typename RetT>
972  constexpr RetT evalFirstPred(Pred, OpT, RetT def) { return def; }
973 
974 private:
975  TupleT mTuple;
976 };
977 
978 } // namespace OPENVDB_VERSION_NAME
979 } // namespace openvdb
980 
981 
982 #endif // OPENVDB_TYPELIST_HAS_BEEN_INCLUDED
OPENVDB_FORCE_INLINE void evalFirstPred(Pred pred, OpT op)
Run a function on the first element in the underlying std::tuple that satisfies the provided predicat...
Definition: TypeList.h:909
Get< Size-1 > Back
Definition: TypeList.h:592
constexpr RetT evalFirstPred(Pred, OpT, RetT def)
Definition: TypeList.h:972
static OPENVDB_FORCE_INLINE void foreachIndex(OpT op)
Definition: TypeList.h:786
constexpr TupleT & tuple()
Definition: TypeList.h:859
A list of types (not necessarily unique)
Definition: TypeList.h:577
Definition: Coord.h:589
typename typelist_internal::TSTranformImpl< OpT, Ts... >::type Transform
Transform each type of this TypeList, rebuiling a new list of converted types. This method instantiat...
Definition: TypeList.h:736
typename typelist_internal::TSAppendImpl< Self, TypesToAppend... >::type Append
Append types, or the members of another TypeList, to this list.
Definition: TypeList.h:656
typename typelist_internal::TSRemoveLastImpl< Self >::type PopBack
Remove the last element of this type list. Has no effect if the type list is already empty...
Definition: TypeList.h:699
const TupleT & tuple() const
Definition: TypeList.h:964
typename typelist_internal::TSRecurseAppendUniqueImpl< ListT, Ts... >::type Unique
Remove any duplicate types from this TypeList by rotating the next valid type left (maintains the ord...
Definition: TypeList.h:638
OPENVDB_FORCE_INLINE RetT evalFirstPred(Pred pred, OpT op, RetT def)
Run a function on the first element in the underlying std::tuple that satisfies the provided predicat...
Definition: TypeList.h:941
typename typelist_internal::TSGetElementImpl< Self, N >::type Get
Access a particular element of this type list. If the index is out of range, typelist_internal::NullT...
Definition: TypeList.h:590
typename typelist_internal::TSRemoveFirstImpl< Self >::type PopFront
Remove the first element of this type list. Has no effect if the type list is already empty...
Definition: TypeList.h:684
A trivial wrapper around a std::tuple but with compatible TypeList methods. Importantly can be instat...
Definition: TypeList.h:850
Get< 0 > Front
Definition: TypeList.h:591
constexpr TupleT & tuple() const
Definition: TypeList.h:860
#define OPENVDB_TYPELIST_FORCE_INLINE
Definition: TypeList.h:31
#define OPENVDB_FORCE_INLINE
Definition: Platform.h:103
constexpr auto size()
Definition: TypeList.h:962
Definition: Exceptions.h:13
TupleT & tuple()
Definition: TypeList.h:963
Index32 Index
Definition: Types.h:54
std::tuple<> TupleT
Definition: TypeList.h:958
TupleList(Ts &&...args)
Definition: TypeList.h:856
typename typelist_internal::TSRemoveImpl< Self, TypesToRemove... >::type Remove
Remove all occurrences of one or more types, or the members of another TypeList, from this list...
Definition: TypeList.h:669
std::tuple< Ts... > TupleT
Definition: TypeList.h:853
constexpr auto size()
Definition: TypeList.h:858
OPENVDB_FORCE_INLINE auto foreachIndex(OpT op)
Definition: TypeList.h:560
OPENVDB_FORCE_INLINE RetT evalFirstIndex(OpT op, const RetT def=RetT())
Definition: TypeList.h:566
typename typelist_internal::TSRemoveIndicesImpl< Self, First, Last >::type RemoveByIndex
Return a new list with types removed by their location within the list. If First is equal to Last...
Definition: TypeList.h:716
static OPENVDB_FORCE_INLINE bool apply(OpT op, BaseT &obj)
Invoke a templated, unary functor on a provide obj of type BaseT only if said object is an applicable...
Definition: TypeList.h:831
constexpr void evalFirstPred(Pred, OpT)
Definition: TypeList.h:970
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition: version.h.in:121
static OPENVDB_FORCE_INLINE RetT foreachIndex(OpT op, RetT def)
Definition: TypeList.h:791
#define OPENVDB_USE_VERSION_NAMESPACE
Definition: version.h.in:212