浏览代码

Created the callable_mp macro, for signals to call method pointers directly.

Juan Linietsky 5 年之前
父节点
当前提交
04bb6a708a
共有 9 个文件被更改,包括 400 次插入17 次删除
  1. 3 4
      core/callable.h
  2. 94 0
      core/callable_method_pointer.cpp
  3. 279 0
      core/callable_method_pointer.h
  4. 5 3
      core/class_db.h
  5. 5 6
      core/method_bind.h
  6. 1 1
      core/object.cpp
  7. 12 0
      core/typedefs.h
  8. 0 1
      core/variant.h
  9. 1 2
      editor/inspector_dock.cpp

+ 3 - 4
core/callable.h

@@ -39,10 +39,9 @@ class Object;
 class Variant;
 class CallableCustom;
 
-// This is an abstraction of things that can be called
-// it is used for signals and other cases where effient
-// calling of functions is required.
-// It is designed for the standard case (object and method)
+// This is an abstraction of things that can be called.
+// It is used for signals and other cases where efficient calling of functions
+// is required. It is designed for the standard case (object and method)
 // but can be optimized or customized.
 
 class Callable {

+ 94 - 0
core/callable_method_pointer.cpp

@@ -0,0 +1,94 @@
+/*************************************************************************/
+/*  callable_method_pointer.cpp                                          */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "callable_method_pointer.h"
+
+bool CallableCustomMethodPointerBase::compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) {
+	const CallableCustomMethodPointerBase *a = static_cast<const CallableCustomMethodPointerBase *>(p_a);
+	const CallableCustomMethodPointerBase *b = static_cast<const CallableCustomMethodPointerBase *>(p_b);
+
+	if (a->comp_size != b->comp_size) {
+		return false;
+	}
+
+	for (uint32_t i = 0; i < a->comp_size; i++) {
+		if (a->comp_ptr[i] != b->comp_ptr[i]) {
+			return false;
+		}
+	}
+
+	return true;
+}
+
+bool CallableCustomMethodPointerBase::compare_less(const CallableCustom *p_a, const CallableCustom *p_b) {
+
+	const CallableCustomMethodPointerBase *a = static_cast<const CallableCustomMethodPointerBase *>(p_a);
+	const CallableCustomMethodPointerBase *b = static_cast<const CallableCustomMethodPointerBase *>(p_b);
+
+	if (a->comp_size != b->comp_size) {
+		return a->comp_size < b->comp_size;
+	}
+
+	for (uint32_t i = 0; i < a->comp_size; i++) {
+		if (a->comp_ptr[i] == b->comp_ptr[i]) {
+			continue;
+		}
+
+		return a->comp_ptr[i] < b->comp_ptr[i];
+	}
+
+	return false;
+}
+
+CallableCustom::CompareEqualFunc CallableCustomMethodPointerBase::get_compare_equal_func() const {
+	return compare_equal;
+}
+
+CallableCustom::CompareLessFunc CallableCustomMethodPointerBase::get_compare_less_func() const {
+	return compare_less;
+}
+
+uint32_t CallableCustomMethodPointerBase::hash() const {
+	return h;
+}
+
+void CallableCustomMethodPointerBase::_setup(uint32_t *p_base_ptr, uint32_t p_ptr_size) {
+	comp_ptr = p_base_ptr;
+	comp_size = p_ptr_size / 4;
+
+	// Precompute hash.
+	for (uint32_t i = 0; i < comp_size; i++) {
+		if (i == 0) {
+			h = hash_djb2_one_32(comp_ptr[i]);
+		} else {
+			h = hash_djb2_one_32(comp_ptr[i], h);
+		}
+	}
+}

+ 279 - 0
core/callable_method_pointer.h

