From d24983d682af6ffe889e259fb4ed677b1b7f31af Mon Sep 17 00:00:00 2001 From: David Snopek Date: Wed, 8 May 2024 16:49:18 -0500 Subject: [PATCH 1/9] Backport miscellaneous changes that can't be cherry-picked (cherry picked from commit 9afbdb9cf67e6962c1ff24f45de8fceb8649bf79) --- include/godot_cpp/core/method_bind.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/godot_cpp/core/method_bind.hpp b/include/godot_cpp/core/method_bind.hpp index 4afd7b8a..eabd6ece 100644 --- a/include/godot_cpp/core/method_bind.hpp +++ b/include/godot_cpp/core/method_bind.hpp @@ -412,6 +412,7 @@ public: method = p_method; generate_argument_types(sizeof...(P)); set_argument_count(sizeof...(P)); + set_const(true); } }; @@ -578,6 +579,7 @@ public: generate_argument_types(sizeof...(P)); set_argument_count(sizeof...(P)); set_return(true); + set_const(true); } }; From a75e33c333060e0eeb171c3a960a7f8bb0d4bbd9 Mon Sep 17 00:00:00 2001 From: Chris Cranford Date: Fri, 19 Apr 2024 14:43:31 -0400 Subject: [PATCH 2/9] Implement to/from dict helpers for PropertyInfo/MethodInfo (cherry picked from commit 2a041b5240b5f8d22e56ffa0f96e6d5b91acd95f) --- include/godot_cpp/core/property_info.hpp | 57 +++++++++++++++++++++++ src/core/object.cpp | 58 ++++++++++++++++++++++++ 2 files changed, 115 insertions(+) diff --git a/include/godot_cpp/core/property_info.hpp b/include/godot_cpp/core/property_info.hpp index 72ee2718..dd71d48a 100644 --- a/include/godot_cpp/core/property_info.hpp +++ b/include/godot_cpp/core/property_info.hpp @@ -68,6 +68,63 @@ struct PropertyInfo { PropertyInfo(GDExtensionVariantType p_type, const StringName &p_name, PropertyHint p_hint = PROPERTY_HINT_NONE, const String &p_hint_string = "", uint32_t p_usage = PROPERTY_USAGE_DEFAULT, const StringName &p_class_name = "") : PropertyInfo((Variant::Type)p_type, p_name, p_hint, p_hint_string, p_usage, p_class_name) {} + + PropertyInfo(const GDExtensionPropertyInfo *p_info) : + PropertyInfo(p_info->type, *reinterpret_cast(p_info->name), (PropertyHint)p_info->hint, *reinterpret_cast(p_info->hint_string), p_info->usage, *reinterpret_cast(p_info->class_name)) {} + + operator Dictionary() const { + Dictionary dict; + dict["name"] = name; + dict["class_name"] = class_name; + dict["type"] = type; + dict["hint"] = hint; + dict["hint_string"] = hint_string; + dict["usage"] = usage; + return dict; + } + + static PropertyInfo from_dict(const Dictionary &p_dict) { + PropertyInfo pi; + if (p_dict.has("type")) { + pi.type = Variant::Type(int(p_dict["type"])); + } + if (p_dict.has("name")) { + pi.name = p_dict["name"]; + } + if (p_dict.has("class_name")) { + pi.class_name = p_dict["class_name"]; + } + if (p_dict.has("hint")) { + pi.hint = PropertyHint(int(p_dict["hint"])); + } + if (p_dict.has("hint_string")) { + pi.hint_string = p_dict["hint_string"]; + } + if (p_dict.has("usage")) { + pi.usage = p_dict["usage"]; + } + return pi; + } + + void _update(GDExtensionPropertyInfo *p_info) { + p_info->type = (GDExtensionVariantType)type; + *(reinterpret_cast(p_info->name)) = name; + p_info->hint = hint; + *(reinterpret_cast(p_info->hint_string)) = hint_string; + p_info->usage = usage; + *(reinterpret_cast(p_info->class_name)) = class_name; + } + + GDExtensionPropertyInfo _to_gdextension() const { + return { + (GDExtensionVariantType)type, + name._native_ptr(), + class_name._native_ptr(), + hint, + hint_string._native_ptr(), + usage, + }; + } }; } // namespace godot diff --git a/src/core/object.cpp b/src/core/object.cpp index dc3c8798..d2e10ffb 100644 --- a/src/core/object.cpp +++ b/src/core/object.cpp @@ -60,8 +60,66 @@ Object *get_object_instance_binding(GodotObject *p_engine_object) { return reinterpret_cast(gdextension_interface_object_get_instance_binding(p_engine_object, token, binding_callbacks)); } +TypedArray convert_property_list(const std::vector &p_list) { + TypedArray va; + for (const PropertyInfo &pi : p_list) { + va.push_back(Dictionary(pi)); + } + return va; +} + } // namespace internal +MethodInfo::operator Dictionary() const { + Dictionary dict; + dict["name"] = name; + dict["args"] = internal::convert_property_list(arguments); + Array da; + for (int i = 0; i < default_arguments.size(); i++) { + da.push_back(default_arguments[i]); + } + dict["default_args"] = da; + dict["flags"] = flags; + dict["id"] = id; + Dictionary r = return_val; + dict["return"] = r; + return dict; +} + +MethodInfo MethodInfo::from_dict(const Dictionary &p_dict) { + MethodInfo mi; + + if (p_dict.has("name")) { + mi.name = p_dict["name"]; + } + Array args; + if (p_dict.has("args")) { + args = p_dict["args"]; + } + + for (int i = 0; i < args.size(); i++) { + Dictionary d = args[i]; + mi.arguments.push_back(PropertyInfo::from_dict(d)); + } + Array defargs; + if (p_dict.has("default_args")) { + defargs = p_dict["default_args"]; + } + for (int i = 0; i < defargs.size(); i++) { + mi.default_arguments.push_back(defargs[i]); + } + + if (p_dict.has("return")) { + mi.return_val = PropertyInfo::from_dict(p_dict["return"]); + } + + if (p_dict.has("flags")) { + mi.flags = p_dict["flags"]; + } + + return mi; +} + MethodInfo::MethodInfo() : flags(GDEXTENSION_METHOD_FLAG_NORMAL) {} From 11cd9a07278c3d50e1df0b60716886fe939b9058 Mon Sep 17 00:00:00 2001 From: A Thousand Ships <96648715+AThousandShips@users.noreply.github.com> Date: Wed, 10 Apr 2024 21:18:46 +0200 Subject: [PATCH 3/9] [Math] Add `is_finite` methods (cherry picked from commit d38917190503b75b307654f54bf5b44c83d0aea4) --- include/godot_cpp/core/math.hpp | 8 ++++++++ include/godot_cpp/variant/aabb.hpp | 1 + include/godot_cpp/variant/basis.hpp | 1 + include/godot_cpp/variant/plane.hpp | 1 + include/godot_cpp/variant/quaternion.hpp | 1 + include/godot_cpp/variant/rect2.hpp | 1 + include/godot_cpp/variant/transform2d.hpp | 1 + include/godot_cpp/variant/transform3d.hpp | 1 + include/godot_cpp/variant/vector2.hpp | 1 + include/godot_cpp/variant/vector3.hpp | 1 + include/godot_cpp/variant/vector4.hpp | 1 + src/variant/aabb.cpp | 4 ++++ src/variant/basis.cpp | 4 ++++ src/variant/plane.cpp | 4 ++++ src/variant/quaternion.cpp | 4 ++++ src/variant/rect2.cpp | 4 ++++ src/variant/transform2d.cpp | 4 ++++ src/variant/transform3d.cpp | 4 ++++ src/variant/vector2.cpp | 4 ++++ src/variant/vector3.cpp | 4 ++++ src/variant/vector4.cpp | 4 ++++ 21 files changed, 58 insertions(+) diff --git a/include/godot_cpp/core/math.hpp b/include/godot_cpp/core/math.hpp index 2cbbe272..1949360a 100644 --- a/include/godot_cpp/core/math.hpp +++ b/include/godot_cpp/core/math.hpp @@ -613,6 +613,14 @@ inline bool is_inf(double p_val) { return std::isinf(p_val); } +inline bool is_finite(float p_val) { + return std::isfinite(p_val); +} + +inline bool is_finite(double p_val) { + return std::isfinite(p_val); +} + inline bool is_equal_approx(float a, float b) { // Check for exact equality first, required to handle "infinity" values. if (a == b) { diff --git a/include/godot_cpp/variant/aabb.hpp b/include/godot_cpp/variant/aabb.hpp index dde392b5..5325bd77 100644 --- a/include/godot_cpp/variant/aabb.hpp +++ b/include/godot_cpp/variant/aabb.hpp @@ -65,6 +65,7 @@ struct _NO_DISCARD_ AABB { bool operator!=(const AABB &p_rval) const; bool is_equal_approx(const AABB &p_aabb) const; + bool is_finite() const; _FORCE_INLINE_ bool intersects(const AABB &p_aabb) const; /// Both AABBs overlap _FORCE_INLINE_ bool intersects_inclusive(const AABB &p_aabb) const; /// Both AABBs (or their faces) overlap _FORCE_INLINE_ bool encloses(const AABB &p_aabb) const; /// p_aabb is completely inside this diff --git a/include/godot_cpp/variant/basis.hpp b/include/godot_cpp/variant/basis.hpp index a365b024..e740a64a 100644 --- a/include/godot_cpp/variant/basis.hpp +++ b/include/godot_cpp/variant/basis.hpp @@ -128,6 +128,7 @@ struct _NO_DISCARD_ Basis { } bool is_equal_approx(const Basis &p_basis) const; + bool is_finite() const; bool operator==(const Basis &p_matrix) const; bool operator!=(const Basis &p_matrix) const; diff --git a/include/godot_cpp/variant/plane.hpp b/include/godot_cpp/variant/plane.hpp index 727f4f54..829f801f 100644 --- a/include/godot_cpp/variant/plane.hpp +++ b/include/godot_cpp/variant/plane.hpp @@ -77,6 +77,7 @@ struct _NO_DISCARD_ Plane { Plane operator-() const { return Plane(-normal, -d); } bool is_equal_approx(const Plane &p_plane) const; bool is_equal_approx_any_side(const Plane &p_plane) const; + bool is_finite() const; _FORCE_INLINE_ bool operator==(const Plane &p_plane) const; _FORCE_INLINE_ bool operator!=(const Plane &p_plane) const; diff --git a/include/godot_cpp/variant/quaternion.hpp b/include/godot_cpp/variant/quaternion.hpp index 3816b666..5de91b20 100644 --- a/include/godot_cpp/variant/quaternion.hpp +++ b/include/godot_cpp/variant/quaternion.hpp @@ -55,6 +55,7 @@ struct _NO_DISCARD_ Quaternion { } _FORCE_INLINE_ real_t length_squared() const; bool is_equal_approx(const Quaternion &p_quaternion) const; + bool is_finite() const; real_t length() const; void normalize(); Quaternion normalized() const; diff --git a/include/godot_cpp/variant/rect2.hpp b/include/godot_cpp/variant/rect2.hpp index cfd24b2f..c37134d0 100644 --- a/include/godot_cpp/variant/rect2.hpp +++ b/include/godot_cpp/variant/rect2.hpp @@ -209,6 +209,7 @@ struct _NO_DISCARD_ Rect2 { } bool is_equal_approx(const Rect2 &p_rect) const; + bool is_finite() const; bool operator==(const Rect2 &p_rect) const { return position == p_rect.position && size == p_rect.size; } bool operator!=(const Rect2 &p_rect) const { return position != p_rect.position || size != p_rect.size; } diff --git a/include/godot_cpp/variant/transform2d.hpp b/include/godot_cpp/variant/transform2d.hpp index 5a483985..d73323f3 100644 --- a/include/godot_cpp/variant/transform2d.hpp +++ b/include/godot_cpp/variant/transform2d.hpp @@ -99,6 +99,7 @@ struct _NO_DISCARD_ Transform2D { void orthonormalize(); Transform2D orthonormalized() const; bool is_equal_approx(const Transform2D &p_transform) const; + bool is_finite() const; Transform2D looking_at(const Vector2 &p_target) const; diff --git a/include/godot_cpp/variant/transform3d.hpp b/include/godot_cpp/variant/transform3d.hpp index 3a54c0b2..6fa5999e 100644 --- a/include/godot_cpp/variant/transform3d.hpp +++ b/include/godot_cpp/variant/transform3d.hpp @@ -78,6 +78,7 @@ struct _NO_DISCARD_ Transform3D { void orthogonalize(); Transform3D orthogonalized() const; bool is_equal_approx(const Transform3D &p_transform) const; + bool is_finite() const; bool operator==(const Transform3D &p_transform) const; bool operator!=(const Transform3D &p_transform) const; diff --git a/include/godot_cpp/variant/vector2.hpp b/include/godot_cpp/variant/vector2.hpp index 13c0da6f..fe4d05aa 100644 --- a/include/godot_cpp/variant/vector2.hpp +++ b/include/godot_cpp/variant/vector2.hpp @@ -123,6 +123,7 @@ struct _NO_DISCARD_ Vector2 { bool is_equal_approx(const Vector2 &p_v) const; bool is_zero_approx() const; + bool is_finite() const; Vector2 operator+(const Vector2 &p_v) const; void operator+=(const Vector2 &p_v); diff --git a/include/godot_cpp/variant/vector3.hpp b/include/godot_cpp/variant/vector3.hpp index a8d96ed0..1107bca8 100644 --- a/include/godot_cpp/variant/vector3.hpp +++ b/include/godot_cpp/variant/vector3.hpp @@ -146,6 +146,7 @@ struct _NO_DISCARD_ Vector3 { bool is_equal_approx(const Vector3 &p_v) const; bool is_zero_approx() const; + bool is_finite() const; /* Operators */ diff --git a/include/godot_cpp/variant/vector4.hpp b/include/godot_cpp/variant/vector4.hpp index 26c57c35..b20915aa 100644 --- a/include/godot_cpp/variant/vector4.hpp +++ b/include/godot_cpp/variant/vector4.hpp @@ -81,6 +81,7 @@ struct _NO_DISCARD_ Vector4 { _FORCE_INLINE_ real_t length_squared() const; bool is_equal_approx(const Vector4 &p_vec4) const; bool is_zero_approx() const; + bool is_finite() const; real_t length() const; void normalize(); Vector4 normalized() const; diff --git a/src/variant/aabb.cpp b/src/variant/aabb.cpp index 92e751b4..ded17d2b 100644 --- a/src/variant/aabb.cpp +++ b/src/variant/aabb.cpp @@ -78,6 +78,10 @@ bool AABB::is_equal_approx(const AABB &p_aabb) const { return position.is_equal_approx(p_aabb.position) && size.is_equal_approx(p_aabb.size); } +bool AABB::is_finite() const { + return position.is_finite() && size.is_finite(); +} + AABB AABB::intersection(const AABB &p_aabb) const { #ifdef MATH_CHECKS if (unlikely(size.x < 0 || size.y < 0 || size.z < 0 || p_aabb.size.x < 0 || p_aabb.size.y < 0 || p_aabb.size.z < 0)) { diff --git a/src/variant/basis.cpp b/src/variant/basis.cpp index 8f78d9f0..8d4176e6 100644 --- a/src/variant/basis.cpp +++ b/src/variant/basis.cpp @@ -692,6 +692,10 @@ bool Basis::is_equal_approx(const Basis &p_basis) const { return rows[0].is_equal_approx(p_basis.rows[0]) && rows[1].is_equal_approx(p_basis.rows[1]) && rows[2].is_equal_approx(p_basis.rows[2]); } +bool Basis::is_finite() const { + return rows[0].is_finite() && rows[1].is_finite() && rows[2].is_finite(); +} + bool Basis::operator==(const Basis &p_matrix) const { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { diff --git a/src/variant/plane.cpp b/src/variant/plane.cpp index 53dd439a..caea516e 100644 --- a/src/variant/plane.cpp +++ b/src/variant/plane.cpp @@ -178,6 +178,10 @@ bool Plane::is_equal_approx(const Plane &p_plane) const { return normal.is_equal_approx(p_plane.normal) && Math::is_equal_approx(d, p_plane.d); } +bool Plane::is_finite() const { + return normal.is_finite() && Math::is_finite(d); +} + Plane::operator String() const { return "[N: " + normal.operator String() + ", D: " + String::num_real(d, false) + "]"; } diff --git a/src/variant/quaternion.cpp b/src/variant/quaternion.cpp index 9d4d838d..c0108505 100644 --- a/src/variant/quaternion.cpp +++ b/src/variant/quaternion.cpp @@ -81,6 +81,10 @@ bool Quaternion::is_equal_approx(const Quaternion &p_quaternion) const { return Math::is_equal_approx(x, p_quaternion.x) && Math::is_equal_approx(y, p_quaternion.y) && Math::is_equal_approx(z, p_quaternion.z) && Math::is_equal_approx(w, p_quaternion.w); } +bool Quaternion::is_finite() const { + return Math::is_finite(x) && Math::is_finite(y) && Math::is_finite(z) && Math::is_finite(w); +} + real_t Quaternion::length() const { return Math::sqrt(length_squared()); } diff --git a/src/variant/rect2.cpp b/src/variant/rect2.cpp index a70fee63..62730a9d 100644 --- a/src/variant/rect2.cpp +++ b/src/variant/rect2.cpp @@ -40,6 +40,10 @@ bool Rect2::is_equal_approx(const Rect2 &p_rect) const { return position.is_equal_approx(p_rect.position) && size.is_equal_approx(p_rect.size); } +bool Rect2::is_finite() const { + return position.is_finite() && size.is_finite(); +} + bool Rect2::intersects_segment(const Point2 &p_from, const Point2 &p_to, Point2 *r_pos, Point2 *r_normal) const { #ifdef MATH_CHECKS if (unlikely(size.x < 0 || size.y < 0)) { diff --git a/src/variant/transform2d.cpp b/src/variant/transform2d.cpp index 530a99e3..3b2c0b09 100644 --- a/src/variant/transform2d.cpp +++ b/src/variant/transform2d.cpp @@ -170,6 +170,10 @@ bool Transform2D::is_equal_approx(const Transform2D &p_transform) const { return columns[0].is_equal_approx(p_transform.columns[0]) && columns[1].is_equal_approx(p_transform.columns[1]) && columns[2].is_equal_approx(p_transform.columns[2]); } +bool Transform2D::is_finite() const { + return columns[0].is_finite() && columns[1].is_finite() && columns[2].is_finite(); +} + Transform2D Transform2D::looking_at(const Vector2 &p_target) const { Transform2D return_trans = Transform2D(get_rotation(), get_origin()); Vector2 target_position = affine_inverse().xform(p_target); diff --git a/src/variant/transform3d.cpp b/src/variant/transform3d.cpp index 1a4189e2..d71e9191 100644 --- a/src/variant/transform3d.cpp +++ b/src/variant/transform3d.cpp @@ -175,6 +175,10 @@ bool Transform3D::is_equal_approx(const Transform3D &p_transform) const { return basis.is_equal_approx(p_transform.basis) && origin.is_equal_approx(p_transform.origin); } +bool Transform3D::is_finite() const { + return basis.is_finite() && origin.is_finite(); +} + bool Transform3D::operator==(const Transform3D &p_transform) const { return (basis == p_transform.basis && origin == p_transform.origin); } diff --git a/src/variant/vector2.cpp b/src/variant/vector2.cpp index df870805..ca1ab8f3 100644 --- a/src/variant/vector2.cpp +++ b/src/variant/vector2.cpp @@ -188,6 +188,10 @@ bool Vector2::is_zero_approx() const { return Math::is_zero_approx(x) && Math::is_zero_approx(y); } +bool Vector2::is_finite() const { + return Math::is_finite(x) && Math::is_finite(y); +} + Vector2::operator String() const { return "(" + String::num_real(x, false) + ", " + String::num_real(y, false) + ")"; } diff --git a/src/variant/vector3.cpp b/src/variant/vector3.cpp index 61536580..9f04340a 100644 --- a/src/variant/vector3.cpp +++ b/src/variant/vector3.cpp @@ -141,6 +141,10 @@ bool Vector3::is_zero_approx() const { return Math::is_zero_approx(x) && Math::is_zero_approx(y) && Math::is_zero_approx(z); } +bool Vector3::is_finite() const { + return Math::is_finite(x) && Math::is_finite(y) && Math::is_finite(z); +} + Vector3::operator String() const { return "(" + String::num_real(x, false) + ", " + String::num_real(y, false) + ", " + String::num_real(z, false) + ")"; } diff --git a/src/variant/vector4.cpp b/src/variant/vector4.cpp index 72c79d1e..483545e2 100644 --- a/src/variant/vector4.cpp +++ b/src/variant/vector4.cpp @@ -67,6 +67,10 @@ bool Vector4::is_zero_approx() const { return Math::is_zero_approx(x) && Math::is_zero_approx(y) && Math::is_zero_approx(z) && Math::is_zero_approx(w); } +bool Vector4::is_finite() const { + return Math::is_finite(x) && Math::is_finite(y) && Math::is_finite(z) && Math::is_finite(w); +} + real_t Vector4::length() const { return Math::sqrt(length_squared()); } From 82ea3f74a470ee5ca66bb34e8917816aabbb9f5e Mon Sep 17 00:00:00 2001 From: pupil1337 <2500698263@qq.com> Date: Sun, 7 Apr 2024 21:07:01 +0800 Subject: [PATCH 4/9] Add static_assert() for register_class (cherry picked from commit 1fa7a9cb1985bdf6c098492563e0e76cf72fd91a) --- include/godot_cpp/core/class_db.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/godot_cpp/core/class_db.hpp b/include/godot_cpp/core/class_db.hpp index 26f58e4a..d8efde3c 100644 --- a/include/godot_cpp/core/class_db.hpp +++ b/include/godot_cpp/core/class_db.hpp @@ -174,6 +174,7 @@ public: template void ClassDB::_register_class(bool p_virtual) { static_assert(TypesAreSame::value, "Class not declared properly, please use GDCLASS."); + static_assert(!std::is_abstract_v || is_abstract, "Class is abstract, please use GDREGISTER_ABSTRACT_CLASS."); instance_binding_callbacks[T::get_class_static()] = &T::_gde_binding_callbacks; // Register this class within our plugin From fff665e0f5137a590a281d71dc7d579e101f2351 Mon Sep 17 00:00:00 2001 From: David Snopek Date: Wed, 24 Apr 2024 14:26:06 -0500 Subject: [PATCH 5/9] Give compile-time error if registering a class without its own `_bind_methods()` function (cherry picked from commit ca46ef4d256c727a116366334b705ecda0a06c5d) --- include/godot_cpp/classes/wrapped.hpp | 5 +++++ include/godot_cpp/core/class_db.hpp | 1 + include/godot_cpp/core/type_info.hpp | 10 ++++++++++ 3 files changed, 16 insertions(+) diff --git a/include/godot_cpp/classes/wrapped.hpp b/include/godot_cpp/classes/wrapped.hpp index 65c62eb5..936b0d9c 100644 --- a/include/godot_cpp/classes/wrapped.hpp +++ b/include/godot_cpp/classes/wrapped.hpp @@ -171,6 +171,7 @@ protected: \ public: \ typedef m_class self_type; \ + typedef m_inherits parent_type; \ \ static void initialize_class() { \ static bool initialized = false; \ @@ -334,6 +335,7 @@ private: private: \ inline static ::godot::internal::EngineClassRegistration _gde_engine_class_registration_helper; \ void operator=(const m_class &p_rval) {} \ + friend class ::godot::ClassDB; \ \ protected: \ virtual const GDExtensionInstanceBindingCallbacks *_get_bindings_callbacks() const override { \ @@ -343,6 +345,8 @@ protected: m_class(const char *p_godot_class) : m_inherits(p_godot_class) {} \ m_class(GodotObject *p_godot_object) : m_inherits(p_godot_object) {} \ \ + static void _bind_methods() {} \ + \ static void (*_get_bind_methods())() { \ return nullptr; \ } \ @@ -385,6 +389,7 @@ protected: \ public: \ typedef m_class self_type; \ + typedef m_inherits parent_type; \ \ static void initialize_class() {} \ \ diff --git a/include/godot_cpp/core/class_db.hpp b/include/godot_cpp/core/class_db.hpp index d8efde3c..66b70d32 100644 --- a/include/godot_cpp/core/class_db.hpp +++ b/include/godot_cpp/core/class_db.hpp @@ -174,6 +174,7 @@ public: template void ClassDB::_register_class(bool p_virtual) { static_assert(TypesAreSame::value, "Class not declared properly, please use GDCLASS."); + static_assert(!FunctionsAreSame::value, "Class must declare 'static void _bind_methods'."); static_assert(!std::is_abstract_v || is_abstract, "Class is abstract, please use GDREGISTER_ABSTRACT_CLASS."); instance_binding_callbacks[T::get_class_static()] = &T::_gde_binding_callbacks; diff --git a/include/godot_cpp/core/type_info.hpp b/include/godot_cpp/core/type_info.hpp index 2c4f8e40..e1f2b205 100644 --- a/include/godot_cpp/core/type_info.hpp +++ b/include/godot_cpp/core/type_info.hpp @@ -58,6 +58,16 @@ struct TypesAreSame { static bool const value = true; }; +template +struct FunctionsAreSame { + static bool const value = false; +}; + +template +struct FunctionsAreSame { + static bool const value = true; +}; + template struct TypeInherits { static D *get_d(); From 6123c86f068f81dc977b33f6ab11093c0c22b893 Mon Sep 17 00:00:00 2001 From: David Snopek Date: Tue, 5 Mar 2024 11:11:29 -0600 Subject: [PATCH 6/9] Correctly handle `Object *` arguments that were encoded as `nullptr` (cherry picked from commit 37542dc2ec1e98fbe93e2daa8f11e7fb5428cb0e) --- include/godot_cpp/core/method_ptrcall.hpp | 8 ++++---- test/project/main.gd | 4 ++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/include/godot_cpp/core/method_ptrcall.hpp b/include/godot_cpp/core/method_ptrcall.hpp index ca3327ed..b12a7e64 100644 --- a/include/godot_cpp/core/method_ptrcall.hpp +++ b/include/godot_cpp/core/method_ptrcall.hpp @@ -170,11 +170,11 @@ template struct PtrToArg { static_assert(std::is_base_of::value, "Cannot encode non-Object value as an Object"); _FORCE_INLINE_ static T *convert(const void *p_ptr) { - return reinterpret_cast(godot::internal::get_object_instance_binding(*reinterpret_cast(const_cast(p_ptr)))); + return likely(p_ptr) ? reinterpret_cast(godot::internal::get_object_instance_binding(*reinterpret_cast(const_cast(p_ptr)))) : nullptr; } typedef Object *EncodeT; _FORCE_INLINE_ static void encode(T *p_var, void *p_ptr) { - *reinterpret_cast(p_ptr) = p_var ? p_var->_owner : nullptr; + *reinterpret_cast(p_ptr) = likely(p_var) ? p_var->_owner : nullptr; } }; @@ -182,11 +182,11 @@ template struct PtrToArg { static_assert(std::is_base_of::value, "Cannot encode non-Object value as an Object"); _FORCE_INLINE_ static const T *convert(const void *p_ptr) { - return reinterpret_cast(godot::internal::get_object_instance_binding(*reinterpret_cast(const_cast(p_ptr)))); + return likely(p_ptr) ? reinterpret_cast(godot::internal::get_object_instance_binding(*reinterpret_cast(const_cast(p_ptr)))) : nullptr; } typedef const Object *EncodeT; _FORCE_INLINE_ static void encode(T *p_var, void *p_ptr) { - *reinterpret_cast(p_ptr) = p_var ? p_var->_owner : nullptr; + *reinterpret_cast(p_ptr) = likely(p_var) ? p_var->_owner : nullptr; } }; diff --git a/test/project/main.gd b/test/project/main.gd index 9717eca2..eb8f734a 100644 --- a/test/project/main.gd +++ b/test/project/main.gd @@ -122,6 +122,10 @@ func _ready(): control.queue_free() sprite.queue_free() + # Test that passing null for objects works as expected too. + var example_null : Example = null + assert_equal(example.test_object_cast_to_node(example_null), false) + # Test conversions to and from Variant. assert_equal(example.test_variant_vector2i_conversion(Vector2i(1, 1)), Vector2i(1, 1)) assert_equal(example.test_variant_vector2i_conversion(Vector2(1.0, 1.0)), Vector2i(1, 1)) From 6328728dc2bd08be271a02b2c9ac99c378f98857 Mon Sep 17 00:00:00 2001 From: David Snopek Date: Tue, 23 Apr 2024 13:37:43 -0500 Subject: [PATCH 7/9] Allow forwarding from `ClassDB` to `ClassDBSingleton` to support enumerations (cherry picked from commit e1b3b32db5a20c8704650f1cd60e134540ada387) --- binding_generator.py | 66 ++++++++++++++++++++++++----- include/godot_cpp/core/class_db.hpp | 2 + 2 files changed, 57 insertions(+), 11 deletions(-) diff --git a/binding_generator.py b/binding_generator.py index 86d511a7..20fd283b 100644 --- a/binding_generator.py +++ b/binding_generator.py @@ -178,6 +178,10 @@ def generate_bindings(api_filepath, use_template_get_node, bits="64", precision= generate_utility_functions(api, target_dir) +CLASS_ALIASES = { + "ClassDB": "ClassDBSingleton", +} + builtin_classes = [] # Key is class name, value is boolean where True means the class is refcounted. @@ -1079,9 +1083,9 @@ def generate_engine_classes_bindings(api, output_dir, use_template_get_node): # First create map of classes and singletons. for class_api in api["classes"]: # Generate code for the ClassDB singleton under a different name. - if class_api["name"] == "ClassDB": - class_api["name"] = "ClassDBSingleton" - class_api["alias_for"] = "ClassDB" + if class_api["name"] in CLASS_ALIASES: + class_api["alias_for"] = class_api["name"] + class_api["name"] = CLASS_ALIASES[class_api["alias_for"]] engine_classes[class_api["name"]] = class_api["is_refcounted"] for native_struct in api["native_structures"]: engine_classes[native_struct["name"]] = False @@ -1089,9 +1093,9 @@ def generate_engine_classes_bindings(api, output_dir, use_template_get_node): for singleton in api["singletons"]: # Generate code for the ClassDB singleton under a different name. - if singleton["name"] == "ClassDB": - singleton["name"] = "ClassDBSingleton" - singleton["alias_for"] = "ClassDB" + if singleton["name"] in CLASS_ALIASES: + singleton["alias_for"] = singleton["name"] + singleton["name"] = CLASS_ALIASES[singleton["name"]] singletons.append(singleton["name"]) for class_api in api["classes"]: @@ -1294,6 +1298,10 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us result.append("#include ") result.append("") + if class_name == "ClassDBSingleton": + result.append("#include ") + result.append("") + result.append("namespace godot {") result.append("") @@ -1448,6 +1456,19 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us if class_name == "ClassDBSingleton": result.append("#define CLASSDB_SINGLETON_FORWARD_METHODS \\") + + if "enums" in class_api: + for enum_api in class_api["enums"]: + if enum_api["is_bitfield"]: + result.append(f'\tenum {enum_api["name"]} : uint64_t {{ \\') + else: + result.append(f'\tenum {enum_api["name"]} {{ \\') + + for value in enum_api["values"]: + result.append(f'\t\t{value["name"]} = {value["value"]}, \\') + result.append("\t}; \\") + result.append("\t \\") + for method in class_api["methods"]: # ClassDBSingleton shouldn't have any static or vararg methods, but if some appear later, lets skip them. if vararg: @@ -1456,12 +1477,17 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us continue method_signature = "\tstatic " + return_type = None if "return_type" in method: - method_signature += f'{correct_type(method["return_type"])} ' + return_type = correct_type(method["return_type"].replace("ClassDBSingleton", "ClassDB"), None, False) elif "return_value" in method: - method_signature += ( - correct_type(method["return_value"]["type"], method["return_value"].get("meta", None)) + " " + return_type = correct_type( + method["return_value"]["type"].replace("ClassDBSingleton", "ClassDB"), + method["return_value"].get("meta", None), + False, ) + if return_type is not None: + method_signature += return_type + " " else: method_signature += "void " @@ -1480,8 +1506,10 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us result.append(method_signature) method_body = "\t\t" - if "return_type" in method or "return_value" in method: + if return_type is not None: method_body += "return " + if "alias_for" in class_api and return_type.startswith(class_api["alias_for"] + "::"): + method_body += f"({return_type})" method_body += f'ClassDBSingleton::get_singleton()->{method["name"]}(' method_body += ", ".join(map(lambda x: escape_identifier(x["name"]), method_arguments)) method_body += "); \\" @@ -1491,6 +1519,18 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us result.append("\t;") result.append("") + result.append("#define CLASSDB_SINGLETON_VARIANT_CAST \\") + + if "enums" in class_api: + for enum_api in class_api["enums"]: + if enum_api["is_bitfield"]: + result.append(f'\tVARIANT_BITFIELD_CAST({class_api["alias_for"]}::{enum_api["name"]}); \\') + else: + result.append(f'\tVARIANT_ENUM_CAST({class_api["alias_for"]}::{enum_api["name"]}); \\') + + result.append("\t;") + result.append("") + result.append(f"#endif // ! {header_guard}") return "\n".join(result) @@ -2285,7 +2325,7 @@ def correct_typed_array(type_name): return type_name -def correct_type(type_name, meta=None): +def correct_type(type_name, meta=None, use_alias=True): type_conversion = {"float": "double", "int": "int64_t", "Nil": "Variant"} if meta != None: if "int" in meta: @@ -2301,11 +2341,15 @@ def correct_type(type_name, meta=None): if is_enum(type_name): if is_bitfield(type_name): base_class = get_enum_class(type_name) + if use_alias and base_class in CLASS_ALIASES: + base_class = CLASS_ALIASES[base_class] if base_class == "GlobalConstants": return f"BitField<{get_enum_name(type_name)}>" return f"BitField<{base_class}::{get_enum_name(type_name)}>" else: base_class = get_enum_class(type_name) + if use_alias and base_class in CLASS_ALIASES: + base_class = CLASS_ALIASES[base_class] if base_class == "GlobalConstants": return f"{get_enum_name(type_name)}" return f"{base_class}::{get_enum_name(type_name)}" diff --git a/include/godot_cpp/core/class_db.hpp b/include/godot_cpp/core/class_db.hpp index 66b70d32..752c4390 100644 --- a/include/godot_cpp/core/class_db.hpp +++ b/include/godot_cpp/core/class_db.hpp @@ -292,4 +292,6 @@ MethodBind *ClassDB::bind_vararg_method(uint32_t p_flags, StringName p_name, M p } // namespace godot +CLASSDB_SINGLETON_VARIANT_CAST; + #endif // GODOT_CLASS_DB_HPP From 1ab42ec6c5f92144991cc11e238f8d068496bd78 Mon Sep 17 00:00:00 2001 From: Daylily-Zeleen Date: Fri, 17 May 2024 02:06:59 +0800 Subject: [PATCH 8/9] mark return value of `get_class_static` and `get_parent_class_static` as const (cherry picked from commit 3db8549e19641d49dfcdf3055da6917699c07d1f) --- include/godot_cpp/classes/wrapped.hpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/include/godot_cpp/classes/wrapped.hpp b/include/godot_cpp/classes/wrapped.hpp index 936b0d9c..a8bd3a0e 100644 --- a/include/godot_cpp/classes/wrapped.hpp +++ b/include/godot_cpp/classes/wrapped.hpp @@ -82,8 +82,8 @@ protected: virtual ~Wrapped() {} public: - static StringName &get_class_static() { - static StringName string_name = StringName("Wrapped"); + static const StringName &get_class_static() { + static const StringName string_name = StringName("Wrapped"); return string_name; } @@ -186,12 +186,12 @@ public: initialized = true; \ } \ \ - static ::godot::StringName &get_class_static() { \ - static ::godot::StringName string_name = ::godot::StringName(#m_class); \ + static const ::godot::StringName &get_class_static() { \ + static const ::godot::StringName string_name = ::godot::StringName(#m_class); \ return string_name; \ } \ \ - static ::godot::StringName &get_parent_class_static() { \ + static const ::godot::StringName &get_parent_class_static() { \ return m_inherits::get_class_static(); \ } \ \ @@ -393,12 +393,12 @@ public: \ static void initialize_class() {} \ \ - static ::godot::StringName &get_class_static() { \ - static ::godot::StringName string_name = ::godot::StringName(#m_alias_for); \ + static const ::godot::StringName &get_class_static() { \ + static const ::godot::StringName string_name = ::godot::StringName(#m_alias_for); \ return string_name; \ } \ \ - static ::godot::StringName &get_parent_class_static() { \ + static const ::godot::StringName &get_parent_class_static() { \ return m_inherits::get_class_static(); \ } \ \ From 7b93607779b6ecaf9d55c345715f6154751f2a14 Mon Sep 17 00:00:00 2001 From: David Snopek Date: Wed, 24 Apr 2024 13:01:53 -0500 Subject: [PATCH 9/9] Fix NOTIFICATION_POSTINITIALIZE sent twice to native parent class (cherry picked from commit 06373ce1cf737a33d6a7b2b14cfe25ed729b9c48) --- include/godot_cpp/classes/wrapped.hpp | 6 ++++++ src/classes/wrapped.cpp | 5 +---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/include/godot_cpp/classes/wrapped.hpp b/include/godot_cpp/classes/wrapped.hpp index a8bd3a0e..d325fed9 100644 --- a/include/godot_cpp/classes/wrapped.hpp +++ b/include/godot_cpp/classes/wrapped.hpp @@ -76,6 +76,7 @@ protected: uint32_t plist_size = 0; void _postinitialize(); + virtual void _notificationv(int32_t p_what) {} Wrapped(const StringName p_godot_class); Wrapped(GodotObject *p_godot_object); @@ -328,6 +329,11 @@ public: _gde_binding_reference_callback, \ }; \ \ +protected: \ + virtual void _notificationv(int32_t p_what) override { \ + m_class::notification_bind(this, p_what); \ + } \ + \ private: // Don't use this for your classes, use GDCLASS() instead. diff --git a/src/classes/wrapped.cpp b/src/classes/wrapped.cpp index 8a2092c1..f1b31aa0 100644 --- a/src/classes/wrapped.cpp +++ b/src/classes/wrapped.cpp @@ -51,10 +51,7 @@ void Wrapped::_postinitialize() { } godot::internal::gdextension_interface_object_set_instance_binding(_owner, godot::internal::token, this, _get_bindings_callbacks()); if (extension_class) { - Object *obj = dynamic_cast(this); - if (obj) { - obj->notification(Object::NOTIFICATION_POSTINITIALIZE); - } + _notificationv(Object::NOTIFICATION_POSTINITIALIZE); } }