Skip to content

Commit cbcf15f

Browse files
authored
Merge pull request #1547 from Idclip/feature/value_accessor_revamp
Consolidated ValueAccessor implementations
2 parents 00a0cd3 + 6e99c79 commit cbcf15f

File tree

11 files changed

+1836
-3653
lines changed

11 files changed

+1836
-3653
lines changed

openvdb/openvdb/Platform.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,17 @@
9595
#define OPENVDB_UNLIKELY(x) (x)
9696
#endif
9797

98+
/// Force inline function macros. These macros do not necessary guarantee that
99+
/// the decorated function will be inlined, but provide the strongest vendor
100+
/// annotations to that end.
101+
#if defined(__GNUC__)
102+
#define OPENVDB_FORCE_INLINE __attribute__((always_inline)) inline
103+
#elif defined(_MSC_VER)
104+
#define OPENVDB_FORCE_INLINE __forceinline
105+
#else
106+
#define OPENVDB_FORCE_INLINE inline
107+
#endif
108+
98109
/// Bracket code with OPENVDB_NO_UNREACHABLE_CODE_WARNING_BEGIN/_END,
99110
/// as in the following example, to inhibit ICC remarks about unreachable code:
100111
/// @code

openvdb/openvdb/TypeList.h

Lines changed: 265 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,29 @@
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+
2034
namespace openvdb {
2135
OPENVDB_USE_VERSION_NAMESPACE
2236
namespace 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

2844
namespace 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

Comments
 (0)