1/**************************************************************************/
2/* callable_method_pointer.h */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#ifndef CALLABLE_METHOD_POINTER_H
32#define CALLABLE_METHOD_POINTER_H
33
34#include "core/object/object.h"
35#include "core/templates/hashfuncs.h"
36#include "core/templates/simple_type.h"
37#include "core/variant/binder_common.h"
38#include "core/variant/callable.h"
39
40class CallableCustomMethodPointerBase : public CallableCustom {
41 uint32_t *comp_ptr = nullptr;
42 uint32_t comp_size;
43 uint32_t h;
44#ifdef DEBUG_METHODS_ENABLED
45 const char *text = "";
46#endif
47 static bool compare_equal(const CallableCustom *p_a, const CallableCustom *p_b);
48 static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b);
49
50protected:
51 void _setup(uint32_t *p_base_ptr, uint32_t p_ptr_size);
52
53public:
54 virtual StringName get_method() const {
55#ifdef DEBUG_METHODS_ENABLED
56 return StringName(text);
57#else
58 return StringName();
59#endif
60 }
61
62#ifdef DEBUG_METHODS_ENABLED
63 void set_text(const char *p_text) {
64 text = p_text;
65 }
66 virtual String get_as_text() const {
67 return text;
68 }
69#else
70 virtual String get_as_text() const {
71 return String();
72 }
73#endif
74 virtual CompareEqualFunc get_compare_equal_func() const;
75 virtual CompareLessFunc get_compare_less_func() const;
76
77 virtual uint32_t hash() const;
78};
79
80template <class T, class... P>
81class CallableCustomMethodPointer : public CallableCustomMethodPointerBase {
82 struct Data {
83 T *instance;
84#ifdef DEBUG_ENABLED
85 uint64_t object_id;
86#endif
87 void (T::*method)(P...);
88 } data;
89
90public:
91 virtual ObjectID get_object() const {
92#ifdef DEBUG_ENABLED
93 if (ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr) {
94 return ObjectID();
95 }
96#endif
97 return data.instance->get_instance_id();
98 }
99
100 virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
101#ifdef DEBUG_ENABLED
102 ERR_FAIL_COND_MSG(ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr, "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
103#endif
104 call_with_variant_args(data.instance, data.method, p_arguments, p_argcount, r_call_error);
105 }
106
107 CallableCustomMethodPointer(T *p_instance, void (T::*p_method)(P...)) {
108 memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes.
109 data.instance = p_instance;
110#ifdef DEBUG_ENABLED
111 data.object_id = p_instance->get_instance_id();
112#endif
113 data.method = p_method;
114 _setup((uint32_t *)&data, sizeof(Data));
115 }
116};
117
118template <class T, class... P>
119Callable create_custom_callable_function_pointer(T *p_instance,
120#ifdef DEBUG_METHODS_ENABLED
121 const char *p_func_text,
122#endif
123 void (T::*p_method)(P...)) {
124 typedef CallableCustomMethodPointer<T, P...> CCMP; // Messes with memnew otherwise.
125 CCMP *ccmp = memnew(CCMP(p_instance, p_method));
126#ifdef DEBUG_METHODS_ENABLED
127 ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand.
128#endif
129 return Callable(ccmp);
130}
131
132// VERSION WITH RETURN
133
134template <class T, class R, class... P>
135class CallableCustomMethodPointerRet : public CallableCustomMethodPointerBase {
136 struct Data {
137 T *instance;
138#ifdef DEBUG_ENABLED
139 uint64_t object_id;
140#endif
141 R(T::*method)
142 (P...);
143 } data;
144
145public:
146 virtual ObjectID get_object() const {
147#ifdef DEBUG_ENABLED
148 if (ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr) {
149 return ObjectID();
150 }
151#endif
152 return data.instance->get_instance_id();
153 }
154
155 virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
156#ifdef DEBUG_ENABLED
157 ERR_FAIL_COND_MSG(ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr, "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
158#endif
159 call_with_variant_args_ret(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error);
160 }
161
162 CallableCustomMethodPointerRet(T *p_instance, R (T::*p_method)(P...)) {
163 memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes.
164 data.instance = p_instance;
165#ifdef DEBUG_ENABLED
166 data.object_id = p_instance->get_instance_id();
167#endif
168 data.method = p_method;
169 _setup((uint32_t *)&data, sizeof(Data));
170 }
171};
172
173template <class T, class R, class... P>
174Callable create_custom_callable_function_pointer(T *p_instance,
175#ifdef DEBUG_METHODS_ENABLED
176 const char *p_func_text,
177#endif
178 R (T::*p_method)(P...)) {
179 typedef CallableCustomMethodPointerRet<T, R, P...> CCMP; // Messes with memnew otherwise.
180 CCMP *ccmp = memnew(CCMP(p_instance, p_method));
181#ifdef DEBUG_METHODS_ENABLED
182 ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand.
183#endif
184 return Callable(ccmp);
185}
186
187// CONST VERSION WITH RETURN
188
189template <class T, class R, class... P>
190class CallableCustomMethodPointerRetC : public CallableCustomMethodPointerBase {
191 struct Data {
192 T *instance;
193#ifdef DEBUG_ENABLED
194 uint64_t object_id;
195#endif
196 R(T::*method)
197 (P...) const;
198 } data;
199
200public:
201 virtual ObjectID get_object() const override {
202#ifdef DEBUG_ENABLED
203 if (ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr) {
204 return ObjectID();
205 }
206#endif
207 return data.instance->get_instance_id();
208 }
209
210 virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override {
211#ifdef DEBUG_ENABLED
212 ERR_FAIL_COND_MSG(ObjectDB::get_instance(ObjectID(data.object_id)) == nullptr, "Invalid Object id '" + uitos(data.object_id) + "', can't call method.");
213#endif
214 call_with_variant_args_retc(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error);
215 }
216
217 CallableCustomMethodPointerRetC(T *p_instance, R (T::*p_method)(P...) const) {
218 memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes.
219 data.instance = p_instance;
220#ifdef DEBUG_ENABLED
221 data.object_id = p_instance->get_instance_id();
222#endif
223 data.method = p_method;
224 _setup((uint32_t *)&data, sizeof(Data));
225 }
226};
227
228template <class T, class R, class... P>
229Callable create_custom_callable_function_pointer(T *p_instance,
230#ifdef DEBUG_METHODS_ENABLED
231 const char *p_func_text,
232#endif
233 R (T::*p_method)(P...) const) {
234 typedef CallableCustomMethodPointerRetC<T, R, P...> CCMP; // Messes with memnew otherwise.
235 CCMP *ccmp = memnew(CCMP(p_instance, p_method));
236#ifdef DEBUG_METHODS_ENABLED
237 ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand.
238#endif
239 return Callable(ccmp);
240}
241
242#ifdef DEBUG_METHODS_ENABLED
243#define callable_mp(I, M) create_custom_callable_function_pointer(I, #M, M)
244#else
245#define callable_mp(I, M) create_custom_callable_function_pointer(I, M)
246#endif
247
248// STATIC VERSIONS
249
250template <class... P>
251class CallableCustomStaticMethodPointer : public CallableCustomMethodPointerBase {
252 struct Data {
253 void (*method)(P...);
254 } data;
255
256public:
257 virtual bool is_valid() const override {
258 return true;
259 }
260
261 virtual ObjectID get_object() const override {
262 return ObjectID();
263 }
264
265 virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override {
266 call_with_variant_args_static_ret(data.method, p_arguments, p_argcount, r_return_value, r_call_error);
267 r_return_value = Variant();
268 }
269
270 CallableCustomStaticMethodPointer(void (*p_method)(P...)) {
271 memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes.
272 data.method = p_method;
273 _setup((uint32_t *)&data, sizeof(Data));
274 }
275};
276
277template <class T, class... P>
278Callable create_custom_callable_static_function_pointer(
279#ifdef DEBUG_METHODS_ENABLED
280 const char *p_func_text,
281#endif
282 void (*p_method)(P...)) {
283 typedef CallableCustomStaticMethodPointer<P...> CCMP; // Messes with memnew otherwise.
284 CCMP *ccmp = memnew(CCMP(p_method));
285#ifdef DEBUG_METHODS_ENABLED
286 ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand.
287#endif
288 return Callable(ccmp);
289}
290
291template <class R, class... P>
292class CallableCustomStaticMethodPointerRet : public CallableCustomMethodPointerBase {
293 struct Data {
294 R(*method)
295 (P...);
296 } data;
297
298public:
299 virtual bool is_valid() const override {
300 return true;
301 }
302
303 virtual ObjectID get_object() const override {
304 return ObjectID();
305 }
306
307 virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override {
308 call_with_variant_args_static_ret(data.method, p_arguments, p_argcount, r_return_value, r_call_error);
309 }
310
311 CallableCustomStaticMethodPointerRet(R (*p_method)(P...)) {
312 memset(&data, 0, sizeof(Data)); // Clear beforehand, may have padding bytes.
313 data.method = p_method;
314 _setup((uint32_t *)&data, sizeof(Data));
315 }
316};
317
318template <class R, class... P>
319Callable create_custom_callable_static_function_pointer(
320#ifdef DEBUG_METHODS_ENABLED
321 const char *p_func_text,
322#endif
323 R (*p_method)(P...)) {
324 typedef CallableCustomStaticMethodPointerRet<R, P...> CCMP; // Messes with memnew otherwise.
325 CCMP *ccmp = memnew(CCMP(p_method));
326#ifdef DEBUG_METHODS_ENABLED
327 ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand.
328#endif
329 return Callable(ccmp);
330}
331
332#ifdef DEBUG_METHODS_ENABLED
333#define callable_mp_static(M) create_custom_callable_static_function_pointer(#M, M)
334#else
335#define callable_mp_static(M) create_custom_callable_static_function_pointer(M)
336#endif
337
338#endif // CALLABLE_METHOD_POINTER_H
339