From 321c8d2b30256b4a1e42aa1afcefcb4c446246a7 Mon Sep 17 00:00:00 2001 From: Chris Cranford Date: Fri, 12 Jan 2024 15:25:56 -0500 Subject: [PATCH 1/2] Rework GDCLASS macro to allow pure virtual functions --- include/godot_cpp/classes/wrapped.hpp | 42 ++++++++++++++++++++------- include/godot_cpp/core/class_db.hpp | 4 +-- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/include/godot_cpp/classes/wrapped.hpp b/include/godot_cpp/classes/wrapped.hpp index f8f921b3..eea161c2 100644 --- a/include/godot_cpp/classes/wrapped.hpp +++ b/include/godot_cpp/classes/wrapped.hpp @@ -42,6 +42,8 @@ namespace godot { class ClassDB; +template +class ClassCreator; typedef void GodotObject; @@ -49,6 +51,8 @@ typedef void GodotObject; class Wrapped { friend class GDExtensionBinding; friend void postinitialize_handler(Wrapped *); + template + friend class ClassCreator; protected: #ifdef HOT_RELOAD_ENABLED @@ -132,16 +136,41 @@ struct EngineClassRegistration { } // namespace godot #ifdef HOT_RELOAD_ENABLED -#define _GDCLASS_RECREATE(m_class, m_inherits) \ +#define _GDCLASS_RECREATE(m_class) \ m_class *new_instance = (m_class *)memalloc(sizeof(m_class)); \ Wrapped::RecreateInstance recreate_data = { new_instance, obj, Wrapped::recreate_instance }; \ Wrapped::recreate_instance = &recreate_data; \ memnew_placement(new_instance, m_class); \ return new_instance; #else -#define _GDCLASS_RECREATE(m_class, m_inherits) return nullptr; +#define _GDCLASS_RECREATE(m_class) return nullptr; #endif +namespace godot { + +template +class ClassCreator { +public: + static GDExtensionObjectPtr create(void *data) { + if constexpr (!std::is_abstract_v) { + T *new_object = memnew(T); + return new_object->_owner; + } else { + return nullptr; + } + }; + + static GDExtensionClassInstancePtr recreate(void *data, GDExtensionObjectPtr obj) { + if constexpr (!std::is_abstract_v) { + _GDCLASS_RECREATE(T) + } else { + return nullptr; + } + } +}; + +} // namespace godot + // Use this on top of your own classes. // Note: the trail of `***` is to keep sane diffs in PRs, because clang-format otherwise moves every `\` which makes // every line of the macro different @@ -226,15 +255,6 @@ public: return m_inherits::get_class_static(); \ } \ \ - static GDExtensionObjectPtr create(void *data) { \ - m_class *new_object = memnew(m_class); \ - return new_object->_owner; \ - } \ - \ - static GDExtensionClassInstancePtr recreate(void *data, GDExtensionObjectPtr obj) { \ - _GDCLASS_RECREATE(m_class, m_inherits); \ - } \ - \ static void notification_bind(GDExtensionClassInstancePtr p_instance, int32_t p_what, GDExtensionBool p_reversed) { \ if (p_instance && m_class::_get_notification()) { \ if (m_class::_get_notification() != m_inherits::_get_notification()) { \ diff --git a/include/godot_cpp/core/class_db.hpp b/include/godot_cpp/core/class_db.hpp index 4196a76b..8a8b7648 100644 --- a/include/godot_cpp/core/class_db.hpp +++ b/include/godot_cpp/core/class_db.hpp @@ -202,9 +202,9 @@ void ClassDB::_register_class(bool p_virtual, bool p_exposed) { T::to_string_bind, // GDExtensionClassToString to_string_func; nullptr, // GDExtensionClassReference reference_func; nullptr, // GDExtensionClassUnreference unreference_func; - T::create, // GDExtensionClassCreateInstance create_instance_func; /* this one is mandatory */ + ClassCreator::create, // GDExtensionClassCreateInstance create_instance_func; /* this one is mandatory */ T::free, // GDExtensionClassFreeInstance free_instance_func; /* this one is mandatory */ - T::recreate, // GDExtensionClassRecreateInstance recreate_instance_func; + ClassCreator::recreate, // GDExtensionClassRecreateInstance recreate_instance_func; &ClassDB::get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func; nullptr, // GDExtensionClassGetVirtualCallData get_virtual_call_data_func; nullptr, // GDExtensionClassCallVirtualWithData call_virtual_func; From ab3db91c92193656f5d2afa91ba99c02c860b5be Mon Sep 17 00:00:00 2001 From: Chris Cranford Date: Sat, 13 Jan 2024 19:56:48 -0500 Subject: [PATCH 2/2] Add pure virtual test --- test/src/example.h | 18 ++++++++++++++++++ test/src/register_types.cpp | 3 +++ 2 files changed, 21 insertions(+) diff --git a/test/src/example.h b/test/src/example.h index 72f6783d..32d66d56 100644 --- a/test/src/example.h +++ b/test/src/example.h @@ -205,4 +205,22 @@ protected: static void _bind_methods() {} }; +class ExamplePureVirtualBase : public Object { + GDCLASS(ExamplePureVirtualBase, Object); + +protected: + static void _bind_methods() {} + + virtual int test_function() = 0; +}; + +class ExamplePureVirtual : public ExamplePureVirtualBase { + GDCLASS(ExamplePureVirtual, ExamplePureVirtualBase); + +protected: + static void _bind_methods() {} + + int test_function() override { return 25; } +}; + #endif // EXAMPLE_CLASS_H diff --git a/test/src/register_types.cpp b/test/src/register_types.cpp index dbb37d90..5b99d385 100644 --- a/test/src/register_types.cpp +++ b/test/src/register_types.cpp @@ -26,6 +26,9 @@ void initialize_example_module(ModuleInitializationLevel p_level) { ClassDB::register_class(); ClassDB::register_class(true); ClassDB::register_abstract_class(); + + GDREGISTER_VIRTUAL_CLASS(ExamplePureVirtualBase); + GDREGISTER_CLASS(ExamplePureVirtual); } void uninitialize_example_module(ModuleInitializationLevel p_level) {