diff options
author | Jacques Lucke <jacques@blender.org> | 2022-03-19 12:57:40 +0300 |
---|---|---|
committer | Jacques Lucke <jacques@blender.org> | 2022-03-19 12:57:40 +0300 |
commit | 8711483632823524019a6cc95575c5a4ba5aa831 (patch) | |
tree | 76c2f8355d0fce66f4f9e35b5ec1282960e51209 /source/blender | |
parent | 3e16f3b3ef4b8f385b30fe4a1e00860620f610ee (diff) |
BLI: generalize converting CPPType to static type
Previously, the conversion was done manually for a fixed set of types.
Now, there is a more general utility that can be used in other contexts
(outside of geometry nodes attribute processing) as well.
Diffstat (limited to 'source/blender')
-rw-r--r-- | source/blender/blenkernel/BKE_attribute_access.hh | 2 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_attribute_math.hh | 71 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_customdata.h | 9 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/attribute_access.cc | 49 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/customdata.cc | 55 | ||||
-rw-r--r-- | source/blender/blenlib/BLI_cpp_type.hh | 73 | ||||
-rw-r--r-- | source/blender/blenlib/intern/cpp_type.cc | 9 | ||||
-rw-r--r-- | source/blender/blenlib/tests/BLI_cpp_type_test.cc | 24 |
8 files changed, 185 insertions, 107 deletions
diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh index 36f29c7fbb7..8d449a124ec 100644 --- a/source/blender/blenkernel/BKE_attribute_access.hh +++ b/source/blender/blenkernel/BKE_attribute_access.hh @@ -164,8 +164,6 @@ using AttributeForeachCallback = blender::FunctionRef<bool( namespace blender::bke { -const CPPType *custom_data_type_to_cpp_type(const CustomDataType type); -CustomDataType cpp_type_to_custom_data_type(const CPPType &type); CustomDataType attribute_data_type_highest_complexity(Span<CustomDataType> data_types); /** * Domains with a higher "information density" have a higher priority, diff --git a/source/blender/blenkernel/BKE_attribute_math.hh b/source/blender/blenkernel/BKE_attribute_math.hh index 9e97979fde9..3075c0689e9 100644 --- a/source/blender/blenkernel/BKE_attribute_math.hh +++ b/source/blender/blenkernel/BKE_attribute_math.hh @@ -8,71 +8,34 @@ #include "BLI_math_vector.h" #include "BLI_math_vector.hh" -#include "DNA_customdata_types.h" +#include "BKE_customdata.h" namespace blender::attribute_math { /** - * Utility function that simplifies calling a templated function based on a custom data type. + * Utility function that simplifies calling a templated function based on a run-time data type. */ template<typename Func> -inline void convert_to_static_type(const CustomDataType data_type, const Func &func) +inline void convert_to_static_type(const CPPType &cpp_type, const Func &func) { - switch (data_type) { - case CD_PROP_FLOAT: - func(float()); - break; - case CD_PROP_FLOAT2: - func(float2()); - break; - case CD_PROP_FLOAT3: - func(float3()); - break; - case CD_PROP_INT32: - func(int()); - break; - case CD_PROP_BOOL: - func(bool()); - break; - case CD_PROP_INT8: - func(int8_t()); - break; - case CD_PROP_COLOR: - func(ColorGeometry4f()); - break; - default: - BLI_assert_unreachable(); - break; - } + cpp_type.to_static_type_tag<float, float2, float3, int, bool, int8_t, ColorGeometry4f>( + [&](auto type_tag) { + using T = typename decltype(type_tag)::type; + if constexpr (std::is_same_v<T, void>) { + /* It's expected that the given cpp type is one of the supported once. */ + BLI_assert_unreachable(); + } + else { + func(T()); + } + }); } template<typename Func> -inline void convert_to_static_type(const CPPType &cpp_type, const Func &func) +inline void convert_to_static_type(const CustomDataType data_type, const Func &func) { - if (cpp_type.is<float>()) { - func(float()); - } - else if (cpp_type.is<float2>()) { - func(float2()); - } - else if (cpp_type.is<float3>()) { - func(float3()); - } - else if (cpp_type.is<int>()) { - func(int()); - } - else if (cpp_type.is<bool>()) { - func(bool()); - } - else if (cpp_type.is<int8_t>()) { - func(int8_t()); - } - else if (cpp_type.is<ColorGeometry4f>()) { - func(ColorGeometry4f()); - } - else { - BLI_assert_unreachable(); - } + const CPPType &cpp_type = *bke::custom_data_type_to_cpp_type(data_type); + convert_to_static_type(cpp_type, func); } /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/BKE_customdata.h b/source/blender/blenkernel/BKE_customdata.h index 940dc3c4f6c..911f4aab394 100644 --- a/source/blender/blenkernel/BKE_customdata.h +++ b/source/blender/blenkernel/BKE_customdata.h @@ -751,3 +751,12 @@ void CustomData_debug_info_from_layers(const struct CustomData *data, #ifdef __cplusplus } #endif + +#ifdef __cplusplus +# include "BLI_cpp_type.hh" + +namespace blender::bke { +const CPPType *custom_data_type_to_cpp_type(const CustomDataType type); +CustomDataType cpp_type_to_custom_data_type(const CPPType &type); +} // namespace blender::bke +#endif diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index 50e35c3c7c2..8fbab8dde25 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -54,55 +54,6 @@ std::ostream &operator<<(std::ostream &stream, const AttributeIDRef &attribute_i return stream; } -const blender::CPPType *custom_data_type_to_cpp_type(const CustomDataType type) -{ - switch (type) { - case CD_PROP_FLOAT: - return &CPPType::get<float>(); - case CD_PROP_FLOAT2: - return &CPPType::get<float2>(); - case CD_PROP_FLOAT3: - return &CPPType::get<float3>(); - case CD_PROP_INT32: - return &CPPType::get<int>(); - case CD_PROP_COLOR: - return &CPPType::get<ColorGeometry4f>(); - case CD_PROP_BOOL: - return &CPPType::get<bool>(); - case CD_PROP_INT8: - return &CPPType::get<int8_t>(); - default: - return nullptr; - } - return nullptr; -} - -CustomDataType cpp_type_to_custom_data_type(const blender::CPPType &type) -{ - if (type.is<float>()) { - return CD_PROP_FLOAT; - } - if (type.is<float2>()) { - return CD_PROP_FLOAT2; - } - if (type.is<float3>()) { - return CD_PROP_FLOAT3; - } - if (type.is<int>()) { - return CD_PROP_INT32; - } - if (type.is<ColorGeometry4f>()) { - return CD_PROP_COLOR; - } - if (type.is<bool>()) { - return CD_PROP_BOOL; - } - if (type.is<int8_t>()) { - return CD_PROP_INT8; - } - return static_cast<CustomDataType>(-1); -} - static int attribute_data_type_complexity(const CustomDataType data_type) { switch (data_type) { diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index b348e18a6a8..251d73dc315 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -18,9 +18,11 @@ #include "DNA_meshdata_types.h" #include "BLI_bitmap.h" +#include "BLI_color.hh" #include "BLI_endian_switch.h" #include "BLI_math.h" #include "BLI_math_color_blend.h" +#include "BLI_math_vector.hh" #include "BLI_mempool.h" #include "BLI_path_util.h" #include "BLI_string.h" @@ -5234,3 +5236,56 @@ void CustomData_debug_info_from_layers(const CustomData *data, const char *inden } #endif /* NDEBUG */ + +namespace blender::bke { + +const blender::CPPType *custom_data_type_to_cpp_type(const CustomDataType type) +{ + switch (type) { + case CD_PROP_FLOAT: + return &CPPType::get<float>(); + case CD_PROP_FLOAT2: + return &CPPType::get<float2>(); + case CD_PROP_FLOAT3: + return &CPPType::get<float3>(); + case CD_PROP_INT32: + return &CPPType::get<int>(); + case CD_PROP_COLOR: + return &CPPType::get<ColorGeometry4f>(); + case CD_PROP_BOOL: + return &CPPType::get<bool>(); + case CD_PROP_INT8: + return &CPPType::get<int8_t>(); + default: + return nullptr; + } + return nullptr; +} + +CustomDataType cpp_type_to_custom_data_type(const blender::CPPType &type) +{ + if (type.is<float>()) { + return CD_PROP_FLOAT; + } + if (type.is<float2>()) { + return CD_PROP_FLOAT2; + } + if (type.is<float3>()) { + return CD_PROP_FLOAT3; + } + if (type.is<int>()) { + return CD_PROP_INT32; + } + if (type.is<ColorGeometry4f>()) { + return CD_PROP_COLOR; + } + if (type.is<bool>()) { + return CD_PROP_BOOL; + } + if (type.is<int8_t>()) { + return CD_PROP_INT8; + } + return static_cast<CustomDataType>(-1); +} + +} // namespace blender::bke diff --git a/source/blender/blenlib/BLI_cpp_type.hh b/source/blender/blenlib/BLI_cpp_type.hh index ae6a87b4b68..881408f460b 100644 --- a/source/blender/blenlib/BLI_cpp_type.hh +++ b/source/blender/blenlib/BLI_cpp_type.hh @@ -22,6 +22,7 @@ * - If the code is not performance sensitive, it usually makes sense to use #CPPType instead. * - Sometimes a combination can make sense. Optimized code can be be generated at compile-time for * some types, while there is a fallback code path using #CPPType for all other types. + * #CPPType::to_static_type allows dispatching between both versions based on the type. * * Under some circumstances, #CPPType serves a similar role as #std::type_info. However, #CPPType * has much more utility because it contains methods for actually working with instances of the @@ -71,6 +72,7 @@ #include "BLI_hash.hh" #include "BLI_index_mask.hh" +#include "BLI_map.hh" #include "BLI_math_base.h" #include "BLI_string_ref.hh" #include "BLI_utility_mixins.hh" @@ -643,6 +645,77 @@ class CPPType : NonCopyable, NonMovable { { return this == &CPPType::get<std::decay_t<T>>(); } + + /** + * Convert a #CPPType that is only known at run-time, to a static type that is known at + * compile-time. This allows the compiler to optimize a function for specific types, while all + * other types can still use a generic fallback function. + * + * \param Types The types that code should be generated for. + * \param fn The function object to call. This is expected to have a templated `operator()` and a + * non-templated `operator()`. The templated version will be called if the current #CPPType + * matches any of the given types. Otherwise, the non-templated function is called. + */ + template<typename... Types, typename Fn> void to_static_type(const Fn &fn) const + { + using Callback = void (*)(const Fn &fn); + + /* Build a lookup table to avoid having to compare the current #CPPType with every type in + * #Types one after another. */ + static const Map<const CPPType *, Callback> callback_map = []() { + Map<const CPPType *, Callback> callback_map; + /* This adds an entry in the map for every type in #Types. */ + (callback_map.add_new(&CPPType::get<Types>(), + [](const Fn &fn) { + /* Call the templated `operator()` of the given function object. */ + fn.template operator()<Types>(); + }), + ...); + return callback_map; + }(); + + const Callback callback = callback_map.lookup_default(this, nullptr); + if (callback != nullptr) { + callback(fn); + } + else { + /* Call the non-templated `operator()` of the given function object. */ + fn(); + } + } + + template<typename T> struct type_tag { + using type = T; + }; + + private: + template<typename Fn> struct TypeTagExecutor { + const Fn &fn; + + template<typename T> void operator()() const + { + fn(type_tag<T>{}); + } + + void operator()() const + { + fn(type_tag<void>{}); + } + }; + + public: + /** + * Similar to #to_static_type but is easier to use with a lambda function. The function is + * expected to take a single `auto type_tag` parameter. To extract the static type, use: + * `using T = typename decltype(type_tag)::type;` + * + * If the current #CPPType is not in #Types, the type tag is `void`. + */ + template<typename... Types, typename Fn> void to_static_type_tag(const Fn &fn) const + { + TypeTagExecutor<Fn> executor{fn}; + this->to_static_type<Types...>(executor); + } }; } // namespace blender diff --git a/source/blender/blenlib/intern/cpp_type.cc b/source/blender/blenlib/intern/cpp_type.cc index 72ccc54e552..d6a087cf175 100644 --- a/source/blender/blenlib/intern/cpp_type.cc +++ b/source/blender/blenlib/intern/cpp_type.cc @@ -12,10 +12,15 @@ BLI_CPP_TYPE_MAKE(float2, blender::float2, CPPTypeFlags::BasicType) BLI_CPP_TYPE_MAKE(float3, blender::float3, CPPTypeFlags::BasicType) BLI_CPP_TYPE_MAKE(float4x4, blender::float4x4, CPPTypeFlags::BasicType) -BLI_CPP_TYPE_MAKE(int32, int32_t, CPPTypeFlags::BasicType) BLI_CPP_TYPE_MAKE(int8, int8_t, CPPTypeFlags::BasicType) -BLI_CPP_TYPE_MAKE(uint32, uint32_t, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(int16, int16_t, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(int32, int32_t, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(int64, int64_t, CPPTypeFlags::BasicType) + BLI_CPP_TYPE_MAKE(uint8, uint8_t, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(uint16, uint16_t, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(uint32, uint32_t, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(uint64, uint64_t, CPPTypeFlags::BasicType) BLI_CPP_TYPE_MAKE(ColorGeometry4f, blender::ColorGeometry4f, CPPTypeFlags::BasicType) BLI_CPP_TYPE_MAKE(ColorGeometry4b, blender::ColorGeometry4b, CPPTypeFlags::BasicType) diff --git a/source/blender/blenlib/tests/BLI_cpp_type_test.cc b/source/blender/blenlib/tests/BLI_cpp_type_test.cc index c7e6ed47b24..94456e1ee28 100644 --- a/source/blender/blenlib/tests/BLI_cpp_type_test.cc +++ b/source/blender/blenlib/tests/BLI_cpp_type_test.cc @@ -323,4 +323,28 @@ TEST(cpp_type, DebugPrint) EXPECT_EQ(text, "42"); } +TEST(cpp_type, ToStaticType) +{ + Vector<const CPPType *> types; + bool found_unsupported_type = false; + auto fn = [&](auto type_tag) { + using T = typename decltype(type_tag)::type; + if constexpr (!std::is_same_v<T, void>) { + types.append(&CPPType::get<T>()); + } + else { + found_unsupported_type = true; + } + }; + CPPType::get<std::string>().to_static_type_tag<int, float, std::string>(fn); + CPPType::get<float>().to_static_type_tag<int, float, std::string>(fn); + EXPECT_FALSE(found_unsupported_type); + CPPType::get<int64_t>().to_static_type_tag<int, float, std::string>(fn); + EXPECT_TRUE(found_unsupported_type); + + EXPECT_EQ(types.size(), 2); + EXPECT_EQ(types[0], &CPPType::get<std::string>()); + EXPECT_EQ(types[1], &CPPType::get<float>()); +} + } // namespace blender::tests |