1717#include < tuple>
1818#include < type_traits>
1919
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+
2034namespace openvdb {
2135OPENVDB_USE_VERSION_NAMESPACE
2236namespace OPENVDB_VERSION_NAME {
2337
2438// / @cond OPENVDB_DOCS_INTERNAL
2539
26- template <typename ... Ts> struct TypeList ; // forward declaration
40+ // forward declarations
41+ template <typename ... Ts> struct TypeList ;
42+ template <typename ... Ts> struct TupleList ;
2743
2844namespace typelist_internal {
2945
@@ -427,16 +443,6 @@ struct TSTranformImpl<OpT, T, Ts...> {
427443 Append<NextList>;
428444};
429445
430-
431- template <typename OpT> inline void TSForEachImpl (OpT) {}
432- template <typename OpT, typename T, typename ... Ts>
433- inline void TSForEachImpl (OpT op) { op (T ()); TSForEachImpl<OpT, Ts...>(op); }
434-
435- template <template <typename > class OpT > inline void TSForEachImpl () {}
436- template <template <typename > class OpT , typename T, typename ... Ts>
437- inline void TSForEachImpl () { OpT<T>()(); TSForEachImpl<OpT, Ts...>(); }
438-
439-
440446// / @brief Partial apply specialization for an empty @c TypeList
441447// / @tparam OpT User functor to apply to the first valid type
442448// / @tparam BaseT Type of the provided obj
@@ -469,11 +475,99 @@ struct TSApplyImpl<OpT, BaseT, TypeList<T, Ts...>>
469475 }
470476};
471477
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+
472553} // namespace internal
473554
474555// / @endcond
475556
476557
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+
477571// / @brief A list of types (not necessarily unique)
478572// / @details Example:
479573// / @code
@@ -485,6 +579,8 @@ struct TypeList
485579 // / The type of this list
486580 using Self = TypeList;
487581
582+ using AsTupleList = TupleList<Ts...>;
583+
488584 // / @brief The number of types in the type list
489585 static constexpr size_t Size = sizeof ...(Ts);
490586
@@ -657,7 +753,9 @@ struct TypeList
657753 // / @note OpT must be a templated class. It is created and invoked for each
658754 // / type in this list.
659755 template <template <typename > class OpT >
660- static void foreach () { typelist_internal::TSForEachImpl<OpT, Ts...>(); }
756+ static OPENVDB_TYPELIST_FORCE_INLINE void foreach () {
757+ typelist_internal::TSForEachImpl<OpT, Ts...>();
758+ }
661759
662760 // / @brief Invoke a templated, unary functor on a value of each type in this list.
663761 // / @details Example:
@@ -680,7 +778,19 @@ struct TypeList
680778 // / @note The functor object is passed by value. Wrap it with @c std::ref
681779 // / to use the same object for each type.
682780 template <typename OpT>
683- static void foreach (OpT op) { typelist_internal::TSForEachImpl<OpT, Ts...>(op); }
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+ }
684794
685795 // / @brief Invoke a templated, unary functor on a provide @c obj of type
686796 // / @c BaseT only if said object is an applicable (derived) type
@@ -718,11 +828,152 @@ struct TypeList
718828 // / @note The functor object is passed by value. Wrap it with @c std::ref
719829 // / pass by reference.
720830 template <typename OpT, typename BaseT>
721- static bool apply (OpT op, BaseT& obj) {
831+ static OPENVDB_TYPELIST_FORCE_INLINE bool apply (OpT op, BaseT& obj) {
722832 return typelist_internal::TSApplyImpl<OpT, BaseT, Self>::apply (obj, op);
723833 }
724834};
725835
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+ };
726977
727978} // namespace OPENVDB_VERSION_NAME
728979} // namespace openvdb
0 commit comments