Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Method Bind] Add support for default argument values and static method binding. #752

Merged
merged 1 commit into from
May 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
463 changes: 403 additions & 60 deletions godot-headers/extension_api.json

Large diffs are not rendered by default.

112 changes: 112 additions & 0 deletions include/godot_cpp/core/binder_common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,118 @@ GDNativeExtensionClassMethodArgumentMetadata call_get_argument_metadata(int p_ar
return md;
}

template <class... P, size_t... Is>
void call_with_variant_args_static(void (*p_method)(P...), const Variant **p_args, GDNativeCallError &r_error, IndexSequence<Is...>) {
r_error.error = GDNATIVE_CALL_OK;

#ifdef DEBUG_METHODS_ENABLED
(p_method)(VariantCasterAndValidate<P>::cast(p_args, Is, r_error)...);
#else
(p_method)(VariantCaster<P>::cast(*p_args[Is])...);
#endif
}

template <class... P>
void call_with_variant_args_static_dv(void (*p_method)(P...), const GDNativeVariantPtr *p_args, int p_argcount, GDNativeCallError &r_error, const std::vector<Variant> &default_values) {
#ifdef DEBUG_ENABLED
if ((size_t)p_argcount > sizeof...(P)) {
r_error.error = GDNATIVE_CALL_ERROR_TOO_MANY_ARGUMENTS;
r_error.argument = sizeof...(P);
return;
}
#endif

int32_t missing = (int32_t)sizeof...(P) - (int32_t)p_argcount;

int32_t dvs = default_values.size();
#ifdef DEBUG_ENABLED
if (missing > dvs) {
r_error.error = GDNATIVE_CALL_ERROR_TOO_FEW_ARGUMENTS;
r_error.argument = sizeof...(P);
return;
}
#endif

Variant args[sizeof...(P) == 0 ? 1 : sizeof...(P)]; // Avoid zero sized array.
std::array<const Variant *, sizeof...(P)> argsp;
for (int32_t i = 0; i < (int32_t)sizeof...(P); i++) {
if (i < p_argcount) {
args[i] = Variant(p_args[i]);
} else {
args[i] = default_values[i - p_argcount + (dvs - missing)];
}
argsp[i] = &args[i];
}

call_with_variant_args_static(p_method, argsp.data(), r_error, BuildIndexSequence<sizeof...(P)>{});
}

template <class... P, size_t... Is>
void call_with_ptr_args_static_method_helper(void (*p_method)(P...), const GDNativeTypePtr *p_args, IndexSequence<Is...>) {
p_method(PtrToArg<P>::convert(p_args[Is])...);
}

template <class... P>
void call_with_ptr_args_static_method(void (*p_method)(P...), const GDNativeTypePtr *p_args) {
call_with_ptr_args_static_method_helper<P...>(p_method, p_args, BuildIndexSequence<sizeof...(P)>{});
}

template <class R, class... P, size_t... Is>
void call_with_variant_args_static_ret(R (*p_method)(P...), const Variant **p_args, Variant &r_ret, GDNativeCallError &r_error, IndexSequence<Is...>) {
r_error.error = GDNATIVE_CALL_OK;

#ifdef DEBUG_METHODS_ENABLED
r_ret = (p_method)(VariantCasterAndValidate<P>::cast(p_args, Is, r_error)...);
#else
r_ret = (p_method)(VariantCaster<P>::cast(*p_args[Is])...);
#endif
}

