Skip to content

Commit f426b12

Browse files
committed
Fix comparison of Callables from callable_mp() of the same method
1 parent 4439a4a commit f426b12

File tree

3 files changed

+140
-31
lines changed

3 files changed

+140
-31
lines changed

include/godot_cpp/variant/callable_method_pointer.hpp

Lines changed: 63 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,17 @@
3737
namespace godot {
3838

3939
class CallableCustomMethodPointerBase : public CallableCustomBase {
40+
uint32_t *comp_ptr = nullptr;
41+
uint32_t comp_size;
42+
uint32_t h;
43+
44+
protected:
45+
void _setup(uint32_t *p_base_ptr, uint32_t p_ptr_size);
46+
47+
public:
48+
_FORCE_INLINE_ const uint32_t *get_comp_ptr() const { return comp_ptr; }
49+
_FORCE_INLINE_ uint32_t get_comp_size() const { return comp_size; }
50+
_FORCE_INLINE_ uint32_t get_hash() const { return h; }
4051
};
4152

4253
namespace internal {
@@ -51,21 +62,26 @@ Callable create_callable_from_ccmp(CallableCustomMethodPointerBase *p_callable_m
5162

5263
template <class T, class... P>
5364
class CallableCustomMethodPointer : public CallableCustomMethodPointerBase {
54-
T *instance;
55-
void (T::*method)(P...);
65+
struct Data {
66+
T *instance;
67+
void (T::*method)(P...);
68+
} data;
69+
static_assert(sizeof(Data) % 4 == 0);
5670

5771
public:
5872
virtual ObjectID get_object() const override {
59-
return ObjectID(instance->get_instance_id());
73+
return ObjectID(data.instance->get_instance_id());
6074
}
6175

6276
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const override {
63-
call_with_variant_args(instance, method, p_arguments, p_argcount, r_call_error);
77+
call_with_variant_args(data.instance, data.method, p_arguments, p_argcount, r_call_error);
6478
}
6579

6680
CallableCustomMethodPointer(T *p_instance, void (T::*p_method)(P...)) {
67-
instance = p_instance;
68-
method = p_method;
81+
memset(&data, 0, sizeof(Data));
82+
data.instance = p_instance;
83+
data.method = p_method;
84+
_setup((uint32_t *)&data, sizeof(Data));
6985
}
7086
};
7187

@@ -82,22 +98,27 @@ Callable create_custom_callable_function_pointer(T *p_instance, void (T::*p_meth
8298

8399
template <class T, class R, class... P>
84100
class CallableCustomMethodPointerRet : public CallableCustomMethodPointerBase {
85-
T *instance;
86-
R(T::*method)
87-
(P...);
101+
struct Data {
102+
T *instance;
103+
R(T::*method)
104+
(P...);
105+
} data;
106+
static_assert(sizeof(Data) % 4 == 0);
88107

89108
public:
90109
virtual ObjectID get_object() const override {
91-
return ObjectID(instance->get_instance_id());
110+
return ObjectID(data.instance->get_instance_id());
92111
}
93112

94113
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const override {
95-
call_with_variant_args_ret(instance, method, p_arguments, p_argcount, r_return_value, r_call_error);
114+
call_with_variant_args_ret(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error);
96115
}
97116

98117
CallableCustomMethodPointerRet(T *p_instance, R (T::*p_method)(P...)) {
99-
instance = p_instance;
100-
method = p_method;
118+
memset(&data, 0, sizeof(Data));
119+
data.instance = p_instance;
120+
data.method = p_method;
121+
_setup((uint32_t *)&data, sizeof(Data));
101122
}
102123
};
103124

@@ -114,22 +135,27 @@ Callable create_custom_callable_function_pointer(T *p_instance, R (T::*p_method)
114135

115136
template <class T, class R, class... P>
116137
class CallableCustomMethodPointerRetC : public CallableCustomMethodPointerBase {
117-
T *instance;
118-
R(T::*method)
119-
(P...) const;
138+
struct Data {
139+
T *instance;
140+
R(T::*method)
141+
(P...) const;
142+
} data;
143+
static_assert(sizeof(Data) % 4 == 0);
120144

121145
public:
122146
virtual ObjectID get_object() const override {
123-
return ObjectID(instance->get_instance_id());
147+
return ObjectID(data.instance->get_instance_id());
124148
}
125149

126150
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const override {
127-
call_with_variant_args_retc(instance, method, p_arguments, p_argcount, r_return_value, r_call_error);
151+
call_with_variant_args_retc(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error);
128152
}
129153

130154
CallableCustomMethodPointerRetC(const T *p_instance, R (T::*p_method)(P...) const) {
131-
instance = const_cast<T *>(p_instance);
132-
method = p_method;
155+
memset(&data, 0, sizeof(Data));
156+
data.instance = const_cast<T *>(p_instance);
157+
data.method = p_method;
158+
_setup((uint32_t *)&data, sizeof(Data));
133159
}
134160
};
135161

@@ -146,20 +172,25 @@ Callable create_custom_callable_function_pointer(const T *p_instance, R (T::*p_m
146172

147173
template <class... P>
148174
class CallableCustomStaticMethodPointer : public CallableCustomMethodPointerBase {
149-
void (*method)(P...);
175+
struct Data {
176+
void (*method)(P...);
177+
} data;
178+
static_assert(sizeof(Data) % 4 == 0);
150179

151180
public:
152181
virtual ObjectID get_object() const override {
153182
return ObjectID();
154183
}
155184

156185
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const override {
157-
call_with_variant_args_static_ret(method, p_arguments, p_argcount, r_return_value, r_call_error);
186+
call_with_variant_args_static_ret(data.method, p_arguments, p_argcount, r_return_value, r_call_error);
158187
r_return_value = Variant();
159188
}
160189

161190
CallableCustomStaticMethodPointer(void (*p_method)(P...)) {
162-
method = p_method;
191+
memset(&data, 0, sizeof(Data));
192+
data.method = p_method;
193+
_setup((uint32_t *)&data, sizeof(Data));
163194
}
164195
};
165196

@@ -176,20 +207,25 @@ Callable create_custom_callable_static_function_pointer(void (*p_method)(P...))
176207

177208
template <class R, class... P>
178209
class CallableCustomStaticMethodPointerRet : public CallableCustomMethodPointerBase {
179-
R(*method)
180-
(P...);
210+
struct Data {
211+
R(*method)
212+
(P...);
213+
} data;
214+
static_assert(sizeof(Data) % 4 == 0);
181215

182216
public:
183217
virtual ObjectID get_object() const override {
184218
return ObjectID();
185219
}
186220

187221
virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, GDExtensionCallError &r_call_error) const override {
188-
call_with_variant_args_static_ret(method, p_arguments, p_argcount, r_return_value, r_call_error);
222+
call_with_variant_args_static_ret(data.method, p_arguments, p_argcount, r_return_value, r_call_error);
189223
}
190224

191225
CallableCustomStaticMethodPointerRet(R (*p_method)(P...)) {
192-
method = p_method;
226+
memset(&data, 0, sizeof(Data));
227+
data.method = p_method;
228+
_setup((uint32_t *)&data, sizeof(Data));
193229
}
194230
};
195231

src/variant/callable_method_pointer.cpp

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,18 +30,66 @@
3030

3131
#include <godot_cpp/variant/callable_method_pointer.hpp>
3232

33+
#include <godot_cpp/templates/hashfuncs.hpp>
34+
3335
namespace godot {
3436

35-
static void custom_callable_mp_call(void *userdata, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error) {
36-
CallableCustomMethodPointerBase *callable_method_pointer = (CallableCustomMethodPointerBase *)userdata;
37+
static void custom_callable_mp_call(void *p_userdata, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error) {
38+
CallableCustomMethodPointerBase *callable_method_pointer = (CallableCustomMethodPointerBase *)p_userdata;
3739
callable_method_pointer->call((const Variant **)p_args, p_argument_count, *(Variant *)r_return, *r_error);
3840
}
3941

40-
static void custom_callable_mp_free(void *userdata) {
41-
CallableCustomMethodPointerBase *callable_method_pointer = (CallableCustomMethodPointerBase *)userdata;
42+
static GDExtensionBool custom_callable_mp_is_valid(void *p_userdata) {
43+
CallableCustomMethodPointerBase *callable_method_pointer = (CallableCustomMethodPointerBase *)p_userdata;
44+
ObjectID object = callable_method_pointer->get_object();
45+
return object == ObjectID() || ObjectDB::get_instance(object);
46+
}
47+
48+
static void custom_callable_mp_free(void *p_userdata) {
49+
CallableCustomMethodPointerBase *callable_method_pointer = (CallableCustomMethodPointerBase *)p_userdata;
4250
memdelete(callable_method_pointer);
4351
}
4452

53+
static uint32_t custom_callable_mp_hash(void *p_userdata) {
54+
CallableCustomMethodPointerBase *callable_method_pointer = (CallableCustomMethodPointerBase *)p_userdata;
55+
return callable_method_pointer->get_hash();
56+
}
57+
58+
static GDExtensionBool custom_callable_mp_equal_func(void *p_a, void *p_b) {
59+
CallableCustomMethodPointerBase *a = (CallableCustomMethodPointerBase *)p_a;
60+
CallableCustomMethodPointerBase *b = (CallableCustomMethodPointerBase *)p_b;
61+
62+
if (a->get_comp_size() != b->get_comp_size()) {
63+
return false;
64+
}
65+
66+
return memcmp(a->get_comp_ptr(), b->get_comp_ptr(), a->get_comp_size() * 4) == 0;
67+
}
68+
69+
static GDExtensionBool custom_callable_mp_less_than_func(void *p_a, void *p_b) {
70+
CallableCustomMethodPointerBase *a = (CallableCustomMethodPointerBase *)p_a;
71+
CallableCustomMethodPointerBase *b = (CallableCustomMethodPointerBase *)p_b;
72+
73+
if (a->get_comp_size() != b->get_comp_size()) {
74+
return a->get_comp_size() < b->get_comp_size();
75+
}
76+
77+
return memcmp(a->get_comp_ptr(), b->get_comp_ptr(), a->get_comp_size() * 4) < 0;
78+
}
79+
80+
void CallableCustomMethodPointerBase::_setup(uint32_t *p_base_ptr, uint32_t p_ptr_size) {
81+
comp_ptr = p_base_ptr;
82+
comp_size = p_ptr_size / 4;
83+
84+
for (uint32_t i = 0; i < comp_size; i++) {
85+
if (i == 0) {
86+
h = hash_murmur3_one_32(comp_ptr[i]);
87+
} else {
88+
h = hash_murmur3_one_32(comp_ptr[i], h);
89+
}
90+
}
91+
}
92+
4593
namespace internal {
4694

4795
Callable create_callable_from_ccmp(CallableCustomMethodPointerBase *p_callable_method_pointer) {
@@ -50,7 +98,11 @@ Callable create_callable_from_ccmp(CallableCustomMethodPointerBase *p_callable_m
5098
info.token = internal::token;
5199
info.object_id = p_callable_method_pointer->get_object();
52100
info.call_func = &custom_callable_mp_call;
101+
info.is_valid_func = &custom_callable_mp_is_valid;
53102
info.free_func = &custom_callable_mp_free;
103+
info.hash_func = &custom_callable_mp_hash;
104+
info.equal_func = &custom_callable_mp_equal_func;
105+
info.less_than_func = &custom_callable_mp_less_than_func;
54106

55107
Callable callable;
56108
::godot::internal::gdextension_interface_callable_custom_create(callable._native_ptr(), &info);

test/project/main.gd

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,20 @@ func _ready():
101101

102102
# mp_callable() with void method.
103103
var mp_callable: Callable = example.test_callable_mp()
104+
assert_equal(mp_callable.is_valid(), true)
104105
mp_callable.call(example, "void", 36)
105106
assert_equal(custom_signal_emitted, ["unbound_method1: Example - void", 36])
106107

108+
# Check that it works with is_connected().
109+
assert_equal(example.renamed.is_connected(mp_callable), false)
110+
example.renamed.connect(mp_callable)
111+
assert_equal(example.renamed.is_connected(mp_callable), true)
112+
# Make sure a new object is still treated as equivalent.
113+
assert_equal(example.renamed.is_connected(example.test_callable_mp()), true)
114+
assert_equal(mp_callable.hash(), example.test_callable_mp().hash())
115+
example.renamed.disconnect(mp_callable)
116+
assert_equal(example.renamed.is_connected(mp_callable), false)
117+
107118
# mp_callable() with return value.
108119
var mp_callable_ret: Callable = example.test_callable_mp_ret()
109120
assert_equal(mp_callable_ret.call(example, "test", 77), "unbound_method2: Example - test - 77")
@@ -117,6 +128,16 @@ func _ready():
117128
mp_callable_static.call(example, "static", 83)
118129
assert_equal(custom_signal_emitted, ["unbound_static_method1: Example - static", 83])
119130

131+
# Check that it works with is_connected().
132+
assert_equal(example.renamed.is_connected(mp_callable_static), false)
133+
example.renamed.connect(mp_callable_static)
134+
assert_equal(example.renamed.is_connected(mp_callable_static), true)
135+
# Make sure a new object is still treated as equivalent.
136+
assert_equal(example.renamed.is_connected(example.test_callable_mp_static()), true)
137+
assert_equal(mp_callable_static.hash(), example.test_callable_mp_static().hash())
138+
example.renamed.disconnect(mp_callable_static)
139+
assert_equal(example.renamed.is_connected(mp_callable_static), false)
140+
120141
# mp_callable_static() with return value.
121142
var mp_callable_static_ret: Callable = example.test_callable_mp_static_ret()
122143
assert_equal(mp_callable_static_ret.call(example, "static-ret", 84), "unbound_static_method2: Example - static-ret - 84")

0 commit comments

Comments
 (0)