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 | |
40 | class 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 | |
50 | protected: |
51 | void _setup(uint32_t *p_base_ptr, uint32_t p_ptr_size); |
52 | |
53 | public: |
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 | |
80 | template <class T, class... P> |
81 | class 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 | |
90 | public: |
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 | |
118 | template <class T, class... P> |
119 | Callable 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 | |
134 | template <class T, class R, class... P> |
135 | class 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 | |
145 | public: |
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 | |
173 | template <class T, class R, class... P> |
174 | Callable 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 | |
189 | template <class T, class R, class... P> |
190 | class 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 | |
200 | public: |
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 | |
228 | template <class T, class R, class... P> |
229 | Callable 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 | |
250 | template <class... P> |
251 | class CallableCustomStaticMethodPointer : public CallableCustomMethodPointerBase { |
252 | struct Data { |
253 | void (*method)(P...); |
254 | } data; |
255 | |
256 | public: |
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 | |
277 | template <class T, class... P> |
278 | Callable 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 | |
291 | template <class R, class... P> |
292 | class CallableCustomStaticMethodPointerRet : public CallableCustomMethodPointerBase { |
293 | struct Data { |
294 | R(*method) |
295 | (P...); |
296 | } data; |
297 | |
298 | public: |
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 | |
318 | template <class R, class... P> |
319 | Callable 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 | |