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.
This commit is contained in:
parent
3e16f3b3ef
commit
8711483632
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Func>
|
||||
inline void convert_to_static_type(const CPPType &cpp_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();
|
||||
}
|
||||
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 CustomDataType data_type, const Func &func)
|
||||
{
|
||||
const CPPType &cpp_type = *bke::custom_data_type_to_cpp_type(data_type);
|
||||
convert_to_static_type(cpp_type, func);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue