Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for getting argument count from Callables #1375

Merged
merged 1 commit into from
Mar 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 50 additions & 1 deletion gdextension/gdextension_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,8 @@ typedef GDExtensionBool (*GDExtensionCallableCustomLessThan)(void *callable_user

typedef void (*GDExtensionCallableCustomToString)(void *callable_userdata, GDExtensionBool *r_is_valid, GDExtensionStringPtr r_out);

typedef GDExtensionInt (*GDExtensionCallableCustomGetArgumentCount)(void *callable_userdata, GDExtensionBool *r_is_valid);

typedef struct {
/* Only `call_func` and `token` are strictly required, however, `object_id` should be passed if its not a static method.
*
Expand Down Expand Up @@ -471,7 +473,40 @@ typedef struct {
GDExtensionCallableCustomLessThan less_than_func;

GDExtensionCallableCustomToString to_string_func;
} GDExtensionCallableCustomInfo;
} GDExtensionCallableCustomInfo; // Deprecated. Use GDExtensionCallableCustomInfo2 instead.

typedef struct {
/* Only `call_func` and `token` are strictly required, however, `object_id` should be passed if its not a static method.
*
* `token` should point to an address that uniquely identifies the GDExtension (for example, the
* `GDExtensionClassLibraryPtr` passed to the entry symbol function.
*
* `hash_func`, `equal_func`, and `less_than_func` are optional. If not provided both `call_func` and
* `callable_userdata` together are used as the identity of the callable for hashing and comparison purposes.
*
* The hash returned by `hash_func` is cached, `hash_func` will not be called more than once per callable.
*
* `is_valid_func` is necessary if the validity of the callable can change before destruction.
*
* `free_func` is necessary if `callable_userdata` needs to be cleaned up when the callable is freed.
*/
void *callable_userdata;
void *token;

GDObjectInstanceID object_id;

GDExtensionCallableCustomCall call_func;
GDExtensionCallableCustomIsValid is_valid_func;
GDExtensionCallableCustomFree free_func;

GDExtensionCallableCustomHash hash_func;
GDExtensionCallableCustomEqual equal_func;
GDExtensionCallableCustomLessThan less_than_func;

GDExtensionCallableCustomToString to_string_func;

GDExtensionCallableCustomGetArgumentCount get_argument_count_func;
} GDExtensionCallableCustomInfo2;

/* SCRIPT INSTANCE EXTENSION */

Expand Down Expand Up @@ -2450,6 +2485,7 @@ typedef GDExtensionScriptInstanceDataPtr (*GDExtensionInterfaceObjectGetScriptIn
/**
* @name callable_custom_create
* @since 4.2
* @deprecated in Godot 4.3. Use `callable_custom_create2` instead.
*
* Creates a custom Callable object from a function pointer.
*
Expand All @@ -2460,6 +2496,19 @@ typedef GDExtensionScriptInstanceDataPtr (*GDExtensionInterfaceObjectGetScriptIn
*/
typedef void (*GDExtensionInterfaceCallableCustomCreate)(GDExtensionUninitializedTypePtr r_callable, GDExtensionCallableCustomInfo *p_callable_custom_info);

/**
* @name callable_custom_create2
* @since 4.3
*
* Creates a custom Callable object from a function pointer.
*
* Provided struct can be safely freed once the function returns.
*
* @param r_callable A pointer that will receive the new Callable.
* @param p_callable_custom_info The info required to construct a Callable.
*/
typedef void (*GDExtensionInterfaceCallableCustomCreate2)(GDExtensionUninitializedTypePtr r_callable, GDExtensionCallableCustomInfo2 *p_callable_custom_info);

/**
* @name callable_custom_get_userdata
* @since 4.2
Expand Down
2 changes: 1 addition & 1 deletion include/godot_cpp/godot.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ extern "C" GDExtensionInterfaceObjectGetInstanceFromId gdextension_interface_obj
extern "C" GDExtensionInterfaceObjectGetInstanceId gdextension_interface_object_get_instance_id;
extern "C" GDExtensionInterfaceObjectHasScriptMethod gdextension_interface_object_has_script_method;
extern "C" GDExtensionInterfaceObjectCallScriptMethod gdextension_interface_object_call_script_method;
extern "C" GDExtensionInterfaceCallableCustomCreate gdextension_interface_callable_custom_create;
extern "C" GDExtensionInterfaceCallableCustomCreate2 gdextension_interface_callable_custom_create2;
extern "C" GDExtensionInterfaceCallableCustomGetUserData gdextension_interface_callable_custom_get_userdata;
extern "C" GDExtensionInterfaceRefGetObject gdextension_interface_ref_get_object;
extern "C" GDExtensionInterfaceRefSetObject gdextension_interface_ref_set_object;
Expand Down
1 change: 1 addition & 0 deletions include/godot_cpp/variant/callable_custom.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class Object;
class CallableCustomBase {
public:
virtual ObjectID get_object() const = 0;
virtual int get_argument_count(bool &r_is_valid) const;
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const = 0;
virtual ~CallableCustomBase() {}
};
Expand Down
25 changes: 25 additions & 0 deletions include/godot_cpp/variant/callable_method_pointer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ class CallableCustomMethodPointer : public CallableCustomMethodPointerBase {
return ObjectID(data.instance->get_instance_id());
}

virtual int get_argument_count(bool &r_is_valid) const override {
r_is_valid = true;
return sizeof...(P);
}

virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const override {
call_with_variant_args(data.instance, data.method, p_arguments, p_argcount, r_call_error);
}
Expand Down Expand Up @@ -110,6 +115,11 @@ class CallableCustomMethodPointerRet : public CallableCustomMethodPointerBase {
return ObjectID(data.instance->get_instance_id());
}

virtual int get_argument_count(bool &r_is_valid) const override {
r_is_valid = true;
return sizeof...(P);
}

virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const override {
call_with_variant_args_ret(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error);
}
Expand Down Expand Up @@ -147,6 +157,11 @@ class CallableCustomMethodPointerRetC : public CallableCustomMethodPointerBase {
return ObjectID(data.instance->get_instance_id());
}

virtual int get_argument_count(bool &r_is_valid) const override {
r_is_valid = true;
return sizeof...(P);
}

virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const override {
call_with_variant_args_retc(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error);
}
Expand Down Expand Up @@ -182,6 +197,11 @@ class CallableCustomStaticMethodPointer : public CallableCustomMethodPointerBase
return ObjectID();
}

virtual int get_argument_count(bool &r_is_valid) const override {
r_is_valid = true;
return sizeof...(P);
}

virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const override {
call_with_variant_args_static_ret(data.method, p_arguments, p_argcount, r_return_value, r_call_error);
r_return_value = Variant();
Expand Down Expand Up @@ -218,6 +238,11 @@ class CallableCustomStaticMethodPointerRet : public CallableCustomMethodPointerB
return ObjectID();
}

virtual int get_argument_count(bool &r_is_valid) const override {
r_is_valid = true;
return sizeof...(P);
}

virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const override {
call_with_variant_args_static_ret(data.method, p_arguments, p_argcount, r_return_value, r_call_error);
}
Expand Down
4 changes: 2 additions & 2 deletions src/godot.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ GDExtensionInterfaceObjectGetInstanceFromId gdextension_interface_object_get_ins
GDExtensionInterfaceObjectGetInstanceId gdextension_interface_object_get_instance_id = nullptr;
GDExtensionInterfaceObjectHasScriptMethod gdextension_interface_object_has_script_method = nullptr;
GDExtensionInterfaceObjectCallScriptMethod gdextension_interface_object_call_script_method = nullptr;
GDExtensionInterfaceCallableCustomCreate gdextension_interface_callable_custom_create = nullptr;
GDExtensionInterfaceCallableCustomCreate2 gdextension_interface_callable_custom_create2 = nullptr;
GDExtensionInterfaceCallableCustomGetUserData gdextension_interface_callable_custom_get_userdata = nullptr;
GDExtensionInterfaceRefGetObject gdextension_interface_ref_get_object = nullptr;
GDExtensionInterfaceRefSetObject gdextension_interface_ref_set_object = nullptr;
Expand Down Expand Up @@ -413,7 +413,7 @@ GDExtensionBool GDExtensionBinding::init(GDExtensionInterfaceGetProcAddress p_ge
LOAD_PROC_ADDRESS(object_get_instance_id, GDExtensionInterfaceObjectGetInstanceId);
LOAD_PROC_ADDRESS(object_has_script_method, GDExtensionInterfaceObjectHasScriptMethod);
LOAD_PROC_ADDRESS(object_call_script_method, GDExtensionInterfaceObjectCallScriptMethod);
LOAD_PROC_ADDRESS(callable_custom_create, GDExtensionInterfaceCallableCustomCreate);
LOAD_PROC_ADDRESS(callable_custom_create2, GDExtensionInterfaceCallableCustomCreate2);
LOAD_PROC_ADDRESS(callable_custom_get_userdata, GDExtensionInterfaceCallableCustomGetUserData);
LOAD_PROC_ADDRESS(ref_get_object, GDExtensionInterfaceRefGetObject);
LOAD_PROC_ADDRESS(ref_set_object, GDExtensionInterfaceRefSetObject);
Expand Down
18 changes: 16 additions & 2 deletions src/variant/callable_custom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@

namespace godot {

int CallableCustomBase::get_argument_count(bool &r_is_valid) const {
r_is_valid = false;
return 0;
}

static void callable_custom_call(void *p_userdata, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error) {
CallableCustom *callable_custom = (CallableCustom *)p_userdata;
callable_custom->call((const Variant **)p_args, p_argument_count, *(Variant *)r_return, *r_error);
Expand Down Expand Up @@ -84,13 +89,21 @@ static GDExtensionBool callable_custom_less_than_func(void *p_a, void *p_b) {
return func_a(a, b);
}

static GDExtensionInt custom_callable_get_argument_count_func(void *p_userdata, GDExtensionBool *r_is_valid) {
CallableCustom *callable_custom = (CallableCustom *)p_userdata;
bool valid = false;
int ret = callable_custom->get_argument_count(valid);
*r_is_valid = valid;
return ret;
}

bool CallableCustom::is_valid() const {
// The same default implementation as in Godot.
return ObjectDB::get_instance(get_object());
}

Callable::Callable(CallableCustom *p_callable_custom) {
GDExtensionCallableCustomInfo info = {};
GDExtensionCallableCustomInfo2 info = {};
info.callable_userdata = p_callable_custom;
info.token = internal::token;
info.object_id = p_callable_custom->get_object();
Expand All @@ -101,8 +114,9 @@ Callable::Callable(CallableCustom *p_callable_custom) {
info.equal_func = &callable_custom_equal_func;
info.less_than_func = &callable_custom_less_than_func;
info.to_string_func = &callable_custom_to_string;
info.get_argument_count_func = &custom_callable_get_argument_count_func;

::godot::internal::gdextension_interface_callable_custom_create(_native_ptr(), &info);
::godot::internal::gdextension_interface_callable_custom_create2(_native_ptr(), &info);
}

CallableCustom *Callable::get_custom() const {
Expand Down
13 changes: 11 additions & 2 deletions src/variant/callable_method_pointer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,14 @@ static GDExtensionBool custom_callable_mp_less_than_func(void *p_a, void *p_b) {
return memcmp(a->get_comp_ptr(), b->get_comp_ptr(), a->get_comp_size() * 4) < 0;
}

static GDExtensionInt custom_callable_mp_get_argument_count_func(void *p_userdata, GDExtensionBool *r_is_valid) {
CallableCustomMethodPointerBase *callable_method_pointer = (CallableCustomMethodPointerBase *)p_userdata;
bool valid = false;
int ret = callable_method_pointer->get_argument_count(valid);
*r_is_valid = valid;
return ret;
}

void CallableCustomMethodPointerBase::_setup(uint32_t *p_base_ptr, uint32_t p_ptr_size) {
comp_ptr = p_base_ptr;
comp_size = p_ptr_size / 4;
Expand All @@ -93,7 +101,7 @@ void CallableCustomMethodPointerBase::_setup(uint32_t *p_base_ptr, uint32_t p_pt
namespace internal {

Callable create_callable_from_ccmp(CallableCustomMethodPointerBase *p_callable_method_pointer) {
GDExtensionCallableCustomInfo info = {};
GDExtensionCallableCustomInfo2 info = {};
info.callable_userdata = p_callable_method_pointer;
info.token = internal::token;
info.object_id = p_callable_method_pointer->get_object();
Expand All @@ -103,9 +111,10 @@ Callable create_callable_from_ccmp(CallableCustomMethodPointerBase *p_callable_m
info.hash_func = &custom_callable_mp_hash;
info.equal_func = &custom_callable_mp_equal_func;
info.less_than_func = &custom_callable_mp_less_than_func;
info.get_argument_count_func = &custom_callable_mp_get_argument_count_func;

Callable callable;
::godot::internal::gdextension_interface_callable_custom_create(callable._native_ptr(), &info);
::godot::internal::gdextension_interface_callable_custom_create2(callable._native_ptr(), &info);
return callable;
}

Expand Down
6 changes: 6 additions & 0 deletions test/project/main.gd
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ func _ready():
# mp_callable() with void method.
var mp_callable: Callable = example.test_callable_mp()
assert_equal(mp_callable.is_valid(), true)
assert_equal(mp_callable.get_argument_count(), 3)
mp_callable.call(example, "void", 36)
assert_equal(custom_signal_emitted, ["unbound_method1: Example - void", 36])

Expand All @@ -117,14 +118,17 @@ func _ready():

# mp_callable() with return value.
var mp_callable_ret: Callable = example.test_callable_mp_ret()
assert_equal(mp_callable_ret.get_argument_count(), 3)
assert_equal(mp_callable_ret.call(example, "test", 77), "unbound_method2: Example - test - 77")

# mp_callable() with const method and return value.
var mp_callable_retc: Callable = example.test_callable_mp_retc()
assert_equal(mp_callable_retc.get_argument_count(), 3)
assert_equal(mp_callable_retc.call(example, "const", 101), "unbound_method3: Example - const - 101")

# mp_callable_static() with void method.
var mp_callable_static: Callable = example.test_callable_mp_static()
assert_equal(mp_callable_static.get_argument_count(), 3)
mp_callable_static.call(example, "static", 83)
assert_equal(custom_signal_emitted, ["unbound_static_method1: Example - static", 83])

Expand All @@ -140,6 +144,7 @@ func _ready():

# mp_callable_static() with return value.
var mp_callable_static_ret: Callable = example.test_callable_mp_static_ret()
assert_equal(mp_callable_static_ret.get_argument_count(), 3)
assert_equal(mp_callable_static_ret.call(example, "static-ret", 84), "unbound_static_method2: Example - static-ret - 84")

# CallableCustom.
Expand All @@ -150,6 +155,7 @@ func _ready():
assert_equal(custom_callable.hash(), 27);
assert_equal(custom_callable.get_object(), null);
assert_equal(custom_callable.get_method(), "");
assert_equal(custom_callable.get_argument_count(), 2)
assert_equal(str(custom_callable), "<MyCallableCustom>");

# PackedArray iterators
Expand Down
5 changes: 5 additions & 0 deletions test/src/example.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ class MyCallableCustom : public CallableCustom {
return ObjectID();
}

virtual int get_argument_count(bool &r_is_valid) const {
r_is_valid = true;
return 2;
}

virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const {
r_return_value = "Hi";
r_call_error.error = GDEXTENSION_CALL_OK;
Expand Down
Loading