template <class R, class... P>
void call_with_variant_args_static_ret_dv(R (*p_method)(P...), const GDNativeVariantPtr *p_args, int p_argcount, Variant &r_ret, GDNativeCallError &r_error, const std::vector<Variant> &default_values) {
#ifdef DEBUG_ENABLED
if ((size_t)p_argcount > sizeof...(P)) {
r_error.error = GDNATIVE_CALL_ERROR_TOO_MANY_ARGUMENTS;
r_error.argument = sizeof...(P);
return;
}
#endif

int32_t missing = (int32_t)sizeof...(P) - (int32_t)p_argcount;

int32_t dvs = default_values.size();
#ifdef DEBUG_ENABLED
if (missing > dvs) {
r_error.error = GDNATIVE_CALL_ERROR_TOO_FEW_ARGUMENTS;
r_error.argument = sizeof...(P);
return;
}
#endif

Variant args[sizeof...(P) == 0 ? 1 : sizeof...(P)]; // Avoid zero sized array.
std::array<const Variant *, sizeof...(P)> argsp;
for (int32_t i = 0; i < (int32_t)sizeof...(P); i++) {
if (i < p_argcount) {
args[i] = Variant(p_args[i]);
} else {
args[i] = default_values[i - p_argcount + (dvs - missing)];
}
argsp[i] = &args[i];
}

call_with_variant_args_static_ret(p_method, argsp.data(), r_ret, r_error, BuildIndexSequence<sizeof...(P)>{});
}

template <class R, class... P, size_t... Is>
void call_with_ptr_args_static_method_ret_helper(R (*p_method)(P...), const GDNativeTypePtr *p_args, void *r_ret, IndexSequence<Is...>) {
PtrToArg<R>::encode(p_method(PtrToArg<P>::convert(p_args[Is])...), r_ret);
}

template <class R, class... P>
void call_with_ptr_args_static_method_ret(R (*p_method)(P...), const GDNativeTypePtr *p_args, void *r_ret) {
call_with_ptr_args_static_method_ret_helper<R, P...>(p_method, p_args, r_ret, BuildIndexSequence<sizeof...(P)>{});
}

#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic pop
#endif
Expand Down
33 changes: 28 additions & 5 deletions include/godot_cpp/core/class_db.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@

