OpenVDB 11.0.0

Loading...
Searching...
No Matches
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 {
35 OPENVDB_USE_VERSION_NAMESPACE
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
538RetT 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>
560 OPENVDB_TYPELIST_FORCE_INLINE auto foreachIndex(OpT op)
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>
623 static constexpr int64_t Index = typelist_internal::TSHasTypeImpl<Self, T>::Index;
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>
786 static OPENVDB_TYPELIST_FORCE_INLINE void foreachIndex(OpT op) {
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>
909 OPENVDB_TYPELIST_FORCE_INLINE void evalFirstPred(Pred pred, OpT op)
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{
957 using AsTypeList = TypeList<>;
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
#define OPENVDB_FORCE_INLINE
Definition Platform.h:103
OPENVDB_TYPELIST_FORCE_INLINE
#define OPENVDB_TYPELIST_FORCE_INLINE
Definition TypeList.h:31
openvdb::v11_0::foreachIndex
OPENVDB_FORCE_INLINE auto foreachIndex(OpT op)
Definition TypeList.h:560
openvdb::v11_0::evalFirstIndex
OPENVDB_FORCE_INLINE RetT evalFirstIndex(OpT op, const RetT def=RetT())
Definition TypeList.h:566
openvdb
Definition Exceptions.h:13
std
Definition Coord.h:589
openvdb::v11_0::TupleList<>::TupleT
std::tuple<> TupleT
Definition TypeList.h:958
openvdb::v11_0::TupleList<>::evalFirstPred
constexpr void evalFirstPred(Pred, OpT)
Definition TypeList.h:970
openvdb::v11_0::TupleList<>::TupleList
TupleList()=default
openvdb::v11_0::TupleList<>::tuple
TupleT & tuple()
Definition TypeList.h:963
openvdb::v11_0::TupleList<>::tuple
const TupleT & tuple() const
Definition TypeList.h:964
openvdb::v11_0::TupleList<>::get
constexpr auto & get()
Definition TypeList.h:966
openvdb::v11_0::TupleList<>::size
constexpr auto size()
Definition TypeList.h:962
openvdb::v11_0::TupleList<>::get
constexpr auto & get() const
Definition TypeList.h:967
openvdb::v11_0::TupleList<>::evalFirstPred
constexpr RetT evalFirstPred(Pred, OpT, RetT def)
Definition TypeList.h:972
openvdb::v11_0::TupleList<>::AsTypeList
TypeList<> AsTypeList
Definition TypeList.h:957
openvdb::v11_0::TupleList
A trivial wrapper around a std::tuple but with compatible TypeList methods. Importantly can be instat...
Definition TypeList.h:851
openvdb::v11_0::TupleList< Ts... >::TupleT
std::tuple< Ts... > TupleT
Definition TypeList.h:853
openvdb::v11_0::TupleList::TupleList
TupleList()=default
openvdb::v11_0::TupleList::tuple
constexpr TupleT & tuple()
Definition TypeList.h:859
openvdb::v11_0::TupleList::get
constexpr auto & get()
Definition TypeList.h:862
openvdb::v11_0::TupleList::size
constexpr auto size()
Definition TypeList.h:858
openvdb::v11_0::TupleList::evalFirstPred
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
openvdb::v11_0::TupleList::evalFirstPred
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
openvdb::v11_0::TupleList::tuple
constexpr TupleT & tuple() const
Definition TypeList.h:860
openvdb::v11_0::TupleList::TupleList
TupleList(Ts &&... args)
Definition TypeList.h:856
openvdb::v11_0::TupleList::get
constexpr auto & get() const
Definition TypeList.h:863
openvdb::v11_0::TupleList< Ts... >::AsTypeList
TypeList< Ts... > AsTypeList
Definition TypeList.h:852
openvdb::v11_0::TypeList
A list of types (not necessarily unique)
Definition TypeList.h:578
openvdb::v11_0::TypeList< FloatGrid, DoubleGrid >::Contains
static constexpr bool Contains
Definition TypeList.h:607
openvdb::v11_0::TypeList< FloatGrid, DoubleGrid >::Remove
typename typelist_internal::TSRemoveImpl< Self, TypesToRemove... >::type Remove
Definition TypeList.h:669
openvdb::v11_0::TypeList< FloatGrid, DoubleGrid >::Index
static constexpr int64_t Index
Definition TypeList.h:623
openvdb::v11_0::TypeList< FloatGrid, DoubleGrid >::Front
Get< 0 > Front
Definition TypeList.h:591
openvdb::v11_0::TypeList< FloatGrid, DoubleGrid >::Back
Get< Size-1 > Back
Definition TypeList.h:592
openvdb::v11_0::TypeList< FloatGrid, DoubleGrid >::Get
typename typelist_internal::TSGetElementImpl< Self, N >::type Get
Definition TypeList.h:590
openvdb::v11_0::TypeList< FloatGrid, DoubleGrid >::RemoveByIndex
typename typelist_internal::TSRemoveIndicesImpl< Self, First, Last >::type RemoveByIndex
Definition TypeList.h:716
openvdb::v11_0::TypeList< FloatGrid, DoubleGrid >::Unique
typename typelist_internal::TSRecurseAppendUniqueImpl< ListT, Ts... >::type Unique
Definition TypeList.h:638
openvdb::v11_0::TypeList< FloatGrid, DoubleGrid >::PopFront
typename typelist_internal::TSRemoveFirstImpl< Self >::type PopFront
Definition TypeList.h:684
openvdb::v11_0::TypeList< FloatGrid, DoubleGrid >::AsTupleList
TupleList< Ts... > AsTupleList
Definition TypeList.h:582
openvdb::v11_0::TypeList< FloatGrid, DoubleGrid >::Size
static constexpr size_t Size
Definition TypeList.h:585
openvdb::v11_0::TypeList::foreachIndex
static OPENVDB_FORCE_INLINE void foreachIndex(OpT op)
Definition TypeList.h:786
openvdb::v11_0::TypeList< FloatGrid, DoubleGrid >::Transform
typename typelist_internal::TSTranformImpl< OpT, Ts... >::type Transform
Definition TypeList.h:736
openvdb::v11_0::TypeList::foreachIndex
static OPENVDB_FORCE_INLINE RetT foreachIndex(OpT op, RetT def)
Definition TypeList.h:791
openvdb::v11_0::TypeList::apply
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
openvdb::v11_0::TypeList< FloatGrid, DoubleGrid >::PopBack
typename typelist_internal::TSRemoveLastImpl< Self >::type PopBack
Definition TypeList.h:699
openvdb::v11_0::TypeList< FloatGrid, DoubleGrid >::Append
typename typelist_internal::TSAppendImpl< Self, TypesToAppend... >::type Append
Definition TypeList.h:656
openvdb::v11_0::TypeList< FloatGrid, DoubleGrid >::Self
TypeList Self
Definition TypeList.h:580
OPENVDB_VERSION_NAME
#define OPENVDB_VERSION_NAME
The version namespace name for this library version.
Definition version.h.in:121
OPENVDB_USE_VERSION_NAMESPACE
#define OPENVDB_USE_VERSION_NAMESPACE
Definition version.h.in:212