@@ -0,0 +1,279 @@
+/*************************************************************************/
+/*  callable_method_pointer.h                                            */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifndef CALLABLE_METHOD_POINTER_H
+#define CALLABLE_METHOD_POINTER_H
+
+#include "core/callable.h"
+#include "core/hashfuncs.h"
+#include "core/object.h"
+#include "core/simple_type.h"
+
+class CallableCustomMethodPointerBase : public CallableCustom {
+
+	uint32_t *comp_ptr;
+	uint32_t comp_size;
+	uint32_t h;
+#ifdef DEBUG_METHODS_ENABLED
+	const char *text = "";
+#endif
+	static bool compare_equal(const CallableCustom *p_a, const CallableCustom *p_b);
+	static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b);
+
+protected:
+	void _setup(uint32_t *p_base_ptr, uint32_t p_ptr_size);
+
+public:
+#ifdef DEBUG_METHODS_ENABLED
+	void set_text(const char *p_text) {
+		text = p_text;
+	}
+	virtual String get_as_text() const {
+		return text;
+	}
+#else
+	virtual String get_as_text() const {
+		return String();
+	}
+#endif
+	virtual CompareEqualFunc get_compare_equal_func() const;
+	virtual CompareLessFunc get_compare_less_func() const;
+
+	virtual uint32_t hash() const;
+};
+
+#ifdef DEBUG_METHODS_ENABLED
+
+template <class T>
+struct VariantCasterAndValidate {
+
+	static _FORCE_INLINE_ T cast(const Variant **p_args, uint32_t p_arg_idx, Callable::CallError &r_error) {
+		Variant::Type argtype = GetTypeInfo<T>::VARIANT_TYPE;
+		if (!Variant::can_convert_strict(p_args[p_arg_idx]->get_type(), argtype)) {
+			r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+			r_error.argument = p_arg_idx;
+			r_error.expected = argtype;
+		}
+
+		return VariantCaster<T>::cast(*p_args[p_arg_idx]);
+	}
+};
+
+template <class T>
+struct VariantCasterAndValidate<T &> {
+
+	static _FORCE_INLINE_ T cast(const Variant **p_args, uint32_t p_arg_idx, Callable::CallError &r_error) {
+		Variant::Type argtype = GetTypeInfo<T>::VARIANT_TYPE;
+		if (!Variant::can_convert_strict(p_args[p_arg_idx]->get_type(), argtype)) {
+			r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+			r_error.argument = p_arg_idx;
+			r_error.expected = argtype;
+		}
+
+		return VariantCaster<T>::cast(*p_args[p_arg_idx]);
+	}
+};
+
+template <class T>
+struct VariantCasterAndValidate<const T &> {
+
+	static _FORCE_INLINE_ T cast(const Variant **p_args, uint32_t p_arg_idx, Callable::CallError &r_error) {
+		Variant::Type argtype = GetTypeInfo<T>::VARIANT_TYPE;
+		if (!Variant::can_convert_strict(p_args[p_arg_idx]->get_type(), argtype)) {
+			r_error.error = Callable::CallError::CALL_ERROR_INVALID_ARGUMENT;
+			r_error.argument = p_arg_idx;
+			r_error.expected = argtype;
+		}
+
+		return VariantCaster<T>::cast(*p_args[p_arg_idx]);
+	}
+};
+
+#endif // DEBUG_METHODS_ENABLED
+
+// GCC 8 raises "parameter 'p_args' set but not used" here, probably using a
+// template version that does not have arguments and thus sees it unused, but
+// obviously the template can be used for functions with and without them, and
+// the optimizer will get rid of it anyway.
+#if defined(DEBUG_METHODS_ENABLED) && defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-but-set-parameter"
+#endif
+
+template <class T, class... P, size_t... Is>
+void call_with_variant_args_helper(T *p_instance, void (T::*p_method)(P...), const Variant **p_args, Callable::CallError &r_error, IndexSequence<Is...>) {
+	r_error.error = Callable::CallError::CALL_OK;
+
+#ifdef DEBUG_METHODS_ENABLED
+	(p_instance->*p_method)(VariantCasterAndValidate<P>::cast(p_args, Is, r_error)...);
+#else
+	(p_instance->*p_method)(VariantCaster<P...>::cast(p_args[Is])...);
+#endif
+}
+
+#if defined(DEBUG_METHODS_ENABLED) && defined(__GNUC__) && !defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+
+template <class T, class... P>
+void call_with_variant_args(T *p_instance, void (T::*p_method)(P...), const Variant **p_args, int p_argcount, Callable::CallError &r_error) {
+#ifdef DEBUG_METHODS_ENABLED
+	if ((size_t)p_argcount > sizeof...(P)) {
+		r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
+		r_error.argument = sizeof...(P);
+		return;
+	}
+
+	if ((size_t)p_argcount < sizeof...(P)) {
+		r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+		r_error.argument = sizeof...(P);
+		return;
+	}
+#endif
+	call_with_variant_args_helper<T, P...>(p_instance, p_method, p_args, r_error, BuildIndexSequence<sizeof...(P)>{});
+}
+
+template <class T, class... P>
+class CallableCustomMethodPointer : public CallableCustomMethodPointerBase {
+
+	struct Data {
+		T *instance;
+		void (T::*method)(P...);
+	} data;
+
+public:
+	virtual ObjectID get_object() const { return data.instance->get_instance_id(); }
+
+	virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
+
+		call_with_variant_args(data.instance, data.method, p_arguments, p_argcount, r_call_error);
+	}
+
+	CallableCustomMethodPointer(T *p_instance, void (T::*p_method)(P...)) {
+		zeromem(&data, sizeof(Data)); // Clear beforehand, may have padding bytes.
+		data.instance = p_instance;
+		data.method = p_method;
+		_setup((uint32_t *)&data, sizeof(Data));
+	}
+};
+
+template <class T, class... P>
+Callable create_custom_callable_function_pointer(T *p_instance,
+#ifdef DEBUG_METHODS_ENABLED
+		const char *p_func_text,
+#endif
+		void (T::*p_method)(P...)) {
+
+	typedef CallableCustomMethodPointer<T, P...> CCMP; // Messes with memnew otherwise.
+	CCMP *ccmp = memnew(CCMP(p_instance, p_method));
+#ifdef DEBUG_METHODS_ENABLED
+	ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand.
+#endif
+	return Callable(ccmp);
+}
+
+// VERSION WITH RETURN
+
+template <class T, class R, class... P, size_t... Is>
+void call_with_variant_args_ret_helper(T *p_instance, R (T::*p_method)(P...), const Variant **p_args, Variant &r_ret, Callable::CallError &r_error, IndexSequence<Is...>) {
+	r_error.error = Callable::CallError::CALL_OK;
+
+#ifdef DEBUG_METHODS_ENABLED
+	r_ret = (p_instance->*p_method)(VariantCasterAndValidate<P>::cast(p_args, Is, r_error)...);
+#else
+	(p_instance->*p_method)(VariantCaster<P...>::cast(p_args[Is])...);
+#endif
+}
+
+template <class T, class R, class... P>
+void call_with_variant_args_ret(T *p_instance, R (T::*p_method)(P...), const Variant **p_args, int p_argcount, Variant &r_ret, Callable::CallError &r_error) {
+#ifdef DEBUG_METHODS_ENABLED
+	if ((size_t)p_argcount > sizeof...(P)) {
+		r_error.error = Callable::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS;
+		r_error.argument = sizeof...(P);
+		return;
+	}
+
+	if ((size_t)p_argcount < sizeof...(P)) {
+		r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
+		r_error.argument = sizeof...(P);
+		return;
+	}
+#endif
+	call_with_variant_args_ret_helper<T, R, P...>(p_instance, p_method, p_args, r_ret, r_error, BuildIndexSequence<sizeof...(P)>{});
+}
+
+template <class T, class R, class... P>
+class CallableCustomMethodPointerRet : public CallableCustomMethodPointerBase {
+
+	struct Data {
+		T *instance;
+		R(T::*method)
+		(P...);
+	} data;
+
+public:
+	virtual ObjectID get_object() const { return data.instance->get_instance_id(); }
+
+	virtual void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const {
+
+		call_with_variant_args_ret(data.instance, data.method, p_arguments, p_argcount, r_return_value, r_call_error);
+	}
+
+	CallableCustomMethodPointerRet(T *p_instance, R (T::*p_method)(P...)) {
+		zeromem(&data, sizeof(Data)); // Clear beforehand, may have padding bytes.
+		data.instance = p_instance;
+		data.method = p_method;
+		_setup((uint32_t *)&data, sizeof(Data));
+	}
+};
+
+template <class T, class R, class... P>
+Callable create_custom_callable_function_pointer(T *p_instance,
+#ifdef DEBUG_METHODS_ENABLED
+		const char *p_func_text,
+#endif
+		R (T::*p_method)(P...)) {
+
+	typedef CallableCustomMethodPointerRet<T, R, P...> CCMP; // Messes with memnew otherwise.
+	CCMP *ccmp = memnew(CCMP(p_instance, p_method));
+#ifdef DEBUG_METHODS_ENABLED
+	ccmp->set_text(p_func_text + 1); // Try to get rid of the ampersand.
+#endif
+	return Callable(ccmp);
+}
+
+#ifdef DEBUG_METHODS_ENABLED
+#define callable_mp(I, M) create_custom_callable_function_pointer(I, #M, M)
+#else
+#define callable_mp(I, M) create_custom_callable_function_pointer(I, M)
+#endif
+
+#endif // CALLABLE_METHOD_POINTER_H

