Add functions for non-ptr style virtual calls in GDExtension
This adds two functions to `GDExtensionClassCreationInfo` that allow for developers to supply a generic virtual call function along with user data to be sent to that call. If `get_virutal_call_data_func` is not null, extensions call this function to get user data to pass to a supplied `call_virtual_with_data_func`. Both must be provided is one is provided. If `get_virtual_call_data_func` is null, Godot falls back to the old `get_virtual_func` logic. Fixes #63275 Co-authored-by: David Snopek <dsnopek@gmail.com>
This commit is contained in:
parent
571cd0eb79
commit
60851af4da
|
@ -301,6 +301,8 @@ void GDExtension::_register_extension_class(GDExtensionClassLibraryPtr p_library
|
||||||
p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance create_instance_func; /* this one is mandatory */
|
p_extension_funcs->create_instance_func, // GDExtensionClassCreateInstance create_instance_func; /* this one is mandatory */
|
||||||
p_extension_funcs->free_instance_func, // GDExtensionClassFreeInstance free_instance_func; /* this one is mandatory */
|
p_extension_funcs->free_instance_func, // GDExtensionClassFreeInstance free_instance_func; /* this one is mandatory */
|
||||||
p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func;
|
p_extension_funcs->get_virtual_func, // GDExtensionClassGetVirtual get_virtual_func;
|
||||||
|
nullptr, // GDExtensionClassGetVirtualCallData get_virtual_call_data_func;
|
||||||
|
nullptr, // GDExtensionClassCallVirtualWithData call_virtual_func;
|
||||||
p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid;
|
p_extension_funcs->get_rid_func, // GDExtensionClassGetRID get_rid;
|
||||||
p_extension_funcs->class_userdata, // void *class_userdata;
|
p_extension_funcs->class_userdata, // void *class_userdata;
|
||||||
};
|
};
|
||||||
|
@ -375,6 +377,8 @@ void GDExtension::_register_extension_class_internal(GDExtensionClassLibraryPtr
|
||||||
extension->gdextension.create_instance = p_extension_funcs->create_instance_func;
|
extension->gdextension.create_instance = p_extension_funcs->create_instance_func;
|
||||||
extension->gdextension.free_instance = p_extension_funcs->free_instance_func;
|
extension->gdextension.free_instance = p_extension_funcs->free_instance_func;
|
||||||
extension->gdextension.get_virtual = p_extension_funcs->get_virtual_func;
|
extension->gdextension.get_virtual = p_extension_funcs->get_virtual_func;
|
||||||
|
extension->gdextension.get_virtual_call_data = p_extension_funcs->get_virtual_call_data_func;
|
||||||
|
extension->gdextension.call_virtual_with_data = p_extension_funcs->call_virtual_with_data_func;
|
||||||
extension->gdextension.get_rid = p_extension_funcs->get_rid_func;
|
extension->gdextension.get_rid = p_extension_funcs->get_rid_func;
|
||||||
|
|
||||||
ClassDB::register_extension_class(&extension->gdextension);
|
ClassDB::register_extension_class(&extension->gdextension);
|
||||||
|
|
|
@ -265,9 +265,11 @@ typedef void (*GDExtensionClassToString)(GDExtensionClassInstancePtr p_instance,
|
||||||
typedef void (*GDExtensionClassReference)(GDExtensionClassInstancePtr p_instance);
|
typedef void (*GDExtensionClassReference)(GDExtensionClassInstancePtr p_instance);
|
||||||
typedef void (*GDExtensionClassUnreference)(GDExtensionClassInstancePtr p_instance);
|
typedef void (*GDExtensionClassUnreference)(GDExtensionClassInstancePtr p_instance);
|
||||||
typedef void (*GDExtensionClassCallVirtual)(GDExtensionClassInstancePtr p_instance, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_ret);
|
typedef void (*GDExtensionClassCallVirtual)(GDExtensionClassInstancePtr p_instance, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_ret);
|
||||||
typedef GDExtensionObjectPtr (*GDExtensionClassCreateInstance)(void *p_userdata);
|
typedef GDExtensionObjectPtr (*GDExtensionClassCreateInstance)(void *p_class_userdata);
|
||||||
typedef void (*GDExtensionClassFreeInstance)(void *p_userdata, GDExtensionClassInstancePtr p_instance);
|
typedef void (*GDExtensionClassFreeInstance)(void *p_class_userdata, GDExtensionClassInstancePtr p_instance);
|
||||||
typedef GDExtensionClassCallVirtual (*GDExtensionClassGetVirtual)(void *p_userdata, GDExtensionConstStringNamePtr p_name);
|
typedef GDExtensionClassCallVirtual (*GDExtensionClassGetVirtual)(void *p_class_userdata, GDExtensionConstStringNamePtr p_name);
|
||||||
|
typedef void *(*GDExtensionClassGetVirtualCallData)(void *p_class_userdata, GDExtensionConstStringNamePtr p_name);
|
||||||
|
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 {
|
||||||
GDExtensionBool is_virtual;
|
GDExtensionBool is_virtual;
|
||||||
|
@ -306,7 +308,17 @@ typedef struct {
|
||||||
GDExtensionClassUnreference unreference_func;
|
GDExtensionClassUnreference unreference_func;
|
||||||
GDExtensionClassCreateInstance create_instance_func; // (Default) constructor; mandatory. If the class is not instantiable, consider making it virtual or abstract.
|
GDExtensionClassCreateInstance create_instance_func; // (Default) constructor; mandatory. If the class is not instantiable, consider making it virtual or abstract.
|
||||||
GDExtensionClassFreeInstance free_instance_func; // Destructor; mandatory.
|
GDExtensionClassFreeInstance free_instance_func; // Destructor; mandatory.
|
||||||
GDExtensionClassGetVirtual get_virtual_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;
|
||||||
|
// 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.
|
||||||
|
// 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.
|
||||||
|
// 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`.
|
||||||
|
GDExtensionClassGetVirtualCallData get_virtual_call_data_func;
|
||||||
|
// Used to call virtual functions when `get_virtual_call_data_func` is not null.
|
||||||
|
GDExtensionClassCallVirtualWithData call_virtual_with_data_func;
|
||||||
GDExtensionClassGetRID get_rid_func;
|
GDExtensionClassGetRID get_rid_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.
|
||||||
} GDExtensionClassCreationInfo2;
|
} GDExtensionClassCreationInfo2;
|
||||||
|
|
|
@ -2,7 +2,7 @@ proto = """
|
||||||
#define GDVIRTUAL$VER($RET m_name $ARG) \\
|
#define GDVIRTUAL$VER($RET m_name $ARG) \\
|
||||||
StringName _gdvirtual_##m_name##_sn = #m_name;\\
|
StringName _gdvirtual_##m_name##_sn = #m_name;\\
|
||||||
mutable bool _gdvirtual_##m_name##_initialized = false;\\
|
mutable bool _gdvirtual_##m_name##_initialized = false;\\
|
||||||
mutable GDExtensionClassCallVirtual _gdvirtual_##m_name = nullptr;\\
|
mutable void* _gdvirtual_##m_name = nullptr;\\
|
||||||
template<bool required>\\
|
template<bool required>\\
|
||||||
_FORCE_INLINE_ bool _gdvirtual_##m_name##_call($CALLARGS) $CONST { \\
|
_FORCE_INLINE_ bool _gdvirtual_##m_name##_call($CALLARGS) $CONST { \\
|
||||||
ScriptInstance *_script_instance = ((Object*)(this))->get_script_instance();\\
|
ScriptInstance *_script_instance = ((Object*)(this))->get_script_instance();\\
|
||||||
|
@ -16,15 +16,24 @@ _FORCE_INLINE_ bool _gdvirtual_##m_name##_call($CALLARGS) $CONST { \\
|
||||||
} \\
|
} \\
|
||||||
}\\
|
}\\
|
||||||
if (unlikely(_get_extension() && !_gdvirtual_##m_name##_initialized)) {\\
|
if (unlikely(_get_extension() && !_gdvirtual_##m_name##_initialized)) {\\
|
||||||
/* TODO: C-style cast because GDExtensionStringNamePtr's const qualifier is broken (see https://github.com/godotengine/godot/pull/67751) */\\
|
_gdvirtual_##m_name = nullptr;\\
|
||||||
_gdvirtual_##m_name = (_get_extension() && _get_extension()->get_virtual) ? _get_extension()->get_virtual(_get_extension()->class_userdata, (GDExtensionStringNamePtr)&_gdvirtual_##m_name##_sn) : (GDExtensionClassCallVirtual) nullptr;\\
|
if (_get_extension()->get_virtual_call_data && _get_extension()->call_virtual_with_data) {\\
|
||||||
|
_gdvirtual_##m_name = _get_extension()->get_virtual_call_data(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\
|
||||||
|
} else if (_get_extension()->get_virtual) {\\
|
||||||
|
_gdvirtual_##m_name = (void *)_get_extension()->get_virtual(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\
|
||||||
|
}\\
|
||||||
_gdvirtual_##m_name##_initialized = true;\\
|
_gdvirtual_##m_name##_initialized = true;\\
|
||||||
}\\
|
}\\
|
||||||
if (_gdvirtual_##m_name) {\\
|
if (_gdvirtual_##m_name) {\\
|
||||||
$CALLPTRARGS\\
|
$CALLPTRARGS\\
|
||||||
$CALLPTRRETDEF\\
|
$CALLPTRRETDEF\\
|
||||||
_gdvirtual_##m_name(_get_extension_instance(),$CALLPTRARGPASS,$CALLPTRRETPASS);\\
|
if (_get_extension()->get_virtual_call_data && _get_extension()->call_virtual_with_data) {\\
|
||||||
$CALLPTRRET\\
|
_get_extension()->call_virtual_with_data(_get_extension_instance(), &_gdvirtual_##m_name##_sn, _gdvirtual_##m_name, $CALLPTRARGPASS,$CALLPTRRETPASS);\\
|
||||||
|
$CALLPTRRET\\
|
||||||
|
} else {\\
|
||||||
|
((GDExtensionClassCallVirtual)_gdvirtual_##m_name)(_get_extension_instance(),$CALLPTRARGPASS,$CALLPTRRETPASS);\\
|
||||||
|
$CALLPTRRET\\
|
||||||
|
}\\
|
||||||
return true;\\
|
return true;\\
|
||||||
}\\
|
}\\
|
||||||
\\
|
\\
|
||||||
|
@ -41,8 +50,12 @@ _FORCE_INLINE_ bool _gdvirtual_##m_name##_overridden() const { \\
|
||||||
return _script_instance->has_method(_gdvirtual_##m_name##_sn);\\
|
return _script_instance->has_method(_gdvirtual_##m_name##_sn);\\
|
||||||
}\\
|
}\\
|
||||||
if (unlikely(_get_extension() && !_gdvirtual_##m_name##_initialized)) {\\
|
if (unlikely(_get_extension() && !_gdvirtual_##m_name##_initialized)) {\\
|
||||||
/* TODO: C-style cast because GDExtensionStringNamePtr's const qualifier is broken (see https://github.com/godotengine/godot/pull/67751) */\\
|
_gdvirtual_##m_name = nullptr;\\
|
||||||
_gdvirtual_##m_name = (_get_extension() && _get_extension()->get_virtual) ? _get_extension()->get_virtual(_get_extension()->class_userdata, (GDExtensionStringNamePtr)&_gdvirtual_##m_name##_sn) : (GDExtensionClassCallVirtual) nullptr;\\
|
if (_get_extension()->get_virtual_call_data && _get_extension()->call_virtual_with_data) {\\
|
||||||
|
_gdvirtual_##m_name = _get_extension()->get_virtual_call_data(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\
|
||||||
|
} else if (_get_extension()->get_virtual) {\\
|
||||||
|
_gdvirtual_##m_name = (void *)_get_extension()->get_virtual(_get_extension()->class_userdata, &_gdvirtual_##m_name##_sn);\\
|
||||||
|
}\\
|
||||||
_gdvirtual_##m_name##_initialized = true;\\
|
_gdvirtual_##m_name##_initialized = true;\\
|
||||||
}\\
|
}\\
|
||||||
if (_gdvirtual_##m_name) {\\
|
if (_gdvirtual_##m_name) {\\
|
||||||
|
|
|
@ -347,6 +347,8 @@ struct ObjectGDExtension {
|
||||||
GDExtensionClassCreateInstance create_instance;
|
GDExtensionClassCreateInstance create_instance;
|
||||||
GDExtensionClassFreeInstance free_instance;
|
GDExtensionClassFreeInstance free_instance;
|
||||||
GDExtensionClassGetVirtual get_virtual;
|
GDExtensionClassGetVirtual get_virtual;
|
||||||
|
GDExtensionClassGetVirtualCallData get_virtual_call_data;
|
||||||
|
GDExtensionClassCallVirtualWithData call_virtual_with_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define GDVIRTUAL_CALL(m_name, ...) _gdvirtual_##m_name##_call<false>(__VA_ARGS__)
|
#define GDVIRTUAL_CALL(m_name, ...) _gdvirtual_##m_name##_call<false>(__VA_ARGS__)
|
||||||
|
|
Loading…
Reference in New Issue