Change constructor/destructor management of extension classes

This makes sure custom constructors are always called on extension
classes. However, note that constructors should not take any parameters,
since Godot doesn't support that. Parameters are ignore in memnew macro.

Use memnew(MyClass()) instead of memnew(MyClass) since it now needs a
value instead of a class name. This macro calls MyClass::_new() (define
in GDCLASS macro) which ultimately calls Godot to create the object,
ensuring that both the Godot and the extension instances are created.

Non Godot classes (that don't derive godot::Object) are constructed as
usual an can have parameters.

memdelete is also changed for the same reason, as it needs to destroy
the Godot object as well, and that automatically frees the bound
extension instance.
pull/602/head
George Marques 2021-09-09 20:40:40 -03:00 committed by Bastiaan Olij
parent e839199848
commit 38ee8bfcf7
5 changed files with 179 additions and 129 deletions

View File

@ -889,6 +889,10 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
result.append("public:") result.append("public:")
# Constructor override, since parent Wrapped has protected constructor.
result.append(f"\t{class_name}() = default;")
result.append("")
if "enums" in class_api: if "enums" in class_api:
for enum_api in class_api["enums"]: for enum_api in class_api["enums"]:
result.append(f'\tenum {enum_api["name"]} {{') result.append(f'\tenum {enum_api["name"]} {{')
@ -963,10 +967,6 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
"\tT *get_node(const NodePath &p_path) const { return Object::cast_to<T>(get_node_internal(p_path)); }" "\tT *get_node(const NodePath &p_path) const { return Object::cast_to<T>(get_node_internal(p_path)); }"
) )
# Constructor.
result.append("")
result.append(f"\t{class_name}();")
result.append("") result.append("")
result.append("};") result.append("};")
result.append("") result.append("")
@ -1104,17 +1104,6 @@ def generate_engine_class_source(class_api, used_classes, fully_used_classes, us
result.append(method_signature) result.append(method_signature)
result.append("") result.append("")
# Constructor.
result.append(f"{class_name}::{class_name}() : {inherits}(godot::internal::empty_constructor()) {{")
result.append(
f'\tstatic GDNativeClassConstructor constructor = internal::interface->classdb_get_constructor("{class_name}");'
)
result.append("\t_owner = (GodotObject *)constructor();")
result.append(
f"\tinternal::interface->object_set_instance_binding((GDNativeObjectPtr)_owner, internal::token, this, &{class_name}::___binding_callbacks);"
)
result.append("}")
result.append("") result.append("")
result.append("} // namespace godot ") result.append("} // namespace godot ")

View File

@ -199,7 +199,7 @@ public:
} }
void instantiate() { void instantiate() {
ref(memnew(T)); ref(memnew(T()));
} }
Ref() {} Ref() {}

View File