+ 5 - 3
core/class_db.h

@@ -35,13 +35,15 @@
 #include "core/object.h"
 #include "core/print_string.h"
 
-/**	To bind more then 6 parameters include this:
+/** To bind more then 6 parameters include this:
  *  #include "core/method_bind_ext.gen.inc"
  */
 
-#define DEFVAL(m_defval) (m_defval)
+// Makes callable_mp readily available in all classes connecting signals.
+// Needs to come after method_bind and object have been included.
+#include "core/callable_method_pointer.h"
 
-//#define SIMPLE_METHODDEF
+#define DEFVAL(m_defval) (m_defval)
 
 #ifdef DEBUG_METHODS_ENABLED
 

+ 5 - 6
core/method_bind.h

@@ -31,19 +31,18 @@
 #ifndef METHOD_BIND_H
 #define METHOD_BIND_H
 
+#ifdef DEBUG_ENABLED
+#define DEBUG_METHODS_ENABLED
+#endif
+
 #include "core/list.h"
 #include "core/method_ptrcall.h"
 #include "core/object.h"
+#include "core/type_info.h"
 #include "core/variant.h"
 
 #include <stdio.h>
 
-#ifdef DEBUG_ENABLED
-#define DEBUG_METHODS_ENABLED
-#endif
-
-#include "core/type_info.h"
-
 enum MethodFlags {
 
 	METHOD_FLAG_NORMAL = 1,

+ 1 - 1
core/object.cpp

@@ -1207,7 +1207,7 @@ Error Object::emit_signal(const StringName &p_name, const Variant **p_args, int
 				if (ce.error == Callable::CallError::CALL_ERROR_INVALID_METHOD && !ClassDB::class_exists(target->get_class_name())) {
 					//most likely object is not initialized yet, do not throw error.
 				} else {
-					ERR_PRINT("Error calling from signal '" + String(p_name) + "': " + Variant::get_callable_error_text(c.callable, args, argc, ce) + ".");
+					ERR_PRINT("Error calling from signal '" + String(p_name) + "' to callable: " + Variant::get_callable_error_text(c.callable, args, argc, ce) + ".");
 					err = ERR_METHOD_NOT_FOUND;
 				}
 			}

+ 12 - 0
core/typedefs.h

@@ -357,4 +357,16 @@ struct _GlobalLock {
 #define FALLTHROUGH
 #endif
 
+// Home-made index sequence trick, so it can be used everywhere without the costly include of std::tuple.
+// https://stackoverflow.com/questions/15014096/c-index-of-type-during-variadic-template-expansion
+
+template <size_t... Is>
+struct IndexSequence {};
+
+template <size_t N, size_t... Is>
+struct BuildIndexSequence : BuildIndexSequence<N - 1, N - 1, Is...> {};
+
+template <size_t... Is>
+struct BuildIndexSequence<0, Is...> : IndexSequence<Is...> {};
+
 #endif // TYPEDEFS_H

+ 0 - 1
core/variant.h

@@ -46,7 +46,6 @@
 #include "core/math/vector3.h"
 #include "core/node_path.h"
 #include "core/object_id.h"
-
 #include "core/rid.h"
 #include "core/ustring.h"
 

+ 1 - 2
editor/inspector_dock.cpp

@@ -349,7 +349,6 @@ void InspectorDock::_bind_methods() {
 	ClassDB::bind_method("_property_keyed", &InspectorDock::_property_keyed);
 	ClassDB::bind_method("_transform_keyed", &InspectorDock::_transform_keyed);
 
-	ClassDB::bind_method("_new_resource", &InspectorDock::_new_resource);
 	ClassDB::bind_method("_resource_file_selected", &InspectorDock::_resource_file_selected);
 	ClassDB::bind_method("_open_resource_selector", &InspectorDock::_open_resource_selector);
 	ClassDB::bind_method("_unref_resource", &InspectorDock::_unref_resource);
@@ -511,7 +510,7 @@ InspectorDock::InspectorDock(EditorNode *p_editor, EditorData &p_editor_data) {
 	resource_new_button->set_tooltip(TTR("Create a new resource in memory and edit it."));
 	resource_new_button->set_icon(get_icon("New", "EditorIcons"));
 	general_options_hb->add_child(resource_new_button);
-	resource_new_button->connect_compat("pressed", this, "_new_resource");
+	resource_new_button->connect("pressed", callable_mp(this, &InspectorDock::_new_resource));
 	resource_new_button->set_focus_mode(Control::FOCUS_NONE);
 
 	resource_load_button = memnew(ToolButton);