namespace godot {

#define DEFVAL(m_defval) (m_defval)

struct MethodDefinition {
const char *name = nullptr;
std::list<std::string> args;
Expand Down Expand Up @@ -101,10 +103,15 @@ class ClassDB {
template <class T>
static void register_class();

template <class N, class M>
static MethodBind *bind_method(N p_method_name, M p_method);
template <class N, class M, typename... VarArgs>
static MethodBind *bind_method(N p_method_name, M p_method, VarArgs... p_args);

template <class N, class M, typename... VarArgs>
static MethodBind *bind_static_method(const char *p_class, N p_method_name, M p_method, VarArgs... p_args);

template <class M>
static MethodBind *bind_vararg_method(uint32_t p_flags, const char *p_name, M p_method, const MethodInfo &p_info = MethodInfo(), const std::vector<Variant> &p_default_args = std::vector<Variant>{}, bool p_return_nil_is_variant = true);

static void add_property_group(const char *p_class, const char *p_name, const char *p_prefix);
static void add_property_subgroup(const char *p_class, const char *p_name, const char *p_prefix);
static void add_property(const char *p_class, const PropertyInfo &p_pinfo, const char *p_setter, const char *p_getter, int p_index = -1);
Expand Down Expand Up @@ -172,11 +179,27 @@ void ClassDB::register_class() {
initialize_class(classes[cl.name]);
}

template <class N, class M>
MethodBind *ClassDB::bind_method(N p_method_name, M p_method) {
template <class N, class M, typename... VarArgs>
MethodBind *ClassDB::bind_method(N p_method_name, M p_method, VarArgs... p_args) {
Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
const Variant *argptrs[sizeof...(p_args) + 1];
for (uint32_t i = 0; i < sizeof...(p_args); i++) {
argptrs[i] = &args[i];
}
MethodBind *bind = create_method_bind(p_method);
return bind_methodfi(METHOD_FLAGS_DEFAULT, bind, p_method_name, sizeof...(p_args) == 0 ? nullptr : (const void **)argptrs, sizeof...(p_args));
}

return bind_methodfi(0, bind, p_method_name, nullptr, 0);
template <class N, class M, typename... VarArgs>
MethodBind *ClassDB::bind_static_method(const char *p_class, N p_method_name, M p_method, VarArgs... p_args) {
Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
const Variant *argptrs[sizeof...(p_args) + 1];
for (uint32_t i = 0; i < sizeof...(p_args); i++) {
argptrs[i] = &args[i];
}
MethodBind *bind = create_static_method_bind(p_method);
bind->set_instance_class(p_class);
return bind_methodfi(0, bind, p_method_name, sizeof...(p_args) == 0 ? nullptr : (const void **)argptrs, sizeof...(p_args));
}

template <class M>
Expand Down
146 changes: 141 additions & 5 deletions include/godot_cpp/core/method_bind.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,10 @@ class MethodBind {
int argument_count = 0;
uint32_t hint_flags = METHOD_FLAGS_DEFAULT;

bool _static = false;
bool _is_const = false;
bool _has_return = false;
bool _vararg = false;

std::vector<std::string> argument_names;
GDNativeVariantType *argument_types = nullptr;
Expand All @@ -66,6 +68,8 @@ class MethodBind {
void generate_argument_types(int p_count);
void set_const(bool p_const);
void set_return(bool p_return);
void set_static(bool p_static);
void set_vararg(bool p_vararg);
void set_argument_count(int p_count);

public:
Expand Down Expand Up @@ -96,8 +100,10 @@ class MethodBind {

_FORCE_INLINE_ int get_argument_count() const { return argument_count; };
_FORCE_INLINE_ bool is_const() const { return _is_const; }
_FORCE_INLINE_ bool is_static() const { return _static; }
_FORCE_INLINE_ bool is_vararg() const { return _vararg; }
_FORCE_INLINE_ bool has_return() const { return _has_return; }
_FORCE_INLINE_ uint32_t get_hint_flags() const { return hint_flags; }
_FORCE_INLINE_ uint32_t get_hint_flags() const { return hint_flags | (is_const() ? GDNATIVE_EXTENSION_METHOD_FLAG_CONST : 0) | (is_vararg() ? GDNATIVE_EXTENSION_METHOD_FLAG_VARARG : 0) | (is_static() ? GDNATIVE_EXTENSION_METHOD_FLAG_STATIC : 0); }
_FORCE_INLINE_ void set_hint_flags(uint32_t p_hint_flags) { hint_flags = p_hint_flags; }
void set_argument_names(const std::vector<std::string> &p_names);
std::vector<std::string> get_argument_names() const;
Expand Down Expand Up @@ -155,15 +161,13 @@ class MethodBindVarArgBase : public MethodBind {
ERR_FAIL(); // Can't call.
}

virtual bool is_const() const { return false; }

virtual bool is_vararg() const { return true; }

MethodBindVarArgBase(
R (T::*p_method)(const Variant **, GDNativeInt, GDNativeCallError &),
const MethodInfo &p_method_info,
bool p_return_nil_is_variant) :
method(p_method), method_info(p_method_info) {
set_vararg(true);
set_const(true);
set_argument_count(method_info.arguments.size());
if (method_info.arguments.size()) {
std::vector<std::string> names;
Expand Down Expand Up @@ -572,6 +576,138 @@ MethodBind *create_method_bind(R (T::*p_method)(P...) const) {
return a;
}

// STATIC BINDS

// no return

template <class... P>
class MethodBindTS : public MethodBind {
void (*function)(P...);

protected:
// GCC raises warnings in the case P = {} as the comparison is always false...
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wlogical-op"
#endif
virtual GDNativeVariantType gen_argument_type(int p_arg) const {
if (p_arg >= 0 && p_arg < (int)sizeof...(P)) {
return call_get_argument_type<P...>(p_arg);
} else {
return GDNATIVE_VARIANT_TYPE_NIL;
}
}

virtual GDNativePropertyInfo gen_argument_type_info(int p_arg) const {
GDNativePropertyInfo pi;
call_get_argument_type_info<P...>(p_arg, pi);
return pi;
}
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic pop
#endif

public:
virtual GDNativeExtensionClassMethodArgumentMetadata get_argument_metadata(int p_arg) const {
return call_get_argument_metadata<P...>(p_arg);
}

virtual Variant call(GDExtensionClassInstancePtr p_object, const GDNativeVariantPtr *p_args, const GDNativeInt p_arg_count, GDNativeCallError &r_error) const {
(void)p_object; // unused
call_with_variant_args_static_dv(function, p_args, p_arg_count, r_error, get_default_arguments());
return Variant();
}

virtual void ptrcall(GDExtensionClassInstancePtr p_object, const GDNativeTypePtr *p_args, GDNativeTypePtr r_ret) const {
(void)p_object;
(void)r_ret;
call_with_ptr_args_static_method(function, p_args);
}

MethodBindTS(void (*p_function)(P...)) {
function = p_function;
generate_argument_types(sizeof...(P));
set_argument_count(sizeof...(P));
set_static(true);
}
};

template <class... P>
MethodBind *create_static_method_bind(void (*p_method)(P...)) {
MethodBind *a = memnew((MethodBindTS<P...>)(p_method));
return a;
}

// return

template <class R, class... P>
class MethodBindTRS : public MethodBind {
R(*function)
(P...);

protected:
// GCC raises warnings in the case P = {} as the comparison is always false...
#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wlogical-op"
#endif
virtual GDNativeVariantType gen_argument_type(int p_arg) const {
if (p_arg >= 0 && p_arg < (int)sizeof...(P)) {
return call_get_argument_type<P...>(p_arg);
} else {
return GetTypeInfo<R>::VARIANT_TYPE;
}
}

virtual GDNativePropertyInfo gen_argument_type_info(int p_arg) const {
if (p_arg >= 0 && p_arg < (int)sizeof...(P)) {
GDNativePropertyInfo pi;
call_get_argument_type_info<P...>(p_arg, pi);
return pi;
} else {
return GetTypeInfo<R>::get_class_info();
}
}

#if defined(__GNUC__) && !defined(__clang__)
#pragma GCC diagnostic pop
#endif

public:
virtual GDNativeExtensionClassMethodArgumentMetadata get_argument_metadata(int p_arg) const {
if (p_arg >= 0) {
return call_get_argument_metadata<P...>(p_arg);
} else {
return GetTypeInfo<R>::METADATA;
}
}

virtual Variant call(GDExtensionClassInstancePtr p_object, const GDNativeVariantPtr *p_args, const GDNativeInt p_arg_count, GDNativeCallError &r_error) const {
Variant ret;
call_with_variant_args_static_ret_dv(function, p_args, p_arg_count, ret, r_error, get_default_arguments());
return ret;
}

virtual void ptrcall(GDExtensionClassInstancePtr p_object, const GDNativeTypePtr *p_args, GDNativeTypePtr r_ret) const {
(void)p_object;
call_with_ptr_args_static_method_ret(function, p_args, r_ret);
}

MethodBindTRS(R (*p_function)(P...)) {
function = p_function;
generate_argument_types(sizeof...(P));
set_argument_count(sizeof...(P));
set_static(true);
set_return(true);
}
};

template <class R, class... P>
MethodBind *create_static_method_bind(R (*p_method)(P...)) {
MethodBind *a = memnew((MethodBindTRS<R, P...>)(p_method));
return a;
}

} // namespace godot

#endif // ! GODOT_CPP_METHOD_BIND_HPP
6 changes: 3 additions & 3 deletions include/godot_cpp/core/object.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,20 +117,20 @@ struct MethodInfo {

template <class... Args>
MethodInfo::MethodInfo(const char *p_name, const Args &...args) :
name(p_name), flags(METHOD_FLAG_NORMAL) {
name(p_name), flags(GDNATIVE_EXTENSION_METHOD_FLAG_NORMAL) {
arguments = { args... };
}

template <class... Args>
MethodInfo::MethodInfo(Variant::Type ret, const char *p_name, const Args &...args) :
name(p_name), flags(METHOD_FLAG_NORMAL) {
name(p_name), flags(GDNATIVE_EXTENSION_METHOD_FLAG_NORMAL) {
return_val.type = ret;
arguments = { args... };
}

template <class... Args>
MethodInfo::MethodInfo(const PropertyInfo &p_ret, const char *p_name, const Args &...args) :
name(p_name), return_val(p_ret), flags(METHOD_FLAG_NORMAL) {
name(p_name), return_val(p_ret), flags(GDNATIVE_EXTENSION_METHOD_FLAG_NORMAL) {
arguments = { args... };
}

Expand Down
Loading