diff options
author | Jacques Lucke <jacques@blender.org> | 2022-11-12 20:33:31 +0300 |
---|---|---|
committer | Jacques Lucke <jacques@blender.org> | 2022-11-12 20:33:31 +0300 |
commit | a6c822733ac7da4921297804475adadd50c08ed9 (patch) | |
tree | b34cc2924d7effa33d9ca9abfa026c8917fb953a | |
parent | a145b96396c4d2b8dd5ae027c676e838df0bd701 (diff) |
BLI: improve CPPType system
* Support bidirectional type lookups. E.g. finding the base type of a
field was supported, but not the other way around. This also removes
the todo in `get_vector_type`. To achieve this, types have to be
registered up-front.
* Separate `CPPType` from other "type traits". For example, previously
`ValueOrFieldCPPType` adds additional behavior on top of `CPPType`.
Previously, it was a subclass, now it just contains a reference to the
`CPPType` it corresponds to. This follows the composition-over-inheritance
idea. This makes it easier to have self-contained "type traits" without
having to put everything into `CPPType`.
Differential Revision: https://developer.blender.org/D16479
27 files changed, 476 insertions, 209 deletions
diff --git a/source/blender/blenkernel/BKE_cpp_types.h b/source/blender/blenkernel/BKE_cpp_types.h new file mode 100644 index 00000000000..7f103f563c3 --- /dev/null +++ b/source/blender/blenkernel/BKE_cpp_types.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Register cpp types and their relations for later use. + */ +void BKE_cpp_types_init(void); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 462ccc19601..bb34ba3f135 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -102,6 +102,7 @@ set(SRC intern/compute_contexts.cc intern/constraint.c intern/context.c + intern/cpp_types.cc intern/crazyspace.cc intern/cryptomatte.cc intern/curve.cc @@ -354,6 +355,7 @@ set(SRC BKE_compute_contexts.hh BKE_constraint.h BKE_context.h + BKE_cpp_types.h BKE_crazyspace.h BKE_crazyspace.hh BKE_cryptomatte.h diff --git a/source/blender/blenkernel/intern/cpp_types.cc b/source/blender/blenkernel/intern/cpp_types.cc new file mode 100644 index 00000000000..e6b33dd5b1a --- /dev/null +++ b/source/blender/blenkernel/intern/cpp_types.cc @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_cpp_type_make.hh" +#include "BLI_cpp_types_make.hh" + +#include "BKE_cpp_types.h" +#include "BKE_geometry_set.hh" +#include "BKE_instances.hh" + +#include "DNA_meshdata_types.h" + +#include "FN_init.h" + +struct Tex; +struct Image; +struct Material; + +BLI_CPP_TYPE_MAKE(GeometrySet, CPPTypeFlags::Printable); +BLI_CPP_TYPE_MAKE(blender::bke::InstanceReference, CPPTypeFlags::None) + +BLI_VECTOR_CPP_TYPE_MAKE(GeometrySet); + +BLI_CPP_TYPE_MAKE(Object *, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(Collection *, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(Tex *, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(Image *, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(Material *, CPPTypeFlags::BasicType) + +BLI_CPP_TYPE_MAKE(MStringProperty, CPPTypeFlags::None); + +void BKE_cpp_types_init() +{ + blender::register_cpp_types(); + FN_register_cpp_types(); + + BLI_CPP_TYPE_REGISTER(GeometrySet); + BLI_CPP_TYPE_REGISTER(blender::bke::InstanceReference); + + BLI_VECTOR_CPP_TYPE_REGISTER(GeometrySet); + + BLI_CPP_TYPE_REGISTER(Object *); + BLI_CPP_TYPE_REGISTER(Collection *); + BLI_CPP_TYPE_REGISTER(Tex *); + BLI_CPP_TYPE_REGISTER(Image *); + BLI_CPP_TYPE_REGISTER(Material *); + + BLI_CPP_TYPE_REGISTER(MStringProperty); +} diff --git a/source/blender/blenkernel/intern/customdata.cc b/source/blender/blenkernel/intern/customdata.cc index bccb625feb2..c8d7c4f2fdc 100644 --- a/source/blender/blenkernel/intern/customdata.cc +++ b/source/blender/blenkernel/intern/customdata.cc @@ -19,7 +19,6 @@ #include "BLI_bitmap.h" #include "BLI_color.hh" -#include "BLI_cpp_type_make.hh" #include "BLI_endian_switch.h" #include "BLI_index_range.hh" #include "BLI_math.h" @@ -5407,5 +5406,3 @@ size_t CustomData_get_elem_size(CustomDataLayer *layer) { return LAYERTYPEINFO[layer->type].size; } - -BLI_CPP_TYPE_MAKE(MStringProperty, MStringProperty, CPPTypeFlags::None); diff --git a/source/blender/blenkernel/intern/instances.cc b/source/blender/blenkernel/intern/instances.cc index 4675562e927..a2c344e918b 100644 --- a/source/blender/blenkernel/intern/instances.cc +++ b/source/blender/blenkernel/intern/instances.cc @@ -1,7 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ #include "BLI_array_utils.hh" -#include "BLI_cpp_type_make.hh" #include "BLI_rand.hh" #include "BLI_task.hh" @@ -9,8 +8,6 @@ #include "BKE_geometry_set.hh" #include "BKE_instances.hh" -BLI_CPP_TYPE_MAKE(InstanceReference, blender::bke::InstanceReference, CPPTypeFlags::None) - namespace blender::bke { InstanceReference::InstanceReference(GeometrySet geometry_set) diff --git a/source/blender/blenlib/BLI_cpp_type.hh b/source/blender/blenlib/BLI_cpp_type.hh index 568ccbb5a64..2f61583589b 100644 --- a/source/blender/blenlib/BLI_cpp_type.hh +++ b/source/blender/blenlib/BLI_cpp_type.hh @@ -74,6 +74,7 @@ #include "BLI_index_mask.hh" #include "BLI_map.hh" #include "BLI_math_base.h" +#include "BLI_parameter_pack_utils.hh" #include "BLI_string_ref.hh" #include "BLI_utility_mixins.hh" @@ -94,10 +95,6 @@ ENUM_OPERATORS(CPPTypeFlags, CPPTypeFlags::EqualityComparable) namespace blender { -/** Utility class to pass template parameters to constructor of `CPPType`. */ -template<typename T, CPPTypeFlags Flags> struct CPPTypeParam { -}; - class CPPType : NonCopyable, NonMovable { private: int64_t size_ = 0; @@ -148,7 +145,8 @@ class CPPType : NonCopyable, NonMovable { std::string debug_name_; public: - template<typename T, CPPTypeFlags Flags> CPPType(CPPTypeParam<T, Flags>, StringRef debug_name); + template<typename T, CPPTypeFlags Flags> + CPPType(TypeTag<T> /*type*/, TypeForValue<CPPTypeFlags, Flags> /*flags*/, StringRef debug_name); virtual ~CPPType() = default; /** @@ -173,7 +171,7 @@ class CPPType : NonCopyable, NonMovable { template<typename T> static const CPPType &get() { /* Store the #CPPType locally to avoid making the function call in most cases. */ - static const CPPType &type = CPPType::get_impl<std::remove_cv_t<T>>(); + static const CPPType &type = CPPType::get_impl<std::decay_t<T>>(); return type; } template<typename T> static const CPPType &get_impl(); @@ -745,30 +743,26 @@ class CPPType : NonCopyable, NonMovable { } } - 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>{}); + fn(TypeTag<T>{}); } void operator()() const { - fn(type_tag<void>{}); + fn(TypeTag<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;` + * expected to take a single `auto TypeTag` parameter. To extract the static type, use: + * `using T = typename decltype(TypeTag)::type;` * * If the current #CPPType is not in #Types, the type tag is `void`. */ @@ -779,6 +773,11 @@ class CPPType : NonCopyable, NonMovable { } }; +/** + * Initialize and register basic cpp types. + */ +void register_cpp_types(); + } // namespace blender /* Utility for allocating an uninitialized buffer for a single value of the given #CPPType. */ diff --git a/source/blender/blenlib/BLI_cpp_type_make.hh b/source/blender/blenlib/BLI_cpp_type_make.hh index 1f494624821..725e73dbb5d 100644 --- a/source/blender/blenlib/BLI_cpp_type_make.hh +++ b/source/blender/blenlib/BLI_cpp_type_make.hh @@ -206,7 +206,9 @@ template<typename T> uint64_t hash_cb(const void *value) namespace blender { template<typename T, CPPTypeFlags Flags> -CPPType::CPPType(CPPTypeParam<T, Flags> /* unused */, StringRef debug_name) +CPPType::CPPType(TypeTag<T> /*type*/, + TypeForValue<CPPTypeFlags, Flags> /*flags*/, + const StringRef debug_name) { using namespace cpp_type_util; @@ -278,9 +280,15 @@ CPPType::CPPType(CPPTypeParam<T, Flags> /* unused */, StringRef debug_name) } // namespace blender -#define BLI_CPP_TYPE_MAKE(IDENTIFIER, TYPE_NAME, FLAGS) \ +/** Create a new #CPPType that can be accessed through `CPPType::get<T>()`. */ +#define BLI_CPP_TYPE_MAKE(TYPE_NAME, FLAGS) \ template<> const blender::CPPType &blender::CPPType::get_impl<TYPE_NAME>() \ { \ - static CPPType cpp_type{blender::CPPTypeParam<TYPE_NAME, FLAGS>(), STRINGIFY(IDENTIFIER)}; \ - return cpp_type; \ + static CPPType type{blender::TypeTag<TYPE_NAME>(), \ + TypeForValue<CPPTypeFlags, FLAGS>(), \ + STRINGIFY(TYPE_NAME)}; \ + return type; \ } + +/** Register a #CPPType created with #BLI_CPP_TYPE_MAKE. */ +#define BLI_CPP_TYPE_REGISTER(TYPE_NAME) blender::CPPType::get<TYPE_NAME>() diff --git a/source/blender/blenlib/BLI_cpp_types.hh b/source/blender/blenlib/BLI_cpp_types.hh new file mode 100644 index 00000000000..596090b95e5 --- /dev/null +++ b/source/blender/blenlib/BLI_cpp_types.hh @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_cpp_type.hh" +#include "BLI_vector.hh" + +namespace blender { + +/** + * Contains information about how to deal with a #Vector<T> generically. + */ +class VectorCPPType { + public: + /** The #Vector<T> itself. */ + const CPPType &self; + /** The type stored in the vector. */ + const CPPType &value; + + template<typename ValueType> VectorCPPType(TypeTag<ValueType> /*value_type*/); + + /** + * Try to find the #VectorCPPType that corresponds to a #CPPType. + */ + static const VectorCPPType *get_from_self(const CPPType &self); + /** + * Try to find the #VectorCPPType that wraps a vector containing the given value type. + * This only works when the vector type has been created with #BLI_VECTOR_CPP_TYPE_MAKE. + */ + static const VectorCPPType *get_from_value(const CPPType &value); + + template<typename ValueType> static const VectorCPPType &get() + { + static const VectorCPPType &type = VectorCPPType::get_impl<std::decay_t<ValueType>>(); + return type; + } + + template<typename ValueType> static const VectorCPPType &get_impl(); + + private: + void register_self(); +}; + +} // namespace blender diff --git a/source/blender/blenlib/BLI_cpp_types_make.hh b/source/blender/blenlib/BLI_cpp_types_make.hh new file mode 100644 index 00000000000..ba729c4cc69 --- /dev/null +++ b/source/blender/blenlib/BLI_cpp_types_make.hh @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_cpp_type_make.hh" +#include "BLI_cpp_types.hh" + +namespace blender { + +template<typename ValueType> +inline VectorCPPType::VectorCPPType(TypeTag<ValueType> /*value_type*/) + : self(CPPType::get<Vector<ValueType>>()), value(CPPType::get<ValueType>()) +{ + this->register_self(); +} + +} // namespace blender + +/** Create a new #VectorCPPType that can be accessed through `VectorCPPType::get<T>()`. */ +#define BLI_VECTOR_CPP_TYPE_MAKE(VALUE_TYPE) \ + BLI_CPP_TYPE_MAKE(blender::Vector<VALUE_TYPE>, CPPTypeFlags::None) \ + template<> const blender::VectorCPPType &blender::VectorCPPType::get_impl<VALUE_TYPE>() \ + { \ + static blender::VectorCPPType type{blender::TypeTag<VALUE_TYPE>{}}; \ + return type; \ + } + +/** Register a #VectorCPPType created with #BLI_VECTOR_CPP_TYPE_MAKE. */ +#define BLI_VECTOR_CPP_TYPE_REGISTER(VALUE_TYPE) blender::VectorCPPType::get<VALUE_TYPE>() diff --git a/source/blender/blenlib/BLI_parameter_pack_utils.hh b/source/blender/blenlib/BLI_parameter_pack_utils.hh index d1ef7bcbc65..36ceea1f4b8 100644 --- a/source/blender/blenlib/BLI_parameter_pack_utils.hh +++ b/source/blender/blenlib/BLI_parameter_pack_utils.hh @@ -24,6 +24,13 @@ template<typename T, T Element> struct TypeForValue { }; /** + * A struct that allows passing in a type as a function parameter. + */ +template<typename T> struct TypeTag { + using type = T; +}; + +/** * A type that encodes a list of values of the same type. * This is similar to #std::integer_sequence, but a bit more general. It's main purpose it to also * support enums instead of just ints. diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index 693a4d98675..782056615be 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -57,7 +57,7 @@ set(SRC intern/cache_mutex.cc intern/compute_context.cc intern/convexhull_2d.c - intern/cpp_type.cc + intern/cpp_types.cc intern/delaunay_2d.cc intern/dot_export.cc intern/dynlib.c @@ -190,6 +190,8 @@ set(SRC BLI_convexhull_2d.h BLI_cpp_type.hh BLI_cpp_type_make.hh + BLI_cpp_types.hh + BLI_cpp_types_make.hh BLI_delaunay_2d.h BLI_devirtualize_parameters.hh BLI_dial_2d.h diff --git a/source/blender/blenlib/intern/cpp_type.cc b/source/blender/blenlib/intern/cpp_type.cc deleted file mode 100644 index 38de32d3ec8..00000000000 --- a/source/blender/blenlib/intern/cpp_type.cc +++ /dev/null @@ -1,29 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "BLI_color.hh" -#include "BLI_cpp_type_make.hh" -#include "BLI_float4x4.hh" -#include "BLI_math_vec_types.hh" - -BLI_CPP_TYPE_MAKE(bool, bool, CPPTypeFlags::BasicType) - -BLI_CPP_TYPE_MAKE(float, float, CPPTypeFlags::BasicType) -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(int8, int8_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) - -BLI_CPP_TYPE_MAKE(string, std::string, CPPTypeFlags::BasicType) -BLI_CPP_TYPE_MAKE(StringVector, blender::Vector<std::string>, CPPTypeFlags::None) diff --git a/source/blender/blenlib/intern/cpp_types.cc b/source/blender/blenlib/intern/cpp_types.cc new file mode 100644 index 00000000000..eb8f03d942f --- /dev/null +++ b/source/blender/blenlib/intern/cpp_types.cc @@ -0,0 +1,98 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "BLI_color.hh" +#include "BLI_cpp_type_make.hh" +#include "BLI_cpp_types_make.hh" +#include "BLI_float4x4.hh" +#include "BLI_math_vec_types.hh" + +namespace blender { + +static auto &get_vector_from_self_map() +{ + static Map<const CPPType *, const VectorCPPType *> map; + return map; +} + +static auto &get_vector_from_value_map() +{ + static Map<const CPPType *, const VectorCPPType *> map; + return map; +} + +void VectorCPPType::register_self() +{ + get_vector_from_self_map().add_new(&this->self, this); + get_vector_from_value_map().add_new(&this->value, this); +} + +const VectorCPPType *VectorCPPType::get_from_self(const CPPType &self) +{ + const VectorCPPType *type = get_vector_from_self_map().lookup_default(&self, nullptr); + BLI_assert(type == nullptr || type->self == self); + return type; +} + +const VectorCPPType *VectorCPPType::get_from_value(const CPPType &value) +{ + const VectorCPPType *type = get_vector_from_value_map().lookup_default(&value, nullptr); + BLI_assert(type == nullptr || type->value == value); + return type; +} + +} // namespace blender + +BLI_CPP_TYPE_MAKE(bool, CPPTypeFlags::BasicType) + +BLI_CPP_TYPE_MAKE(float, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(blender::float2, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(blender::float3, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(blender::float4x4, CPPTypeFlags::BasicType) + +BLI_CPP_TYPE_MAKE(int8_t, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(int16_t, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(int32_t, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(int64_t, CPPTypeFlags::BasicType) + +BLI_CPP_TYPE_MAKE(uint8_t, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(uint16_t, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(uint32_t, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(uint64_t, CPPTypeFlags::BasicType) + +BLI_CPP_TYPE_MAKE(blender::ColorGeometry4f, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(blender::ColorGeometry4b, CPPTypeFlags::BasicType) + +BLI_CPP_TYPE_MAKE(std::string, CPPTypeFlags::BasicType) + +BLI_VECTOR_CPP_TYPE_MAKE(std::string) + +namespace blender { + +void register_cpp_types() +{ + BLI_CPP_TYPE_REGISTER(bool); + + BLI_CPP_TYPE_REGISTER(float); + BLI_CPP_TYPE_REGISTER(blender::float2); + BLI_CPP_TYPE_REGISTER(blender::float3); + BLI_CPP_TYPE_REGISTER(blender::float4x4); + + BLI_CPP_TYPE_REGISTER(int8_t); + BLI_CPP_TYPE_REGISTER(int16_t); + BLI_CPP_TYPE_REGISTER(int32_t); + BLI_CPP_TYPE_REGISTER(int64_t); + + BLI_CPP_TYPE_REGISTER(uint8_t); + BLI_CPP_TYPE_REGISTER(uint16_t); + BLI_CPP_TYPE_REGISTER(uint32_t); + BLI_CPP_TYPE_REGISTER(uint64_t); + + BLI_CPP_TYPE_REGISTER(blender::ColorGeometry4f); + BLI_CPP_TYPE_REGISTER(blender::ColorGeometry4b); + + BLI_CPP_TYPE_REGISTER(std::string); + + BLI_VECTOR_CPP_TYPE_REGISTER(std::string); +} + +} // namespace blender diff --git a/source/blender/blenlib/tests/BLI_cpp_type_test.cc b/source/blender/blenlib/tests/BLI_cpp_type_test.cc index 5823d54f51b..57077db53dd 100644 --- a/source/blender/blenlib/tests/BLI_cpp_type_test.cc +++ b/source/blender/blenlib/tests/BLI_cpp_type_test.cc @@ -76,7 +76,7 @@ struct TestType { } // namespace blender::tests -BLI_CPP_TYPE_MAKE(TestType, blender::tests::TestType, CPPTypeFlags::BasicType) +BLI_CPP_TYPE_MAKE(blender::tests::TestType, CPPTypeFlags::BasicType) namespace blender::tests { diff --git a/source/blender/functions/CMakeLists.txt b/source/blender/functions/CMakeLists.txt index 3d153813425..54f67122907 100644 --- a/source/blender/functions/CMakeLists.txt +++ b/source/blender/functions/CMakeLists.txt @@ -13,6 +13,7 @@ set(INC_SYS set(SRC intern/cpp_types.cc intern/field.cc + intern/field_cpp_type.cc intern/lazy_function.cc intern/lazy_function_execute.cc intern/lazy_function_graph.cc @@ -27,6 +28,8 @@ set(SRC FN_field.hh FN_field_cpp_type.hh + FN_field_cpp_type_make.hh + FN_init.h FN_lazy_function.hh FN_lazy_function_execute.hh FN_lazy_function_graph.hh diff --git a/source/blender/functions/FN_field_cpp_type.hh b/source/blender/functions/FN_field_cpp_type.hh index 6900a093dc6..cbb2a576272 100644 --- a/source/blender/functions/FN_field_cpp_type.hh +++ b/source/blender/functions/FN_field_cpp_type.hh @@ -6,49 +6,15 @@ * \ingroup fn */ -#include "BLI_cpp_type_make.hh" #include "FN_field.hh" namespace blender::fn { -template<typename T> struct FieldCPPTypeParam { -}; - -class FieldCPPType : public CPPType { - private: - const CPPType &base_type_; - - public: - template<typename T> - FieldCPPType(FieldCPPTypeParam<Field<T>> /* unused */, StringRef debug_name) - : CPPType(CPPTypeParam<Field<T>, CPPTypeFlags::None>(), debug_name), - base_type_(CPPType::get<T>()) - { - } - - const CPPType &base_type() const - { - return base_type_; - } - - /* Ensure that #GField and #Field<T> have the same layout, to enable casting between the two. */ - static_assert(sizeof(Field<int>) == sizeof(GField)); - static_assert(sizeof(Field<int>) == sizeof(Field<std::string>)); - - const GField &get_gfield(const void *field) const - { - return *(const GField *)field; - } - - void construct_from_gfield(void *r_value, const GField &gfield) const - { - new (r_value) GField(gfield); - } -}; - -class ValueOrFieldCPPType : public CPPType { +/** + * Contains information about how to deal with a `ValueOrField<T>` generically. + */ +class ValueOrFieldCPPType { private: - const CPPType &base_type_; void (*construct_from_value_)(void *dst, const void *value); void (*construct_from_field_)(void *dst, GField field); const void *(*get_value_ptr_)(const void *value_or_field); @@ -57,35 +23,12 @@ class ValueOrFieldCPPType : public CPPType { GField (*as_field_)(const void *value_or_field); public: - template<typename T> - ValueOrFieldCPPType(FieldCPPTypeParam<ValueOrField<T>> /* unused */, StringRef debug_name) - : CPPType(CPPTypeParam<ValueOrField<T>, CPPTypeFlags::Printable>(), debug_name), - base_type_(CPPType::get<T>()) - { - construct_from_value_ = [](void *dst, const void *value_or_field) { - new (dst) ValueOrField<T>(*(const T *)value_or_field); - }; - construct_from_field_ = [](void *dst, GField field) { - new (dst) ValueOrField<T>(Field<T>(std::move(field))); - }; - get_value_ptr_ = [](const void *value_or_field) { - return (const void *)&((ValueOrField<T> *)value_or_field)->value; - }; - get_field_ptr_ = [](const void *value_or_field) -> const GField * { - return &((ValueOrField<T> *)value_or_field)->field; - }; - is_field_ = [](const void *value_or_field) { - return ((ValueOrField<T> *)value_or_field)->is_field(); - }; - as_field_ = [](const void *value_or_field) -> GField { - return ((ValueOrField<T> *)value_or_field)->as_field(); - }; - } + /** The #ValueOrField<T> itself. */ + const CPPType &self; + /** The type stored in the field. */ + const CPPType &value; - const CPPType &base_type() const - { - return base_type_; - } + template<typename ValueType> ValueOrFieldCPPType(TypeTag<ValueType> /*value_type*/); void construct_from_value(void *dst, const void *value) const { @@ -122,22 +65,29 @@ class ValueOrFieldCPPType : public CPPType { { return as_field_(value_or_field); } -}; -} // namespace blender::fn + /** + * Try to find the #ValueOrFieldCPPType that corresponds to a #CPPType. + */ + static const ValueOrFieldCPPType *get_from_self(const CPPType &self); -#define MAKE_FIELD_CPP_TYPE(DEBUG_NAME, FIELD_TYPE) \ - template<> const blender::CPPType &blender::CPPType::get_impl<blender::fn::Field<FIELD_TYPE>>() \ - { \ - static blender::fn::FieldCPPType cpp_type{ \ - blender::fn::FieldCPPTypeParam<blender::fn::Field<FIELD_TYPE>>(), STRINGIFY(DEBUG_NAME)}; \ - return cpp_type; \ - } \ - template<> \ - const blender::CPPType &blender::CPPType::get_impl<blender::fn::ValueOrField<FIELD_TYPE>>() \ - { \ - static blender::fn::ValueOrFieldCPPType cpp_type{ \ - blender::fn::FieldCPPTypeParam<blender::fn::ValueOrField<FIELD_TYPE>>(), \ - STRINGIFY(DEBUG_NAME##OrValue)}; \ - return cpp_type; \ + /** + * Try to find the #ValueOrFieldCPPType that wraps a #ValueOrField containing the given value + * type. This only works when the type has been created with #FN_FIELD_CPP_TYPE_MAKE. + */ + static const ValueOrFieldCPPType *get_from_value(const CPPType &value); + + template<typename ValueType> static const ValueOrFieldCPPType &get() + { + static const ValueOrFieldCPPType &type = + ValueOrFieldCPPType::get_impl<std::decay_t<ValueType>>(); + return type; } + + private: + template<typename ValueType> static const ValueOrFieldCPPType &get_impl(); + + void register_self(); +}; + +} // namespace blender::fn diff --git a/source/blender/functions/FN_field_cpp_type_make.hh b/source/blender/functions/FN_field_cpp_type_make.hh new file mode 100644 index 00000000000..79e46faa9e7 --- /dev/null +++ b/source/blender/functions/FN_field_cpp_type_make.hh @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "FN_field_cpp_type.hh" + +namespace blender::fn { + +template<typename ValueType> +inline ValueOrFieldCPPType::ValueOrFieldCPPType(TypeTag<ValueType> /*value_type*/) + : self(CPPType::get<ValueOrField<ValueType>>()), value(CPPType::get<ValueType>()) +{ + using T = ValueType; + construct_from_value_ = [](void *dst, const void *value_or_field) { + new (dst) ValueOrField<T>(*(const T *)value_or_field); + }; + construct_from_field_ = [](void *dst, GField field) { + new (dst) ValueOrField<T>(Field<T>(std::move(field))); + }; + get_value_ptr_ = [](const void *value_or_field) { + return (const void *)&((ValueOrField<T> *)value_or_field)->value; + }; + get_field_ptr_ = [](const void *value_or_field) -> const GField * { + return &((ValueOrField<T> *)value_or_field)->field; + }; + is_field_ = [](const void *value_or_field) { + return ((ValueOrField<T> *)value_or_field)->is_field(); + }; + as_field_ = [](const void *value_or_field) -> GField { + return ((ValueOrField<T> *)value_or_field)->as_field(); + }; + this->register_self(); +} + +} // namespace blender::fn + +/** + * Create a new #ValueOrFieldCPPType that can be accessed through `ValueOrFieldCPPType::get<T>()`. + */ +#define FN_FIELD_CPP_TYPE_MAKE(VALUE_TYPE) \ + BLI_CPP_TYPE_MAKE(blender::fn::ValueOrField<VALUE_TYPE>, CPPTypeFlags::None) \ + template<> \ + const blender::fn::ValueOrFieldCPPType & \ + blender::fn::ValueOrFieldCPPType::get_impl<VALUE_TYPE>() \ + { \ + static blender::fn::ValueOrFieldCPPType type{blender::TypeTag<VALUE_TYPE>{}}; \ + return type; \ + } + +/** Register a #ValueOrFieldCPPType created with #FN_FIELD_CPP_TYPE_MAKE. */ +#define FN_FIELD_CPP_TYPE_REGISTER(VALUE_TYPE) blender::fn::ValueOrFieldCPPType::get<VALUE_TYPE>() diff --git a/source/blender/functions/FN_init.h b/source/blender/functions/FN_init.h new file mode 100644 index 00000000000..e8c9fbce57e --- /dev/null +++ b/source/blender/functions/FN_init.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +void FN_register_cpp_types(void); + +#ifdef __cplusplus +} +#endif diff --git a/source/blender/functions/intern/cpp_types.cc b/source/blender/functions/intern/cpp_types.cc index f046da30994..098e7030de4 100644 --- a/source/blender/functions/intern/cpp_types.cc +++ b/source/blender/functions/intern/cpp_types.cc @@ -2,20 +2,36 @@ #include "BLI_color.hh" #include "BLI_cpp_type_make.hh" +#include "BLI_cpp_types_make.hh" #include "BLI_float4x4.hh" #include "BLI_math_vec_types.hh" -#include "FN_field_cpp_type.hh" +#include "FN_field_cpp_type_make.hh" +#include "FN_init.h" -MAKE_FIELD_CPP_TYPE(FloatField, float); -MAKE_FIELD_CPP_TYPE(Float2Field, blender::float2); -MAKE_FIELD_CPP_TYPE(Float3Field, blender::float3); -MAKE_FIELD_CPP_TYPE(ColorGeometry4fField, blender::ColorGeometry4f); -MAKE_FIELD_CPP_TYPE(ColorGeometry4bField, blender::ColorGeometry4b); -MAKE_FIELD_CPP_TYPE(BoolField, bool); -MAKE_FIELD_CPP_TYPE(Int8Field, int8_t); -MAKE_FIELD_CPP_TYPE(Int32Field, int32_t); -MAKE_FIELD_CPP_TYPE(StringField, std::string); -BLI_CPP_TYPE_MAKE(StringValueOrFieldVector, - blender::Vector<blender::fn::ValueOrField<std::string>>, - CPPTypeFlags::None); +FN_FIELD_CPP_TYPE_MAKE(float); +FN_FIELD_CPP_TYPE_MAKE(blender::float2); +FN_FIELD_CPP_TYPE_MAKE(blender::float3); +FN_FIELD_CPP_TYPE_MAKE(blender::ColorGeometry4f); +FN_FIELD_CPP_TYPE_MAKE(blender::ColorGeometry4b); +FN_FIELD_CPP_TYPE_MAKE(bool); +FN_FIELD_CPP_TYPE_MAKE(int8_t); +FN_FIELD_CPP_TYPE_MAKE(int32_t); +FN_FIELD_CPP_TYPE_MAKE(std::string); + +BLI_VECTOR_CPP_TYPE_MAKE(blender::fn::ValueOrField<std::string>); + +void FN_register_cpp_types() +{ + FN_FIELD_CPP_TYPE_REGISTER(float); + FN_FIELD_CPP_TYPE_REGISTER(blender::float2); + FN_FIELD_CPP_TYPE_REGISTER(blender::float3); + FN_FIELD_CPP_TYPE_REGISTER(blender::ColorGeometry4f); + FN_FIELD_CPP_TYPE_REGISTER(blender::ColorGeometry4b); + FN_FIELD_CPP_TYPE_REGISTER(bool); + FN_FIELD_CPP_TYPE_REGISTER(int8_t); + FN_FIELD_CPP_TYPE_REGISTER(int32_t); + FN_FIELD_CPP_TYPE_REGISTER(std::string); + + BLI_VECTOR_CPP_TYPE_REGISTER(blender::fn::ValueOrField<std::string>); +} diff --git a/source/blender/functions/intern/field_cpp_type.cc b/source/blender/functions/intern/field_cpp_type.cc new file mode 100644 index 00000000000..9057bbb887a --- /dev/null +++ b/source/blender/functions/intern/field_cpp_type.cc @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "FN_field_cpp_type.hh" + +namespace blender::fn { + +static auto &get_from_self_map() +{ + static Map<const CPPType *, const ValueOrFieldCPPType *> map; + return map; +} + +static auto &get_from_value_map() +{ + static Map<const CPPType *, const ValueOrFieldCPPType *> map; + return map; +} + +void ValueOrFieldCPPType::register_self() +{ + get_from_value_map().add_new(&this->value, this); + get_from_self_map().add_new(&this->self, this); +} + +const ValueOrFieldCPPType *ValueOrFieldCPPType::get_from_self(const CPPType &self) +{ + const ValueOrFieldCPPType *type = get_from_self_map().lookup_default(&self, nullptr); + BLI_assert(type == nullptr || type->self == self); + return type; +} + +const ValueOrFieldCPPType *ValueOrFieldCPPType::get_from_value(const CPPType &value) +{ + const ValueOrFieldCPPType *type = get_from_value_map().lookup_default(&value, nullptr); + BLI_assert(type == nullptr || type->value == value); + return type; +} + +} // namespace blender::fn diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 15d7e494c04..13674c0f201 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -817,11 +817,10 @@ static void initialize_group_input(NodesModifierData &nmd, auto attribute_input = std::make_shared<blender::bke::AttributeFieldInput>( attribute_name, *socket_type.base_cpp_type); GField attribute_field{std::move(attribute_input), 0}; - const blender::fn::ValueOrFieldCPPType *cpp_type = - dynamic_cast<const blender::fn::ValueOrFieldCPPType *>( - socket_type.geometry_nodes_cpp_type); - BLI_assert(cpp_type != nullptr); - cpp_type->construct_from_field(r_value, std::move(attribute_field)); + const auto *value_or_field_cpp_type = ValueOrFieldCPPType::get_from_self( + *socket_type.geometry_nodes_cpp_type); + BLI_assert(value_or_field_cpp_type != nullptr); + value_or_field_cpp_type->construct_from_field(r_value, std::move(attribute_field)); } else { init_socket_cpp_value_from_property(*property, socket_data_type, r_value); @@ -984,9 +983,9 @@ static MultiValueMap<eAttrDomain, OutputAttributeInfo> find_output_attributes_to const int index = socket->index(); const GPointer value = output_values[index]; - const ValueOrFieldCPPType *cpp_type = dynamic_cast<const ValueOrFieldCPPType *>(value.type()); - BLI_assert(cpp_type != nullptr); - const GField field = cpp_type->as_field(value.get()); + const auto *value_or_field_type = ValueOrFieldCPPType::get_from_self(*value.type()); + BLI_assert(value_or_field_type != nullptr); + const GField field = value_or_field_type->as_field(value.get()); const bNodeSocket *interface_socket = (const bNodeSocket *)BLI_findlink( &nmd.node_group->outputs, index); diff --git a/source/blender/nodes/geometry/CMakeLists.txt b/source/blender/nodes/geometry/CMakeLists.txt index 2b8de906a77..930f9731258 100644 --- a/source/blender/nodes/geometry/CMakeLists.txt +++ b/source/blender/nodes/geometry/CMakeLists.txt @@ -173,7 +173,6 @@ set(SRC nodes/node_geo_volume_cube.cc nodes/node_geo_volume_to_mesh.cc - node_geometry_exec.cc node_geometry_tree.cc node_geometry_util.cc diff --git a/source/blender/nodes/geometry/node_geometry_exec.cc b/source/blender/nodes/geometry/node_geometry_exec.cc deleted file mode 100644 index ef4daf94bbe..00000000000 --- a/source/blender/nodes/geometry/node_geometry_exec.cc +++ /dev/null @@ -1,7 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ - -#include "BLI_cpp_type_make.hh" -#include "NOD_geometry_exec.hh" - -BLI_CPP_TYPE_MAKE(GeometrySet, GeometrySet, CPPTypeFlags::Printable); -BLI_CPP_TYPE_MAKE(GeometrySetVector, blender::Vector<GeometrySet>, CPPTypeFlags::None); diff --git a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc index 96c369f2f6b..d9e82c6b4ee 100644 --- a/source/blender/nodes/intern/geometry_nodes_lazy_function.cc +++ b/source/blender/nodes/intern/geometry_nodes_lazy_function.cc @@ -16,6 +16,7 @@ #include "NOD_multi_function.hh" #include "NOD_node_declaration.hh" +#include "BLI_cpp_types.hh" #include "BLI_lazy_threading.hh" #include "BLI_map.hh" @@ -53,14 +54,11 @@ static const CPPType *get_socket_cpp_type(const bNodeSocket &socket) static const CPPType *get_vector_type(const CPPType &type) { - /* This could be generalized in the future. For now we only support a small set of vectors. */ - if (type.is<GeometrySet>()) { - return &CPPType::get<Vector<GeometrySet>>(); - } - if (type.is<ValueOrField<std::string>>()) { - return &CPPType::get<Vector<ValueOrField<std::string>>>(); + const VectorCPPType *vector_type = VectorCPPType::get_from_value(type); + if (vector_type == nullptr) { + return nullptr; } - return nullptr; + return &vector_type->self; } /** @@ -296,19 +294,17 @@ static void execute_multi_function_on_value_or_field( for (const int i : input_types.index_range()) { const ValueOrFieldCPPType &type = *input_types[i]; - const CPPType &base_type = type.base_type(); const void *value_or_field = input_values[i]; const void *value = type.get_value_ptr(value_or_field); - params.add_readonly_single_input(GVArray::ForSingleRef(base_type, 1, value)); + params.add_readonly_single_input(GVArray::ForSingleRef(type.value, 1, value)); } for (const int i : output_types.index_range()) { const ValueOrFieldCPPType &type = *output_types[i]; - const CPPType &base_type = type.base_type(); void *value_or_field = output_values[i]; - type.default_construct(value_or_field); + type.self.default_construct(value_or_field); void *value = type.get_value_ptr(value_or_field); - base_type.destruct(value); - params.add_uninitialized_single_output(GMutableSpan{base_type, value, 1}); + type.value.destruct(value); + params.add_uninitialized_single_output(GMutableSpan{type.value, value, 1}); } fn.call(IndexRange(1), params, context); } @@ -380,16 +376,14 @@ class LazyFunctionForMutedNode : public LazyFunction { } /* Perform a type conversion and then format the value. */ const bke::DataTypeConversions &conversions = bke::get_implicit_type_conversions(); - const auto *from_field_type = dynamic_cast<const ValueOrFieldCPPType *>(&input_type); - const auto *to_field_type = dynamic_cast<const ValueOrFieldCPPType *>(&output_type); - if (from_field_type != nullptr && to_field_type != nullptr) { - const CPPType &from_base_type = from_field_type->base_type(); - const CPPType &to_base_type = to_field_type->base_type(); - if (conversions.is_convertible(from_base_type, to_base_type)) { + const auto *from_type = ValueOrFieldCPPType::get_from_self(input_type); + const auto *to_type = ValueOrFieldCPPType::get_from_self(output_type); + if (from_type != nullptr && to_type != nullptr) { + if (conversions.is_convertible(from_type->value, to_type->value)) { const MultiFunction &multi_fn = *conversions.get_conversion_multi_function( - MFDataType::ForSingle(from_base_type), MFDataType::ForSingle(to_base_type)); + MFDataType::ForSingle(from_type->value), MFDataType::ForSingle(to_type->value)); execute_multi_function_on_value_or_field( - multi_fn, {}, {from_field_type}, {to_field_type}, {input_value}, {output_value}); + multi_fn, {}, {from_type}, {to_type}, {input_value}, {output_value}); } params.output_set(output_i); continue; @@ -420,8 +414,8 @@ class LazyFunctionForMultiFunctionConversion : public LazyFunction { : fn_(fn), from_type_(from), to_type_(to), target_sockets_(std::move(target_sockets)) { debug_name_ = "Convert"; - inputs_.append({"From", from}); - outputs_.append({"To", to}); + inputs_.append({"From", from.self}); + outputs_.append({"To", to.self}); } void execute_impl(lf::Params ¶ms, const lf::Context & /*context*/) const override @@ -458,10 +452,10 @@ class LazyFunctionForMultiFunctionNode : public LazyFunction { debug_name_ = node.name; lazy_function_interface_from_node(node, r_used_inputs, r_used_outputs, inputs_, outputs_); for (const lf::Input &fn_input : inputs_) { - input_types_.append(dynamic_cast<const ValueOrFieldCPPType *>(fn_input.type)); + input_types_.append(ValueOrFieldCPPType::get_from_self(*fn_input.type)); } for (const lf::Output &fn_output : outputs_) { - output_types_.append(dynamic_cast<const ValueOrFieldCPPType *>(fn_output.type)); + output_types_.append(ValueOrFieldCPPType::get_from_self(*fn_output.type)); } } @@ -552,8 +546,7 @@ class LazyFunctionForViewerNode : public LazyFunction { if (use_field_input_) { const void *value_or_field = params.try_get_input_data_ptr(1); BLI_assert(value_or_field != nullptr); - const ValueOrFieldCPPType &value_or_field_type = static_cast<const ValueOrFieldCPPType &>( - *inputs_[1].type); + const auto &value_or_field_type = *ValueOrFieldCPPType::get_from_self(*inputs_[1].type); GField field = value_or_field_type.as_field(value_or_field); const eAttrDomain domain = eAttrDomain(storage->domain); const StringRefNull viewer_attribute_name = ".viewer"; @@ -1193,14 +1186,13 @@ struct GeometryNodesLazyFunctionGraphBuilder { if (from_type == to_type) { return &from_socket; } - const auto *from_field_type = dynamic_cast<const ValueOrFieldCPPType *>(&from_type); - const auto *to_field_type = dynamic_cast<const ValueOrFieldCPPType *>(&to_type); + const auto *from_field_type = ValueOrFieldCPPType::get_from_self(from_type); + const auto *to_field_type = ValueOrFieldCPPType::get_from_self(to_type); if (from_field_type != nullptr && to_field_type != nullptr) { - const CPPType &from_base_type = from_field_type->base_type(); - const CPPType &to_base_type = to_field_type->base_type(); - if (conversions_->is_convertible(from_base_type, to_base_type)) { + if (conversions_->is_convertible(from_field_type->value, to_field_type->value)) { const MultiFunction &multi_fn = *conversions_->get_conversion_multi_function( - MFDataType::ForSingle(from_base_type), MFDataType::ForSingle(to_base_type)); + MFDataType::ForSingle(from_field_type->value), + MFDataType::ForSingle(to_field_type->value)); auto fn = std::make_unique<LazyFunctionForMultiFunctionConversion>( multi_fn, *from_field_type, *to_field_type, std::move(target_sockets)); lf::Node &conversion_node = lf_graph_->add_function(*fn); diff --git a/source/blender/nodes/intern/geometry_nodes_log.cc b/source/blender/nodes/intern/geometry_nodes_log.cc index 0f122307328..640296ead66 100644 --- a/source/blender/nodes/intern/geometry_nodes_log.cc +++ b/source/blender/nodes/intern/geometry_nodes_log.cc @@ -166,10 +166,9 @@ void GeoTreeLogger::log_value(const bNode &node, const bNodeSocket &socket, cons const GeometrySet &geometry = *value.get<GeometrySet>(); store_logged_value(this->allocator->construct<GeometryInfoLog>(geometry)); } - else if (const auto *value_or_field_type = dynamic_cast<const fn::ValueOrFieldCPPType *>( - &type)) { + else if (const auto *value_or_field_type = fn::ValueOrFieldCPPType::get_from_self(type)) { const void *value_or_field = value.get(); - const CPPType &base_type = value_or_field_type->base_type(); + const CPPType &base_type = value_or_field_type->value; if (value_or_field_type->is_field(value_or_field)) { const GField *field = value_or_field_type->get_field_ptr(value_or_field); if (field->node().depends_on_input()) { diff --git a/source/blender/nodes/intern/node_socket.cc b/source/blender/nodes/intern/node_socket.cc index f2f4519625a..6d02ed7d553 100644 --- a/source/blender/nodes/intern/node_socket.cc +++ b/source/blender/nodes/intern/node_socket.cc @@ -10,7 +10,6 @@ #include "DNA_node_types.h" #include "BLI_color.hh" -#include "BLI_cpp_type_make.hh" #include "BLI_listbase.h" #include "BLI_math_vec_types.hh" #include "BLI_string.h" @@ -779,12 +778,6 @@ static bNodeSocketType *make_socket_type_string() return socktype; } -BLI_CPP_TYPE_MAKE(Object, Object *, CPPTypeFlags::BasicType) -BLI_CPP_TYPE_MAKE(Collection, Collection *, CPPTypeFlags::BasicType) -BLI_CPP_TYPE_MAKE(Texture, Tex *, CPPTypeFlags::BasicType) -BLI_CPP_TYPE_MAKE(Image, Image *, CPPTypeFlags::BasicType) -BLI_CPP_TYPE_MAKE(Material, Material *, CPPTypeFlags::BasicType) - static bNodeSocketType *make_socket_type_object() { bNodeSocketType *socktype = make_standard_socket_type(SOCK_OBJECT, PROP_NONE); diff --git a/source/creator/creator.c b/source/creator/creator.c index 2cd54deeab5..0a6537865d4 100644 --- a/source/creator/creator.c +++ b/source/creator/creator.c @@ -37,6 +37,7 @@ #include "BKE_cachefile.h" #include "BKE_callbacks.h" #include "BKE_context.h" +#include "BKE_cpp_types.h" #include "BKE_global.h" #include "BKE_gpencil_modifier.h" #include "BKE_idtype.h" @@ -425,6 +426,7 @@ int main(int argc, BKE_blender_globals_init(); /* blender.c */ + BKE_cpp_types_init(); BKE_idtype_init(); BKE_cachefiles_init(); BKE_modifier_init(); |