Changeset View
Changeset View
Standalone View
Standalone View
source/blender/blenlib/BLI_cpp_type.hh
| Show First 20 Lines • Show All 65 Lines • ▼ Show 20 Lines | |||||
| * one necessary indirection using function pointers adds a lot of overhead. If all methods were | * one necessary indirection using function pointers adds a lot of overhead. If all methods were | ||||
| * virtual, there would be a second level of indirection that increases the overhead even more. | * virtual, there would be a second level of indirection that increases the overhead even more. | ||||
| * - If it becomes necessary, we could pass the function pointers to C functions more easily than | * - If it becomes necessary, we could pass the function pointers to C functions more easily than | ||||
| * pointers to virtual member functions. | * pointers to virtual member functions. | ||||
| */ | */ | ||||
| #include "BLI_hash.hh" | #include "BLI_hash.hh" | ||||
| #include "BLI_index_mask.hh" | #include "BLI_index_mask.hh" | ||||
| #include "BLI_map.hh" | |||||
| #include "BLI_math_base.h" | #include "BLI_math_base.h" | ||||
| #include "BLI_string_ref.hh" | #include "BLI_string_ref.hh" | ||||
| #include "BLI_utility_mixins.hh" | #include "BLI_utility_mixins.hh" | ||||
| /** | /** | ||||
| * Different types support different features. Features like copy constructability can be detected | * Different types support different features. Features like copy constructability can be detected | ||||
| * automatically easily. For some features this is harder as of C++17. Those have flags in this | * automatically easily. For some features this is harder as of C++17. Those have flags in this | ||||
| * enum and need to be determined by the programmer. | * enum and need to be determined by the programmer. | ||||
| ▲ Show 20 Lines • Show All 556 Lines • ▼ Show 20 Lines | public: | ||||
| { | { | ||||
| return destruct_; | return destruct_; | ||||
| } | } | ||||
| template<typename T> bool is() const | template<typename T> bool is() const | ||||
| { | { | ||||
| return this == &CPPType::get<std::decay_t<T>>(); | 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 | } // namespace blender | ||||
| /* Utility for allocating an uninitialized buffer for a single value of the given #CPPType. */ | /* Utility for allocating an uninitialized buffer for a single value of the given #CPPType. */ | ||||
| #define BUFFER_FOR_CPP_TYPE_VALUE(type, variable_name) \ | #define BUFFER_FOR_CPP_TYPE_VALUE(type, variable_name) \ | ||||
| blender::DynamicStackBuffer<64, 64> stack_buffer_for_##variable_name((type).size(), \ | blender::DynamicStackBuffer<64, 64> stack_buffer_for_##variable_name((type).size(), \ | ||||
| (type).alignment()); \ | (type).alignment()); \ | ||||
| void *variable_name = stack_buffer_for_##variable_name.buffer(); | void *variable_name = stack_buffer_for_##variable_name.buffer(); | ||||