diff --git a/include/godot_cpp/core/class_db.hpp b/include/godot_cpp/core/class_db.hpp index af394f04..feb47e0c 100644 --- a/include/godot_cpp/core/class_db.hpp +++ b/include/godot_cpp/core/class_db.hpp @@ -160,6 +160,9 @@ public: template static MethodBind *bind_vararg_method(uint32_t p_flags, StringName p_name, M p_method, const MethodInfo &p_info = MethodInfo(), const std::vector &p_default_args = std::vector{}, bool p_return_nil_is_variant = true); + template + static MethodBind *bind_static_vararg_method(uint32_t p_flags, StringName p_class, StringName p_name, M p_method, const MethodInfo &p_info = MethodInfo(), const std::vector &p_default_args = std::vector{}, bool p_return_nil_is_variant = true); + static void add_property_group(const StringName &p_class, const String &p_name, const String &p_prefix); static void add_property_subgroup(const StringName &p_class, const String &p_name, const String &p_prefix); static void add_property(const StringName &p_class, const PropertyInfo &p_pinfo, const StringName &p_setter, const StringName &p_getter, int p_index = -1); @@ -318,6 +321,37 @@ MethodBind *ClassDB::bind_vararg_method(uint32_t p_flags, StringName p_name, M p return bind; } +template +MethodBind *ClassDB::bind_static_vararg_method(uint32_t p_flags, StringName p_class, StringName p_name, M p_method, const MethodInfo &p_info, const std::vector &p_default_args, bool p_return_nil_is_variant) { + MethodBind *bind = create_vararg_method_bind(p_method, p_info, p_return_nil_is_variant); + ERR_FAIL_COND_V(!bind, nullptr); + + bind->set_name(p_name); + bind->set_default_arguments(p_default_args); + bind->set_instance_class(p_class); + + std::unordered_map::iterator type_it = classes.find(p_class); + if (type_it == classes.end()) { + memdelete(bind); + ERR_FAIL_V_MSG(nullptr, String("Class '{0}' doesn't exist.").format(Array::make(p_class))); + } + + ClassInfo &type = type_it->second; + + if (type.method_map.find(p_name) != type.method_map.end()) { + memdelete(bind); + ERR_FAIL_V_MSG(nullptr, String("Binding duplicate method: {0}::{1}.").format(Array::make(p_class, p_method))); + } + + // register our method bind within our plugin + type.method_map[p_name] = bind; + + // and register with godot + bind_method_godot(type.name, bind); + + return bind; +} + #define GDREGISTER_CLASS(m_class) ClassDB::register_class(); #define GDREGISTER_VIRTUAL_CLASS(m_class) ClassDB::register_class(true); #define GDREGISTER_ABSTRACT_CLASS(m_class) ClassDB::register_abstract_class(); diff --git a/include/godot_cpp/core/method_bind.hpp b/include/godot_cpp/core/method_bind.hpp index 37ae7317..822e98cd 100644 --- a/include/godot_cpp/core/method_bind.hpp +++ b/include/godot_cpp/core/method_bind.hpp @@ -267,6 +267,120 @@ MethodBind *create_vararg_method_bind(R (T::*p_method)(const Variant **, GDExten return a; } +template +class MethodBindVarArgBaseS : public MethodBind { +protected: + R(*function) + (const Variant **, GDExtensionInt, GDExtensionCallError &); + std::vector arguments; + +public: + virtual PropertyInfo gen_argument_type_info(int p_arg) const { + if (p_arg < 0) { + return _gen_return_type_info(); + } else if ((size_t)(p_arg) < arguments.size()) { + return arguments[p_arg]; + } else { + return make_property_info(Variant::Type::NIL, "vararg", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT); + } + } + + virtual GDExtensionVariantType gen_argument_type(int p_arg) const { + return static_cast(gen_argument_type_info(p_arg).type); + } + + virtual GDExtensionClassMethodArgumentMetadata get_argument_metadata(int) const { + return GDEXTENSION_METHOD_ARGUMENT_METADATA_NONE; + } + + virtual void ptrcall(GDExtensionClassInstancePtr p_instance, const GDExtensionConstTypePtr *p_args, GDExtensionTypePtr r_return) const { + ERR_FAIL(); // Can't call. + } + + MethodBindVarArgBaseS( + R (*p_function)(const Variant **, GDExtensionInt, GDExtensionCallError &), + const MethodInfo &p_method_info, + bool p_return_nil_is_variant) : + function(p_function) { + set_vararg(true); + set_argument_count(p_method_info.arguments.size()); + if (p_method_info.arguments.size()) { + arguments = p_method_info.arguments; + + std::vector names; + names.reserve(p_method_info.arguments.size()); + for (size_t i = 0; i < p_method_info.arguments.size(); i++) { + names.push_back(p_method_info.arguments[i].name); + } + set_argument_names(names); + } + generate_argument_types((int)p_method_info.arguments.size()); + set_return(should_returns); + set_static(true); + } + + ~MethodBindVarArgBaseS() {} + +private: + PropertyInfo _gen_return_type_info() const { + return reinterpret_cast(this)->_gen_return_type_info_impl(); + } +}; + +class MethodBindVarArgTS : public MethodBindVarArgBaseS { + friend class MethodBindVarArgBaseS; + +public: + virtual Variant call(GDExtensionClassInstancePtr p_instance, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionCallError &r_error) const { + (void)p_instance; // unused + MethodBindVarArgBaseS::function((const Variant **)p_args, p_argument_count, r_error); + return {}; + } + + MethodBindVarArgTS( + void (*p_function)(const Variant **, GDExtensionInt, GDExtensionCallError &), + const MethodInfo &p_method_info, + bool p_return_nil_is_variant) : + MethodBindVarArgBaseS(p_function, p_method_info, p_return_nil_is_variant) { + } + +private: + PropertyInfo _gen_return_type_info_impl() const { + return {}; + } +}; + +MethodBind *create_vararg_method_bind(void (*p_function)(const Variant **, GDExtensionInt, GDExtensionCallError &), const MethodInfo &p_info, bool p_return_nil_is_variant); + +template +class MethodBindVarArgTRS : public MethodBindVarArgBaseS, R, true> { + friend class MethodBindVarArgBaseS; + +public: + virtual Variant call(GDExtensionClassInstancePtr p_instance, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionCallError &r_error) const { + call_with_variant_args_static_dv(MethodBindVarArgBaseS, R, true>::function, p_args, p_argument_count, r_error); + return {}; + } + + MethodBindVarArgTRS( + void (*p_function)(const Variant **, GDExtensionInt, GDExtensionCallError &), + const MethodInfo &p_method_info, + bool p_return_nil_is_variant) : + MethodBindVarArgBaseS, R, true>(p_function, p_method_info, p_return_nil_is_variant) { + } + +private: + PropertyInfo _gen_return_type_info_impl() const { + return {}; + } +}; + +template +MethodBind *create_vararg_method_bind(R (*p_function)(const Variant **, GDExtensionInt, GDExtensionCallError &), const MethodInfo &p_info, bool p_return_nil_is_variant) { + MethodBind *a = memnew((MethodBindVarArgTRS)(p_function, p_info, p_return_nil_is_variant)); + return a; +} + #ifndef TYPED_METHOD_BIND class _gde_UnexistingClass; #define MB_T _gde_UnexistingClass diff --git a/src/core/method_bind.cpp b/src/core/method_bind.cpp index 0b9b5eba..a140f091 100644 --- a/src/core/method_bind.cpp +++ b/src/core/method_bind.cpp @@ -110,4 +110,9 @@ MethodBind::~MethodBind() { } } +MethodBind *create_vararg_method_bind(void (*p_function)(const Variant **, GDExtensionInt, GDExtensionCallError &), const MethodInfo &p_info, bool p_return_nil_is_variant) { + MethodBind *a = memnew((MethodBindVarArgTS)(p_function, p_info, p_return_nil_is_variant)); + return a; +} + } // namespace godot