@ -33,10 +33,8 @@
#include <godot_cpp/core/memory.hpp> #include <godot_cpp/core/memory.hpp>
#include <godot_cpp/godot.hpp>
namespace godot { namespace godot {
namespace internal {
struct empty_constructor {};
} // namespace internal
typedef void GodotObject; typedef void GodotObject;
@ -50,124 +48,169 @@ class Wrapped {
protected: protected:
Wrapped() = default; Wrapped() = default;
Wrapped(internal::empty_constructor empty) {}
public: public:
// Must be public but you should not touch this. // Must be public but you should not touch this.
GodotObject *_owner = nullptr; GodotObject *_owner = nullptr;
static Wrapped *_new() {
return nullptr;
}
}; };
namespace internal {
template <class T, class Enable = void>
struct Creator {
static T *_new() { return nullptr; }
};
template <class T>
struct Creator<T, typename std::enable_if<std::is_base_of_v<godot::Wrapped, T>>::type> {
static T *_new() { return T::_new(); }
};
// template <class T>
// struct Creator<T, std::false_type> {
// };
// template <class T>
// struct Creator<T, std::enable_if_t<std::is_base_of_v<godot::Wrapped, T>, bool>> {
// static T *_new() { return T::_new(); }
// };
}; // namespace internal
} // namespace godot } // namespace godot
#define GDCLASS(m_class, m_inherits) \ #ifdef DEBUG_ENABLED
private: \ #define CHECK_CLASS_CONSTRUCTOR(m_constructor, m_class) \
friend class ClassDB; \ if (unlikely(!m_constructor)) { \
\ ERR_PRINT_ONCE("Constructor for class " #m_class "not found. Likely wasn't registered in ClassDB."); \
using SelfType = m_class; \ return nullptr; \
\ } else \
protected: \ ((void)0)
static void (*_get_bind_methods())() { \ #else
return &m_class::_bind_methods; \ #define CHECK_CLASS_CONSTRUCTOR(m_constructor, m_class)
} \ #endif
\
m_class(godot::GodotObject *owner) : m_inherits(godot::internal::empty_constructor()) { \ #define GDCLASS(m_class, m_inherits) \
_owner = owner; \ private: \
} \ friend class ClassDB; \
\ \
m_class(godot::internal::empty_constructor empty) : m_inherits(empty) {} \ using SelfType = m_class; \
\ \
template <class T> \ protected: \
static void register_virtuals() { \ static void (*_get_bind_methods())() { \
m_inherits::register_virtuals<T>(); \ return &m_class::_bind_methods; \
} \ } \
\ \
public: \ template <class T> \
static void initialize_class() { \ static void register_virtuals() { \
static bool initialized = false; \ m_inherits::register_virtuals<T>(); \
if (initialized) { \ } \
return; \ \
} \ public: \
m_inherits::initialize_class(); \ static void initialize_class() { \
if (m_class::_get_bind_methods() != m_inherits::_get_bind_methods()) { \ static bool initialized = false; \
_bind_methods(); \ if (initialized) { \
m_inherits::register_virtuals<m_class>(); \ return; \
} \ } \
initialized = true; \ m_inherits::initialize_class(); \
} \ if (m_class::_get_bind_methods() != m_inherits::_get_bind_methods()) { \
\ _bind_methods(); \
static const char *get_class_static() { \ m_inherits::register_virtuals<m_class>(); \
return #m_class; \ } \
} \ initialized = true; \
\ } \
static const char *get_parent_class_static() { \ \
return #m_inherits; \ static const char *get_class_static() { \
} \ return #m_class; \
\ } \
static GDExtensionClassInstancePtr create(void *data) { \ \
return (GDExtensionClassInstancePtr)godot::Memory::alloc_static(sizeof(m_class)); \ static const char *get_parent_class_static() { \
} \ return #m_inherits; \
\ } \
static void free(void *data, GDExtensionClassInstancePtr ptr) { \ \
godot::memdelete(reinterpret_cast<m_class *>(ptr)); \ static GDExtensionClassInstancePtr create(void *data) { \
} \ return reinterpret_cast<GDExtensionClassInstancePtr>(new ("") m_class); \
\ } \
static void set_object_instance(GDExtensionClassInstancePtr p_instance, GDNativeObjectPtr p_object_instance) { \ \
memnew_placement((void *)p_instance, m_class((godot::GodotObject *)p_object_instance)); \ static void free(void *data, GDExtensionClassInstancePtr ptr) { \
} \ Memory::free_static(reinterpret_cast<m_class *>(ptr)); \
\ } \
static void *___binding_create_callback(void *p_token, void *p_instance) { \ \
return memnew(m_class((godot::GodotObject *)p_instance)); \ static void set_object_instance(GDExtensionClassInstancePtr p_instance, GDNativeObjectPtr p_object_instance) { \
} \ reinterpret_cast<m_class *>(p_instance)->_owner = reinterpret_cast<GodotObject *>(p_object_instance); \
static void ___binding_free_callback(void *p_token, void *p_instance, void *p_binding) { \ } \
memdelete((m_class *)p_binding); \ \
} \ static void *___binding_create_callback(void *p_token, void *p_instance) { \
static GDNativeBool ___binding_reference_callback(void *p_token, void *p_instance, GDNativeBool p_reference) { \ m_class *result = new ("") m_class; \
return true; \ result->_owner = reinterpret_cast<godot::GodotObject *>(p_instance); \
} \ return result; \
static constexpr GDNativeInstanceBindingCallbacks ___binding_callbacks = { \ } \
___binding_create_callback, \ static void ___binding_free_callback(void *p_token, void *p_instance, void *p_binding) { \
___binding_free_callback, \ Memory::free_static(reinterpret_cast<m_class *>(p_binding)); \
___binding_reference_callback, \ } \
}; \ static GDNativeBool ___binding_reference_callback(void *p_token, void *p_instance, GDNativeBool p_reference) { \
\ return true; \
} \
static constexpr GDNativeInstanceBindingCallbacks ___binding_callbacks = { \
___binding_create_callback, \
___binding_free_callback, \
___binding_reference_callback, \
}; \
\
static m_class *_new() { \
static GDNativeClassConstructor ___constructor = godot::internal::interface->classdb_get_constructor(#m_class); \
CHECK_CLASS_CONSTRUCTOR(___constructor, m_class); \
GDNativeObjectPtr obj = ___constructor(); \
return reinterpret_cast<m_class *>(godot::internal::interface->object_get_instance_binding(obj, godot::internal::token, &m_class::___binding_callbacks)); \
} \
\
private: private:
// Don't use this for your classes, use GDCLASS() instead. // Don't use this for your classes, use GDCLASS() instead.
#define GDNATIVE_CLASS(m_class, m_inherits) \ #define GDNATIVE_CLASS(m_class, m_inherits) \
protected: \ protected: \
static void (*_get_bind_methods())() { \ static void (*_get_bind_methods())() { \
return nullptr; \ return nullptr; \
} \ } \
m_class(godot::internal::empty_constructor empty) : m_inherits(empty) {} \ \
\ public: \
public: \ static void initialize_class() {} \
static void initialize_class() {} \ \
\ static const char *get_class_static() { \
static const char *get_class_static() { \ return #m_class; \
return #m_class; \ } \
} \ \
\ static const char *get_parent_class_static() { \
static const char *get_parent_class_static() { \ return #m_inherits; \
return #m_inherits; \ } \
} \ \
\ static void *___binding_create_callback(void *p_token, void *p_instance) { \
static void *___binding_create_callback(void *p_token, void *p_instance) { \ m_class *obj = new ("") m_class; \
m_class *obj = memnew(m_class(godot::internal::empty_constructor())); \ obj->_owner = (godot::GodotObject *)p_instance; \
obj->_owner = (godot::GodotObject *)p_instance; \ return obj; \
return obj; \ } \
} \ static void ___binding_free_callback(void *p_token, void *p_instance, void *p_binding) { \
static void ___binding_free_callback(void *p_token, void *p_instance, void *p_binding) { \ Memory::free_static(reinterpret_cast<m_class *>(p_binding)); \
memdelete((m_class *)p_binding); \ } \
} \ static GDNativeBool ___binding_reference_callback(void *p_token, void *p_instance, GDNativeBool p_reference) { \
static GDNativeBool ___binding_reference_callback(void *p_token, void *p_instance, GDNativeBool p_reference) { \ return true; \
return true; \ } \
} \ static constexpr GDNativeInstanceBindingCallbacks ___binding_callbacks = { \
static constexpr GDNativeInstanceBindingCallbacks ___binding_callbacks = { \ ___binding_create_callback, \
___binding_create_callback, \ ___binding_free_callback, \
___binding_free_callback, \ ___binding_reference_callback, \
___binding_reference_callback, \ }; \
}; \ static m_class *_new() { \
\ static GDNativeClassConstructor ___constructor = godot::internal::interface->classdb_get_constructor(#m_class); \
CHECK_CLASS_CONSTRUCTOR(___constructor, m_class); \
GDNativeObjectPtr obj = ___constructor(); \
return reinterpret_cast<m_class *>(godot::internal::interface->object_get_instance_binding(obj, godot::internal::token, &m_class::___binding_callbacks)); \
} \
\
private: private:
#endif // ! GODOT_CPP_WRAPPED_HPP #endif // ! GODOT_CPP_WRAPPED_HPP

View File

@ -36,6 +36,9 @@
#include <godot_cpp/core/defs.hpp> #include <godot_cpp/core/defs.hpp>
#include <godot_cpp/core/error_macros.hpp> #include <godot_cpp/core/error_macros.hpp>
#include <godot_cpp/godot.hpp>
#include <type_traits>
void *operator new(size_t p_size, const char *p_description); ///< operator new that takes a description and uses MemoryStaticPool void *operator new(size_t p_size, const char *p_description); ///< operator new that takes a description and uses MemoryStaticPool
void *operator new(size_t p_size, void *p_pointer, size_t check, const char *p_description); ///< operator new that takes a description and uses a pointer to the preallocated memory void *operator new(size_t p_size, void *p_pointer, size_t check, const char *p_description); ///< operator new that takes a description and uses a pointer to the preallocated memory
@ -54,6 +57,8 @@ void operator delete(void *p_mem, void *p_pointer, size_t check, const char *p_d
namespace godot { namespace godot {
class Wrapped;
class Memory { class Memory {
Memory(); Memory();
@ -63,11 +68,19 @@ public:
static void free_static(void *p_ptr); static void free_static(void *p_ptr);
}; };
#define memnew(m_v) (new ("") m_v) #define memnew(m_v) \
([&]() { \
if constexpr (std::is_base_of<godot::Object, decltype(m_v)>::value) { \
return godot::internal::Creator<decltype(m_v)>::_new(); \
} else { \
return new ("") m_v; \
} \
}())
#define memnew_placement(m_placement, m_class) (new (m_placement, sizeof(m_class), "") m_class) #define memnew_placement(m_placement, m_class) (new (m_placement, sizeof(m_class), "") m_class)
template <class T> template <class T>
void memdelete(T *p_class) { void memdelete(T *p_class, typename std::enable_if<!std::is_base_of_v<godot::Wrapped, T>>::type * = 0) {
if (!__has_trivial_destructor(T)) { if (!__has_trivial_destructor(T)) {
p_class->~T(); p_class->~T();
} }
@ -75,6 +88,11 @@ void memdelete(T *p_class) {
Memory::free_static(p_class); Memory::free_static(p_class);
} }
template <class T, std::enable_if_t<std::is_base_of_v<godot::Wrapped, T>, bool> = true>
void memdelete(T *p_class) {
godot::internal::interface->object_destroy(p_class->_owner);
}
#define memnew_arr(m_class, m_count) memnew_arr_template<m_class>(m_count) #define memnew_arr(m_class, m_count) memnew_arr_template<m_class>(m_count)
template <typename T> template <typename T>

View File

@ -194,7 +194,7 @@ public:
template <class T> template <class T>
MethodBind *create_vararg_method_bind(Variant (T::*p_method)(const Variant **, GDNativeInt, GDNativeCallError &), const MethodInfo &p_info, bool p_return_nil_is_variant) { MethodBind *create_vararg_method_bind(Variant (T::*p_method)(const Variant **, GDNativeInt, GDNativeCallError &), const MethodInfo &p_info, bool p_return_nil_is_variant) {
MethodBindVarArg<T> *a = memnew((MethodBindVarArg<T>)); MethodBindVarArg<T> *a = memnew(MethodBindVarArg<T>());
a->set_method(p_method); a->set_method(p_method);
a->set_method_info(p_info, p_return_nil_is_variant); a->set_method_info(p_info, p_return_nil_is_variant);
a->set_instance_class(T::get_class_static()); a->set_instance_class(T::get_class_static());