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->free_instance_func, // GDExtensionClassFreeInstance free_instance_func; /* this one is mandatory */
|
||||
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->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.free_instance = p_extension_funcs->free_instance_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;
|
||||
|
||||
ClassDB::register_extension_class(&extension->gdextension);
|
||||
|
|
|
@ -265,9 +265,11 @@ typedef void (*GDExtensionClassToString)(GDExtensionClassInstancePtr p_instance,
|
|||
typedef void (*GDExtensionClassReference)(GDExtensionClassInstancePtr p_instance);
|
||||
typedef void (*GDExtensionClassUnreference)(GDExtensionClassInstancePtr p_instance);
|
||||
typedef void (*GDExtensionClassCallVirtual)(GDExtensionClassInstancePtr p_instance, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_ret);
|
||||
typedef GDExtensionObjectPtr (*GDExtensionClassCreateInstance)(void *p_userdata);
|
||||
typedef void (*GDExtensionClassFreeInstance)(void *p_userdata, GDExtensionClassInstancePtr p_instance);
|
||||
typedef GDExtensionClassCallVirtual (*GDExtensionClassGetVirtual)(void *p_userdata, GDExtensionConstStringNamePtr p_name);
|
||||
typedef GDExtensionObjectPtr (*GDExtensionClassCreateInstance)(void *p_class_userdata);
|
||||
typedef void (*GDExtensionClassFreeInstance)(void *p_class_userdata, GDExtensionClassInstancePtr p_instance);
|
||||
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 {
|
||||
GDExtensionBool is_virtual;
|
||||
|
@ -306,7 +308,17 @@ typedef struct {
|
|||
GDExtensionClassUnreference unreference_func;
|
||||
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.
|
||||
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;
|
||||
void *class_userdata; // Per-class user data, later accessible in instance bindings.
|
||||
} GDExtensionClassCreationInfo2;
|
||||
|
|
|
@ -2,7 +2,7 @@ proto = """
|
|||
#define GDVIRTUAL$VER($RET m_name $ARG) \\
|
||||
StringName _gdvirtual_##m_name##_sn = #m_name;\\
|
||||
mutable bool _gdvirtual_##m_name##_initialized = false;\\
|
||||
mutable GDExtensionClassCallVirtual _gdvirtual_##m_name = nullptr;\\
|
||||
mutable void* _gdvirtual_##m_name = nullptr;\\
|
||||
template<bool required>\\
|
||||
_FORCE_INLINE_ bool _gdvirtual_##m_name##_call($CALLARGS) $CONST { \\
|
||||
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)) {\\
|
||||
/* TODO: C-style cast because GDExtensionStringNamePtr's const qualifier is broken (see https://github.com/godotengine/godot/pull/67751) */\\
|
||||
_gdvirtual_##m_name = (_get_extension() && _get_extension()->get_virtual) ? _get_extension()->get_virtual(_get_extension()->class_userdata, (GDExtensionStringNamePtr)&_gdvirtual_##m_name##_sn) : (GDExtensionClassCallVirtual) nullptr;\\
|
||||
_gdvirtual_##m_name = 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;\\
|
||||
}\\
|
||||
if (_gdvirtual_##m_name) {\\
|
||||
$CALLPTRARGS\\
|
||||
$CALLPTRRETDEF\\
|
||||
_gdvirtual_##m_name(_get_extension_instance(),$CALLPTRARGPASS,$CALLPTRRETPASS);\\
|
||||
$CALLPTRRET\\
|
||||
if (_get_extension()->get_virtual_call_data && _get_extension()->call_virtual_with_data) {\\
|
||||
_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;\\
|
||||
}\\
|
||||
\\
|
||||
|
@ -41,8 +50,12 @@ _FORCE_INLINE_ bool _gdvirtual_##m_name##_overridden() const { \\
|
|||
return _script_instance->has_method(_gdvirtual_##m_name##_sn);\\
|
||||
}\\
|
||||
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 = (_get_extension() && _get_extension()->get_virtual) ? _get_extension()->get_virtual(_get_extension()->class_userdata, (GDExtensionStringNamePtr)&_gdvirtual_##m_name##_sn) : (GDExtensionClassCallVirtual) nullptr;\\
|
||||
_gdvirtual_##m_name = 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;\\
|
||||
}\\
|
||||
if (_gdvirtual_##m_name) {\\
|
||||
|
|
|
@ -347,6 +347,8 @@ struct ObjectGDExtension {
|
|||
GDExtensionClassCreateInstance create_instance;
|
||||
GDExtensionClassFreeInstance free_instance;
|
||||
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__)
|
||||
|
|
Loading…
Reference in New Issue