Update for virtual method compatibility system
parent
27ffd8c6be
commit
5c9529fc84
|
@ -1788,7 +1788,7 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
|
||||||
# condition returns false (in such cases it can't compile due to ambiguity).
|
# condition returns false (in such cases it can't compile due to ambiguity).
|
||||||
f"\t\tif constexpr (!std::is_same_v<decltype(&B::{method_name}), decltype(&T::{method_name})>) {{"
|
f"\t\tif constexpr (!std::is_same_v<decltype(&B::{method_name}), decltype(&T::{method_name})>) {{"
|
||||||
)
|
)
|
||||||
result.append(f"\t\t\tBIND_VIRTUAL_METHOD(T, {method_name});")
|
result.append(f"\t\t\tBIND_VIRTUAL_METHOD(T, {method_name}, {method['hash']});")
|
||||||
result.append("\t\t}")
|
result.append("\t\t}")
|
||||||
|
|
||||||
result.append("\t}")
|
result.append("\t}")
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -273,7 +273,9 @@ typedef GDExtensionObjectPtr (*GDExtensionClassCreateInstance2)(void *p_class_us
|
||||||
typedef void (*GDExtensionClassFreeInstance)(void *p_class_userdata, GDExtensionClassInstancePtr p_instance);
|
typedef void (*GDExtensionClassFreeInstance)(void *p_class_userdata, GDExtensionClassInstancePtr p_instance);
|
||||||
typedef GDExtensionClassInstancePtr (*GDExtensionClassRecreateInstance)(void *p_class_userdata, GDExtensionObjectPtr p_object);
|
typedef GDExtensionClassInstancePtr (*GDExtensionClassRecreateInstance)(void *p_class_userdata, GDExtensionObjectPtr p_object);
|
||||||
typedef GDExtensionClassCallVirtual (*GDExtensionClassGetVirtual)(void *p_class_userdata, GDExtensionConstStringNamePtr p_name);
|
typedef GDExtensionClassCallVirtual (*GDExtensionClassGetVirtual)(void *p_class_userdata, GDExtensionConstStringNamePtr p_name);
|
||||||
|
typedef GDExtensionClassCallVirtual (*GDExtensionClassGetVirtual2)(void *p_class_userdata, GDExtensionConstStringNamePtr p_name, uint32_t p_hash);
|
||||||
typedef void *(*GDExtensionClassGetVirtualCallData)(void *p_class_userdata, GDExtensionConstStringNamePtr p_name);
|
typedef void *(*GDExtensionClassGetVirtualCallData)(void *p_class_userdata, GDExtensionConstStringNamePtr p_name);
|
||||||
|
typedef void *(*GDExtensionClassGetVirtualCallData2)(void *p_class_userdata, GDExtensionConstStringNamePtr p_name, uint32_t p_hash);
|
||||||
typedef void (*GDExtensionClassCallVirtualWithData)(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name, void *p_virtual_call_userdata, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_ret);
|
typedef void (*GDExtensionClassCallVirtualWithData)(GDExtensionClassInstancePtr p_instance, GDExtensionConstStringNamePtr p_name, void *p_virtual_call_userdata, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_ret);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -383,14 +385,14 @@ typedef struct {
|
||||||
GDExtensionClassFreeInstance free_instance_func; // Destructor; mandatory.
|
GDExtensionClassFreeInstance free_instance_func; // Destructor; mandatory.
|
||||||
GDExtensionClassRecreateInstance recreate_instance_func;
|
GDExtensionClassRecreateInstance recreate_instance_func;
|
||||||
// Queries a virtual function by name and returns a callback to invoke the requested virtual function.
|
// Queries a virtual function by name and returns a callback to invoke the requested virtual function.
|
||||||
GDExtensionClassGetVirtual get_virtual_func;
|
GDExtensionClassGetVirtual2 get_virtual_func;
|
||||||
// Paired with `call_virtual_with_data_func`, this is an alternative to `get_virtual_func` for extensions that
|
// Paired with `call_virtual_with_data_func`, this is an alternative to `get_virtual_func` for extensions that
|
||||||
// need or benefit from extra data when calling virtual functions.
|
// need or benefit from extra data when calling virtual functions.
|
||||||
// Returns user data that will be passed to `call_virtual_with_data_func`.
|
// Returns user data that will be passed to `call_virtual_with_data_func`.
|
||||||
// Returning `NULL` from this function signals to Godot that the virtual function is not overridden.
|
// Returning `NULL` from this function signals to Godot that the virtual function is not overridden.
|
||||||
// Data returned from this function should be managed by the extension and must be valid until the extension is deinitialized.
|
// Data returned from this function should be managed by the extension and must be valid until the extension is deinitialized.
|
||||||
// You should supply either `get_virtual_func`, or `get_virtual_call_data_func` with `call_virtual_with_data_func`.
|
// You should supply either `get_virtual_func`, or `get_virtual_call_data_func` with `call_virtual_with_data_func`.
|
||||||
GDExtensionClassGetVirtualCallData get_virtual_call_data_func;
|
GDExtensionClassGetVirtualCallData2 get_virtual_call_data_func;
|
||||||
// Used to call virtual functions when `get_virtual_call_data_func` is not null.
|
// Used to call virtual functions when `get_virtual_call_data_func` is not null.
|
||||||
GDExtensionClassCallVirtualWithData call_virtual_with_data_func;
|
GDExtensionClassCallVirtualWithData call_virtual_with_data_func;
|
||||||
void *class_userdata; // Per-class user data, later accessible in instance bindings.
|
void *class_userdata; // Per-class user data, later accessible in instance bindings.
|
||||||
|
|
|
@ -88,12 +88,17 @@ class ClassDB {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
struct ClassInfo {
|
struct ClassInfo {
|
||||||
|
struct VirtualMethod {
|
||||||
|
GDExtensionClassCallVirtual func;
|
||||||
|
uint32_t hash;
|
||||||
|
};
|
||||||
|
|
||||||
StringName name;
|
StringName name;
|
||||||
StringName parent_name;
|
StringName parent_name;
|
||||||
GDExtensionInitializationLevel level = GDEXTENSION_INITIALIZATION_SCENE;
|
GDExtensionInitializationLevel level = GDEXTENSION_INITIALIZATION_SCENE;
|
||||||
std::unordered_map<StringName, MethodBind *> method_map;
|
std::unordered_map<StringName, MethodBind *> method_map;
|
||||||
std::set<StringName> signal_names;
|
std::set<StringName> signal_names;
|
||||||
std::unordered_map<StringName, GDExtensionClassCallVirtual> virtual_methods;
|
std::unordered_map<StringName, VirtualMethod> virtual_methods;
|
||||||
std::set<StringName> property_names;
|
std::set<StringName> property_names;
|
||||||
std::set<StringName> constant_names;
|
std::set<StringName> constant_names;
|
||||||
// Pointer to the parent custom class, if any. Will be null if the parent class is a Godot class.
|
// Pointer to the parent custom class, if any. Will be null if the parent class is a Godot class.
|
||||||
|
@ -193,13 +198,13 @@ public:
|
||||||
static void add_signal(const StringName &p_class, const MethodInfo &p_signal);
|
static void add_signal(const StringName &p_class, const MethodInfo &p_signal);
|
||||||
static void bind_integer_constant(const StringName &p_class_name, const StringName &p_enum_name, const StringName &p_constant_name, GDExtensionInt p_constant_value, bool p_is_bitfield = false);
|
static void bind_integer_constant(const StringName &p_class_name, const StringName &p_enum_name, const StringName &p_constant_name, GDExtensionInt p_constant_value, bool p_is_bitfield = false);
|
||||||
// Binds an implementation of a virtual method defined in Godot.
|
// Binds an implementation of a virtual method defined in Godot.
|
||||||
static void bind_virtual_method(const StringName &p_class, const StringName &p_method, GDExtensionClassCallVirtual p_call);
|
static void bind_virtual_method(const StringName &p_class, const StringName &p_method, GDExtensionClassCallVirtual p_call, uint32_t p_hash);
|
||||||
// Add a new virtual method that can be implemented by scripts.
|
// Add a new virtual method that can be implemented by scripts.
|
||||||
static void add_virtual_method(const StringName &p_class, const MethodInfo &p_method, const Vector<StringName> &p_arg_names = Vector<StringName>());
|
static void add_virtual_method(const StringName &p_class, const MethodInfo &p_method, const Vector<StringName> &p_arg_names = Vector<StringName>());
|
||||||
|
|
||||||
static MethodBind *get_method(const StringName &p_class, const StringName &p_method);
|
static MethodBind *get_method(const StringName &p_class, const StringName &p_method);
|
||||||
|
|
||||||
static GDExtensionClassCallVirtual get_virtual_func(void *p_userdata, GDExtensionConstStringNamePtr p_name);
|
static GDExtensionClassCallVirtual get_virtual_func(void *p_userdata, GDExtensionConstStringNamePtr p_name, uint32_t p_hash);
|
||||||
static const GDExtensionInstanceBindingCallbacks *get_instance_binding_callbacks(const StringName &p_class);
|
static const GDExtensionInstanceBindingCallbacks *get_instance_binding_callbacks(const StringName &p_class);
|
||||||
|
|
||||||
static void initialize(GDExtensionInitializationLevel p_level);
|
static void initialize(GDExtensionInitializationLevel p_level);
|
||||||
|
@ -217,12 +222,12 @@ public:
|
||||||
#define BIND_BITFIELD_FLAG(m_constant) \
|
#define BIND_BITFIELD_FLAG(m_constant) \
|
||||||
::godot::ClassDB::bind_integer_constant(get_class_static(), ::godot::_gde_constant_get_bitfield_name(m_constant, #m_constant), #m_constant, m_constant, true);
|
::godot::ClassDB::bind_integer_constant(get_class_static(), ::godot::_gde_constant_get_bitfield_name(m_constant, #m_constant), #m_constant, m_constant, true);
|
||||||
|
|
||||||
#define BIND_VIRTUAL_METHOD(m_class, m_method) \
|
#define BIND_VIRTUAL_METHOD(m_class, m_method, m_hash) \
|
||||||
{ \
|
{ \
|
||||||
auto _call##m_method = [](GDExtensionObjectPtr p_instance, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr p_ret) -> void { \
|
auto _call##m_method = [](GDExtensionObjectPtr p_instance, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr p_ret) -> void { \
|
||||||
call_with_ptr_args(reinterpret_cast<m_class *>(p_instance), &m_class::m_method, p_args, p_ret); \
|
call_with_ptr_args(reinterpret_cast<m_class *>(p_instance), &m_class::m_method, p_args, p_ret); \
|
||||||
}; \
|
}; \
|
||||||
::godot::ClassDB::bind_virtual_method(m_class::get_class_static(), #m_method, _call##m_method); \
|
::godot::ClassDB::bind_virtual_method(m_class::get_class_static(), #m_method, _call##m_method, m_hash); \
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, bool is_abstract>
|
template <typename T, bool is_abstract>
|
||||||
|
|
|
@ -285,7 +285,7 @@ void ClassDB::bind_integer_constant(const StringName &p_class_name, const String
|
||||||
// Register it with Godot
|
// Register it with Godot
|
||||||
internal::gdextension_interface_classdb_register_extension_class_integer_constant(internal::library, p_class_name._native_ptr(), p_enum_name._native_ptr(), p_constant_name._native_ptr(), p_constant_value, p_is_bitfield);
|
internal::gdextension_interface_classdb_register_extension_class_integer_constant(internal::library, p_class_name._native_ptr(), p_enum_name._native_ptr(), p_constant_name._native_ptr(), p_constant_value, p_is_bitfield);
|
||||||
}
|
}
|
||||||
GDExtensionClassCallVirtual ClassDB::get_virtual_func(void *p_userdata, GDExtensionConstStringNamePtr p_name) {
|
GDExtensionClassCallVirtual ClassDB::get_virtual_func(void *p_userdata, GDExtensionConstStringNamePtr p_name, uint32_t p_hash) {
|
||||||
// This is called by Godot the first time it calls a virtual function, and it caches the result, per object instance.
|
// This is called by Godot the first time it calls a virtual function, and it caches the result, per object instance.
|
||||||
// Because of this, it can happen from different threads at once.
|
// Because of this, it can happen from different threads at once.
|
||||||
// It should be ok not using any mutex as long as we only READ data.
|
// It should be ok not using any mutex as long as we only READ data.
|
||||||
|
@ -299,10 +299,10 @@ GDExtensionClassCallVirtual ClassDB::get_virtual_func(void *p_userdata, GDExtens
|
||||||
|
|
||||||
// Find method in current class, or any of its parent classes (Godot classes not included)
|
// Find method in current class, or any of its parent classes (Godot classes not included)
|
||||||
while (type != nullptr) {
|
while (type != nullptr) {
|
||||||
std::unordered_map<StringName, GDExtensionClassCallVirtual>::const_iterator method_it = type->virtual_methods.find(*name);
|
std::unordered_map<StringName, ClassInfo::VirtualMethod>::const_iterator method_it = type->virtual_methods.find(*name);
|
||||||
|
|
||||||
if (method_it != type->virtual_methods.end()) {
|
if (method_it != type->virtual_methods.end() && method_it->second.hash == p_hash) {
|
||||||
return method_it->second;
|
return method_it->second.func;
|
||||||
}
|
}
|
||||||
|
|
||||||
type = type->parent_ptr;
|
type = type->parent_ptr;
|
||||||
|
@ -328,7 +328,7 @@ const GDExtensionInstanceBindingCallbacks *ClassDB::get_instance_binding_callbac
|
||||||
return callbacks_it->second;
|
return callbacks_it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClassDB::bind_virtual_method(const StringName &p_class, const StringName &p_method, GDExtensionClassCallVirtual p_call) {
|
void ClassDB::bind_virtual_method(const StringName &p_class, const StringName &p_method, GDExtensionClassCallVirtual p_call, uint32_t p_hash) {
|
||||||
std::unordered_map<StringName, ClassInfo>::iterator type_it = classes.find(p_class);
|
std::unordered_map<StringName, ClassInfo>::iterator type_it = classes.find(p_class);
|
||||||
ERR_FAIL_COND_MSG(type_it == classes.end(), String("Class '{0}' doesn't exist.").format(Array::make(p_class)));
|
ERR_FAIL_COND_MSG(type_it == classes.end(), String("Class '{0}' doesn't exist.").format(Array::make(p_class)));
|
||||||
|
|
||||||
|
@ -337,7 +337,10 @@ void ClassDB::bind_virtual_method(const StringName &p_class, const StringName &p
|
||||||
ERR_FAIL_COND_MSG(type.method_map.find(p_method) != type.method_map.end(), String("Method '{0}::{1}()' already registered as non-virtual.").format(Array::make(p_class, p_method)));
|
ERR_FAIL_COND_MSG(type.method_map.find(p_method) != type.method_map.end(), String("Method '{0}::{1}()' already registered as non-virtual.").format(Array::make(p_class, p_method)));
|
||||||
ERR_FAIL_COND_MSG(type.virtual_methods.find(p_method) != type.virtual_methods.end(), String("Virtual '{0}::{1}()' method already registered.").format(Array::make(p_class, p_method)));
|
ERR_FAIL_COND_MSG(type.virtual_methods.find(p_method) != type.virtual_methods.end(), String("Virtual '{0}::{1}()' method already registered.").format(Array::make(p_class, p_method)));
|
||||||
|
|
||||||
type.virtual_methods[p_method] = p_call;
|
type.virtual_methods[p_method] = ClassInfo::VirtualMethod{
|
||||||
|
p_call,
|
||||||
|
p_hash,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClassDB::add_virtual_method(const StringName &p_class, const MethodInfo &p_method, const Vector<StringName> &p_arg_names) {
|
void ClassDB::add_virtual_method(const StringName &p_class, const MethodInfo &p_method, const Vector<StringName> &p_arg_names) {
|
||||||
|
|
Loading…
Reference in New Issue