Add support for default arguments in utility functions

Co-authored-by: Leo Belda <leo.belda@wanadoo.fr>
This commit is contained in:
Chaosus 2024-08-01 13:35:07 +03:00
parent 3978628c6c
commit bf4ff9b792
10 changed files with 366 additions and 314 deletions

View File

@ -580,14 +580,20 @@ Dictionary GDExtensionAPIDump::generate_extension_api(bool p_include_docs) {
bool vararg = Variant::is_utility_function_vararg(name); bool vararg = Variant::is_utility_function_vararg(name);
func["is_vararg"] = Variant::is_utility_function_vararg(name); func["is_vararg"] = Variant::is_utility_function_vararg(name);
func["hash"] = Variant::get_utility_function_hash(name); func["hash"] = Variant::get_utility_function_hash(name);
Array arguments; Array arguments;
const Vector<Variant> def_args = Variant::get_utility_function_default_arguments(name);
int argcount = Variant::get_utility_function_argument_count(name); int argcount = Variant::get_utility_function_argument_count(name);
for (int i = 0; i < argcount; i++) { for (int i = 0; i < argcount; i++) {
Dictionary arg; Dictionary arg;
String argname = vararg ? "arg" + itos(i + 1) : Variant::get_utility_function_argument_name(name, i); String argname = vararg ? "arg" + itos(i + 1) : Variant::get_utility_function_argument_name(name, i);
arg["name"] = argname; arg["name"] = argname;
arg["type"] = get_builtin_or_variant_type_name(Variant::get_utility_function_argument_type(name, i)); arg["type"] = get_builtin_or_variant_type_name(Variant::get_utility_function_argument_type(name, i));
//no default value support in utility functions
const int dargidx = Variant::get_utility_function_default_argument_index(name, i);
if (dargidx >= 0) {
arg["default_value"] = def_args[dargidx].get_construct_string();
}
arguments.push_back(arg); arguments.push_back(arg);
} }

View File

@ -551,7 +551,7 @@ public:
static uint32_t rand(); static uint32_t rand();
static _ALWAYS_INLINE_ double randd() { return (double)rand() / (double)Math::RANDOM_32BIT_MAX; } static _ALWAYS_INLINE_ double randd() { return (double)rand() / (double)Math::RANDOM_32BIT_MAX; }
static _ALWAYS_INLINE_ float randf() { return (float)rand() / (float)Math::RANDOM_32BIT_MAX; } static _ALWAYS_INLINE_ float randf() { return (float)rand() / (float)Math::RANDOM_32BIT_MAX; }
static double randfn(double mean, double deviation); static double randfn(double mean = 0.0, double deviation = 1.0);
static double random(double from, double to); static double random(double from, double to);
static float random(float from, float to); static float random(float from, float to);

View File

@ -752,6 +752,8 @@ public:
static int get_utility_function_argument_count(const StringName &p_name); static int get_utility_function_argument_count(const StringName &p_name);
static Variant::Type get_utility_function_argument_type(const StringName &p_name, int p_arg); static Variant::Type get_utility_function_argument_type(const StringName &p_name, int p_arg);
static String get_utility_function_argument_name(const StringName &p_name, int p_arg); static String get_utility_function_argument_name(const StringName &p_name, int p_arg);
static Vector<Variant> get_utility_function_default_arguments(const StringName &p_name);
static int get_utility_function_default_argument_index(const StringName &p_name, int p_arg);
static bool has_utility_function_return_value(const StringName &p_name); static bool has_utility_function_return_value(const StringName &p_name);
static Variant::Type get_utility_function_return_type(const StringName &p_name); static Variant::Type get_utility_function_return_type(const StringName &p_name);
static bool is_utility_function_vararg(const StringName &p_name); static bool is_utility_function_vararg(const StringName &p_name);

View File

@ -1228,47 +1228,19 @@ bool VariantUtilityFunctions::is_same(const Variant &p_a, const Variant &p_b) {
return p_a.identity_compare(p_b); return p_a.identity_compare(p_b);
} }
#ifdef DEBUG_METHODS_ENABLED
#define VCALLR *ret = p_func(VariantCasterAndValidate<P>::cast(p_args, Is, r_error)...)
#define VCALL p_func(VariantCasterAndValidate<P>::cast(p_args, Is, r_error)...)
#else
#define VCALLR *ret = p_func(VariantCaster<P>::cast(*p_args[Is])...)
#define VCALL p_func(VariantCaster<P>::cast(*p_args[Is])...)
#endif
template <typename R, typename... P, size_t... Is>
static _FORCE_INLINE_ void call_helperpr(R (*p_func)(P...), Variant *ret, const Variant **p_args, Callable::CallError &r_error, IndexSequence<Is...>) {
r_error.error = Callable::CallError::CALL_OK;
VCALLR;
(void)p_args; // avoid gcc warning
(void)r_error;
}
template <typename R, typename... P, size_t... Is>
static _FORCE_INLINE_ void validated_call_helperpr(R (*p_func)(P...), Variant *ret, const Variant **p_args, IndexSequence<Is...>) {
*ret = p_func(VariantCaster<P>::cast(*p_args[Is])...);
(void)p_args;
}
template <typename R, typename... P, size_t... Is>
static _FORCE_INLINE_ void ptr_call_helperpr(R (*p_func)(P...), void *ret, const void **p_args, IndexSequence<Is...>) {
PtrToArg<R>::encode(p_func(PtrToArg<P>::convert(p_args[Is])...), ret);
(void)p_args;
}
template <typename R, typename... P> template <typename R, typename... P>
static _FORCE_INLINE_ void call_helperr(R (*p_func)(P...), Variant *ret, const Variant **p_args, Callable::CallError &r_error) { static _FORCE_INLINE_ void call_helperr(R (*p_func)(P...), Variant *ret, const Variant **p_args, int p_argcount, const Vector<Variant> &p_defvals, Callable::CallError &r_error) {
call_helperpr(p_func, ret, p_args, r_error, BuildIndexSequence<sizeof...(P)>{}); call_with_variant_args_static_ret_dv(p_func, p_args, p_argcount, *ret, r_error, p_defvals);
} }
template <typename R, typename... P> template <typename R, typename... P>
static _FORCE_INLINE_ void validated_call_helperr(R (*p_func)(P...), Variant *ret, const Variant **p_args) { static _FORCE_INLINE_ void validated_call_helperr(R (*p_func)(P...), Variant *ret, const Variant **p_args) {
validated_call_helperpr(p_func, ret, p_args, BuildIndexSequence<sizeof...(P)>{}); call_with_validated_variant_args_static_method_ret(p_func, p_args, ret);
} }
template <typename R, typename... P> template <typename R, typename... P>
static _FORCE_INLINE_ void ptr_call_helperr(R (*p_func)(P...), void *ret, const void **p_args) { static _FORCE_INLINE_ void ptr_call_helperr(R (*p_func)(P...), void *ret, const void **p_args) {
ptr_call_helperpr(p_func, ret, p_args, BuildIndexSequence<sizeof...(P)>{}); call_with_ptr_args_static_method_ret<R, P...>(p_func, p_args, ret);
} }
template <typename R, typename... P> template <typename R, typename... P>
@ -1288,20 +1260,6 @@ static _FORCE_INLINE_ Variant::Type get_ret_type_helperr(R (*p_func)(P...)) {
// WITHOUT RET // WITHOUT RET
template <typename... P, size_t... Is>
static _FORCE_INLINE_ void call_helperp(void (*p_func)(P...), const Variant **p_args, Callable::CallError &r_error, IndexSequence<Is...>) {
r_error.error = Callable::CallError::CALL_OK;
VCALL;
(void)p_args;
(void)r_error;
}
template <typename... P, size_t... Is>
static _FORCE_INLINE_ void validated_call_helperp(void (*p_func)(P...), const Variant **p_args, IndexSequence<Is...>) {
p_func(VariantCaster<P>::cast(*p_args[Is])...);
(void)p_args;
}
template <typename... P, size_t... Is> template <typename... P, size_t... Is>
static _FORCE_INLINE_ void ptr_call_helperp(void (*p_func)(P...), const void **p_args, IndexSequence<Is...>) { static _FORCE_INLINE_ void ptr_call_helperp(void (*p_func)(P...), const void **p_args, IndexSequence<Is...>) {
p_func(PtrToArg<P>::convert(p_args[Is])...); p_func(PtrToArg<P>::convert(p_args[Is])...);
@ -1309,18 +1267,18 @@ static _FORCE_INLINE_ void ptr_call_helperp(void (*p_func)(P...), const void **p
} }
template <typename... P> template <typename... P>
static _FORCE_INLINE_ void call_helper(void (*p_func)(P...), const Variant **p_args, Callable::CallError &r_error) { static _FORCE_INLINE_ void call_helper(void (*p_func)(P...), const Variant **p_args, int p_argcount, const Vector<Variant> &p_defvals, Callable::CallError &r_error) {
call_helperp(p_func, p_args, r_error, BuildIndexSequence<sizeof...(P)>{}); call_with_variant_args_static_dv(p_func, p_args, p_argcount, r_error, p_defvals);
} }
template <typename... P> template <typename... P>
static _FORCE_INLINE_ void validated_call_helper(void (*p_func)(P...), const Variant **p_args) { static _FORCE_INLINE_ void validated_call_helper(void (*p_func)(P...), const Variant **p_args) {
validated_call_helperp(p_func, p_args, BuildIndexSequence<sizeof...(P)>{}); call_with_validated_variant_args_static_method(p_func, p_args);
} }
template <typename... P> template <typename... P>
static _FORCE_INLINE_ void ptr_call_helper(void (*p_func)(P...), const void **p_args) { static _FORCE_INLINE_ void ptr_call_helper(void (*p_func)(P...), const void **p_args) {
ptr_call_helperp(p_func, p_args, BuildIndexSequence<sizeof...(P)>{}); call_with_ptr_args_static_method<P...>(p_func, p_args);
} }
template <typename... P> template <typename... P>
@ -1338,105 +1296,134 @@ static _FORCE_INLINE_ Variant::Type get_ret_type_helper(void (*p_func)(P...)) {
return Variant::NIL; return Variant::NIL;
} }
#define FUNCBINDR(m_func, m_args, m_category) \ #define FUNCBINDR(m_func, m_args, m_category) \
class Func_##m_func { \ class Func_##m_func { \
public: \ public: \
static void call(Variant *r_ret, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { \ static void call(Variant *r_ret, const Variant **p_args, int p_argcount, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { \
call_helperr(VariantUtilityFunctions::m_func, r_ret, p_args, r_error); \ call_helperr(VariantUtilityFunctions::m_func, r_ret, p_args, p_argcount, varray(), r_error); \
} \ } \
static void validated_call(Variant *r_ret, const Variant **p_args, int p_argcount) { \ static void validated_call(Variant *r_ret, const Variant **p_args, int p_argcount) { \
validated_call_helperr(VariantUtilityFunctions::m_func, r_ret, p_args); \ validated_call_helperr(VariantUtilityFunctions::m_func, r_ret, p_args); \
} \ } \
static void ptrcall(void *ret, const void **p_args, int p_argcount) { \ static void ptrcall(void *ret, const void **p_args, int p_argcount) { \
ptr_call_helperr(VariantUtilityFunctions::m_func, ret, p_args); \ ptr_call_helperr(VariantUtilityFunctions::m_func, ret, p_args); \
} \ } \
static int get_argument_count() { \ static int get_argument_count() { \
return get_arg_count_helperr(VariantUtilityFunctions::m_func); \ return get_arg_count_helperr(VariantUtilityFunctions::m_func); \
} \ } \
static Variant::Type get_argument_type(int p_arg) { \ static Variant::Type get_argument_type(int p_arg) { \
return get_arg_type_helperr(VariantUtilityFunctions::m_func, p_arg); \ return get_arg_type_helperr(VariantUtilityFunctions::m_func, p_arg); \
} \ } \
static Variant::Type get_return_type() { \ static Variant::Type get_return_type() { \
return get_ret_type_helperr(VariantUtilityFunctions::m_func); \ return get_ret_type_helperr(VariantUtilityFunctions::m_func); \
} \ } \
static bool has_return_type() { \ static bool has_return_type() { \
return true; \ return true; \
} \ } \
static bool is_vararg() { return false; } \ static bool is_vararg() { return false; } \
static Variant::UtilityFunctionType get_type() { return m_category; } \ static Variant::UtilityFunctionType get_type() { return m_category; } \
}; \ }; \
register_utility_function<Func_##m_func>(#m_func, m_args) register_utility_function<Func_##m_func>(#m_func, m_args, varray())
#define FUNCBINDVR(m_func, m_args, m_category) \ #define FUNCBINDRD(m_func, m_args, m_defvals, m_category) \
class Func_##m_func { \ class Func_##m_func { \
public: \ public: \
static void call(Variant *r_ret, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { \ static void call(Variant *r_ret, const Variant **p_args, int p_argcount, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { \
r_error.error = Callable::CallError::CALL_OK; \ call_helperr(VariantUtilityFunctions::m_func, r_ret, p_args, p_argcount, p_defvals, r_error); \
*r_ret = VariantUtilityFunctions::m_func(*p_args[0], r_error); \ } \
} \ static void validated_call(Variant *r_ret, const Variant **p_args, int p_argcount) { \
static void validated_call(Variant *r_ret, const Variant **p_args, int p_argcount) { \ validated_call_helperr(VariantUtilityFunctions::m_func, r_ret, p_args); \
Callable::CallError ce; \ } \
*r_ret = VariantUtilityFunctions::m_func(*p_args[0], ce); \ static void ptrcall(void *ret, const void **p_args, int p_argcount) { \
} \ ptr_call_helperr(VariantUtilityFunctions::m_func, ret, p_args); \
static void ptrcall(void *ret, const void **p_args, int p_argcount) { \ } \
Callable::CallError ce; \ static int get_argument_count() { \
PtrToArg<Variant>::encode(VariantUtilityFunctions::m_func(PtrToArg<Variant>::convert(p_args[0]), ce), ret); \ return get_arg_count_helperr(VariantUtilityFunctions::m_func); \
} \ } \
static int get_argument_count() { \ static Variant::Type get_argument_type(int p_arg) { \
return 1; \ return get_arg_type_helperr(VariantUtilityFunctions::m_func, p_arg); \
} \ } \
static Variant::Type get_argument_type(int p_arg) { \ static Variant::Type get_return_type() { \
return Variant::NIL; \ return get_ret_type_helperr(VariantUtilityFunctions::m_func); \
} \ } \
static Variant::Type get_return_type() { \ static bool has_return_type() { \
return Variant::NIL; \ return true; \
} \ } \
static bool has_return_type() { \ static bool is_vararg() { return false; } \
return true; \ static Variant::UtilityFunctionType get_type() { return m_category; } \
} \ }; \
static bool is_vararg() { return false; } \ register_utility_function<Func_##m_func>(#m_func, m_args, m_defvals)
static Variant::UtilityFunctionType get_type() { return m_category; } \
}; \
register_utility_function<Func_##m_func>(#m_func, m_args)
#define FUNCBINDVR2(m_func, m_args, m_category) \ #define FUNCBINDVR(m_func, m_args, m_category) \
class Func_##m_func { \ class Func_##m_func { \
public: \ public: \
static void call(Variant *r_ret, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { \ static void call(Variant *r_ret, const Variant **p_args, int p_argcount, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { \
r_error.error = Callable::CallError::CALL_OK; \ r_error.error = Callable::CallError::CALL_OK; \
*r_ret = VariantUtilityFunctions::m_func(*p_args[0], *p_args[1], r_error); \ *r_ret = VariantUtilityFunctions::m_func(*p_args[0], r_error); \
} \ } \
static void validated_call(Variant *r_ret, const Variant **p_args, int p_argcount) { \ static void validated_call(Variant *r_ret, const Variant **p_args, int p_argcount) { \
Callable::CallError ce; \ Callable::CallError ce; \
*r_ret = VariantUtilityFunctions::m_func(*p_args[0], *p_args[1], ce); \ *r_ret = VariantUtilityFunctions::m_func(*p_args[0], ce); \
} \ } \
static void ptrcall(void *ret, const void **p_args, int p_argcount) { \ static void ptrcall(void *ret, const void **p_args, int p_argcount) { \
Callable::CallError ce; \ Callable::CallError ce; \
Variant r; \ PtrToArg<Variant>::encode(VariantUtilityFunctions::m_func(PtrToArg<Variant>::convert(p_args[0]), ce), ret); \
r = VariantUtilityFunctions::m_func(PtrToArg<Variant>::convert(p_args[0]), PtrToArg<Variant>::convert(p_args[1]), ce); \ } \
PtrToArg<Variant>::encode(r, ret); \ static int get_argument_count() { \
} \ return 1; \
static int get_argument_count() { \ } \
return 2; \ static Variant::Type get_argument_type(int p_arg) { \
} \ return Variant::NIL; \
static Variant::Type get_argument_type(int p_arg) { \ } \
return Variant::NIL; \ static Variant::Type get_return_type() { \
} \ return Variant::NIL; \
static Variant::Type get_return_type() { \ } \
return Variant::NIL; \ static bool has_return_type() { \
} \ return true; \
static bool has_return_type() { \ } \
return true; \ static bool is_vararg() { return false; } \
} \ static Variant::UtilityFunctionType get_type() { return m_category; } \
static bool is_vararg() { return false; } \ }; \
static Variant::UtilityFunctionType get_type() { return m_category; } \ register_utility_function<Func_##m_func>(#m_func, m_args, varray())
}; \
register_utility_function<Func_##m_func>(#m_func, m_args) #define FUNCBINDVR2(m_func, m_args, m_category) \
class Func_##m_func { \
public: \
static void call(Variant *r_ret, const Variant **p_args, int p_argcount, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { \
r_error.error = Callable::CallError::CALL_OK; \
*r_ret = VariantUtilityFunctions::m_func(*p_args[0], *p_args[1], r_error); \
} \
static void validated_call(Variant *r_ret, const Variant **p_args, int p_argcount) { \
Callable::CallError ce; \
*r_ret = VariantUtilityFunctions::m_func(*p_args[0], *p_args[1], ce); \
} \
static void ptrcall(void *ret, const void **p_args, int p_argcount) { \
Callable::CallError ce; \
Variant r; \
r = VariantUtilityFunctions::m_func(PtrToArg<Variant>::convert(p_args[0]), PtrToArg<Variant>::convert(p_args[1]), ce); \
PtrToArg<Variant>::encode(r, ret); \
} \
static int get_argument_count() { \
return 2; \
} \
static Variant::Type get_argument_type(int p_arg) { \
return Variant::NIL; \
} \
static Variant::Type get_return_type() { \
return Variant::NIL; \
} \
static bool has_return_type() { \
return true; \
} \
static bool is_vararg() { return false; } \
static Variant::UtilityFunctionType get_type() { return m_category; } \
}; \
register_utility_function<Func_##m_func>(#m_func, m_args, varray())
#define FUNCBINDVR3(m_func, m_args, m_category) \ #define FUNCBINDVR3(m_func, m_args, m_category) \
class Func_##m_func { \ class Func_##m_func { \
public: \ public: \
static void call(Variant *r_ret, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { \ static void call(Variant *r_ret, const Variant **p_args, int p_argcount, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { \
r_error.error = Callable::CallError::CALL_OK; \ r_error.error = Callable::CallError::CALL_OK; \
*r_ret = VariantUtilityFunctions::m_func(*p_args[0], *p_args[1], *p_args[2], r_error); \ *r_ret = VariantUtilityFunctions::m_func(*p_args[0], *p_args[1], *p_args[2], r_error); \
} \ } \
@ -1465,175 +1452,176 @@ static _FORCE_INLINE_ Variant::Type get_ret_type_helper(void (*p_func)(P...)) {
static bool is_vararg() { return false; } \ static bool is_vararg() { return false; } \
static Variant::UtilityFunctionType get_type() { return m_category; } \ static Variant::UtilityFunctionType get_type() { return m_category; } \
}; \ }; \
register_utility_function<Func_##m_func>(#m_func, m_args) register_utility_function<Func_##m_func>(#m_func, m_args, varray())
#define FUNCBINDVARARG(m_func, m_args, m_category) \ #define FUNCBINDVARARG(m_func, m_args, m_category) \
class Func_##m_func { \ class Func_##m_func { \
public: \ public: \
static void call(Variant *r_ret, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { \ static void call(Variant *r_ret, const Variant **p_args, int p_argcount, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { \
r_error.error = Callable::CallError::CALL_OK; \ r_error.error = Callable::CallError::CALL_OK; \
*r_ret = VariantUtilityFunctions::m_func(p_args, p_argcount, r_error); \ *r_ret = VariantUtilityFunctions::m_func(p_args, p_argcount, r_error); \
} \ } \
static void validated_call(Variant *r_ret, const Variant **p_args, int p_argcount) { \ static void validated_call(Variant *r_ret, const Variant **p_args, int p_argcount) { \
Callable::CallError c; \ Callable::CallError c; \
*r_ret = VariantUtilityFunctions::m_func(p_args, p_argcount, c); \ *r_ret = VariantUtilityFunctions::m_func(p_args, p_argcount, c); \
} \ } \
static void ptrcall(void *ret, const void **p_args, int p_argcount) { \ static void ptrcall(void *ret, const void **p_args, int p_argcount) { \
Vector<Variant> args; \ Vector<Variant> args; \
for (int i = 0; i < p_argcount; i++) { \ for (int i = 0; i < p_argcount; i++) { \
args.push_back(PtrToArg<Variant>::convert(p_args[i])); \ args.push_back(PtrToArg<Variant>::convert(p_args[i])); \
} \ } \
Vector<const Variant *> argsp; \ Vector<const Variant *> argsp; \
for (int i = 0; i < p_argcount; i++) { \ for (int i = 0; i < p_argcount; i++) { \
argsp.push_back(&args[i]); \ argsp.push_back(&args[i]); \
} \ } \
Variant r; \ Variant r; \
validated_call(&r, (const Variant **)argsp.ptr(), p_argcount); \ validated_call(&r, (const Variant **)argsp.ptr(), p_argcount); \
PtrToArg<Variant>::encode(r, ret); \ PtrToArg<Variant>::encode(r, ret); \
} \ } \
static int get_argument_count() { \ static int get_argument_count() { \
return 2; \ return 2; \
} \ } \
static Variant::Type get_argument_type(int p_arg) { \ static Variant::Type get_argument_type(int p_arg) { \
return Variant::NIL; \ return Variant::NIL; \
} \ } \
static Variant::Type get_return_type() { \ static Variant::Type get_return_type() { \
return Variant::NIL; \ return Variant::NIL; \
} \ } \
static bool has_return_type() { \ static bool has_return_type() { \
return true; \ return true; \
} \ } \
static bool is_vararg() { \ static bool is_vararg() { \
return true; \ return true; \
} \ } \
static Variant::UtilityFunctionType get_type() { \ static Variant::UtilityFunctionType get_type() { \
return m_category; \ return m_category; \
} \ } \
}; \ }; \
register_utility_function<Func_##m_func>(#m_func, m_args) register_utility_function<Func_##m_func>(#m_func, m_args, varray())
#define FUNCBINDVARARGS(m_func, m_args, m_category) \ #define FUNCBINDVARARGS(m_func, m_args, m_category) \
class Func_##m_func { \ class Func_##m_func { \
public: \ public: \
static void call(Variant *r_ret, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { \ static void call(Variant *r_ret, const Variant **p_args, int p_argcount, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { \
r_error.error = Callable::CallError::CALL_OK; \ r_error.error = Callable::CallError::CALL_OK; \
*r_ret = VariantUtilityFunctions::m_func(p_args, p_argcount, r_error); \ *r_ret = VariantUtilityFunctions::m_func(p_args, p_argcount, r_error); \
} \ } \
static void validated_call(Variant *r_ret, const Variant **p_args, int p_argcount) { \ static void validated_call(Variant *r_ret, const Variant **p_args, int p_argcount) { \
Callable::CallError c; \ Callable::CallError c; \
*r_ret = VariantUtilityFunctions::m_func(p_args, p_argcount, c); \ *r_ret = VariantUtilityFunctions::m_func(p_args, p_argcount, c); \
} \ } \
static void ptrcall(void *ret, const void **p_args, int p_argcount) { \ static void ptrcall(void *ret, const void **p_args, int p_argcount) { \
Vector<Variant> args; \ Vector<Variant> args; \
for (int i = 0; i < p_argcount; i++) { \ for (int i = 0; i < p_argcount; i++) { \
args.push_back(PtrToArg<Variant>::convert(p_args[i])); \ args.push_back(PtrToArg<Variant>::convert(p_args[i])); \
} \ } \
Vector<const Variant *> argsp; \ Vector<const Variant *> argsp; \
for (int i = 0; i < p_argcount; i++) { \ for (int i = 0; i < p_argcount; i++) { \
argsp.push_back(&args[i]); \ argsp.push_back(&args[i]); \
} \ } \
Variant r; \ Variant r; \
validated_call(&r, (const Variant **)argsp.ptr(), p_argcount); \ validated_call(&r, (const Variant **)argsp.ptr(), p_argcount); \
PtrToArg<String>::encode(r.operator String(), ret); \ PtrToArg<String>::encode(r.operator String(), ret); \
} \ } \
static int get_argument_count() { \ static int get_argument_count() { \
return 1; \ return 1; \
} \ } \
static Variant::Type get_argument_type(int p_arg) { \ static Variant::Type get_argument_type(int p_arg) { \
return Variant::NIL; \ return Variant::NIL; \
} \ } \
static Variant::Type get_return_type() { \ static Variant::Type get_return_type() { \
return Variant::STRING; \ return Variant::STRING; \
} \ } \
static bool has_return_type() { \ static bool has_return_type() { \
return true; \ return true; \
} \ } \
static bool is_vararg() { \ static bool is_vararg() { \
return true; \ return true; \
} \ } \
static Variant::UtilityFunctionType get_type() { \ static Variant::UtilityFunctionType get_type() { \
return m_category; \ return m_category; \
} \ } \
}; \ }; \
register_utility_function<Func_##m_func>(#m_func, m_args) register_utility_function<Func_##m_func>(#m_func, m_args, varray())
#define FUNCBINDVARARGV(m_func, m_args, m_category) \ #define FUNCBINDVARARGV(m_func, m_args, m_category) \
class Func_##m_func { \ class Func_##m_func { \
public: \ public: \
static void call(Variant *r_ret, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { \ static void call(Variant *r_ret, const Variant **p_args, int p_argcount, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { \
r_error.error = Callable::CallError::CALL_OK; \ r_error.error = Callable::CallError::CALL_OK; \
VariantUtilityFunctions::m_func(p_args, p_argcount, r_error); \ VariantUtilityFunctions::m_func(p_args, p_argcount, r_error); \
} \ } \
static void validated_call(Variant *r_ret, const Variant **p_args, int p_argcount) { \ static void validated_call(Variant *r_ret, const Variant **p_args, int p_argcount) { \
Callable::CallError c; \ Callable::CallError c; \
VariantUtilityFunctions::m_func(p_args, p_argcount, c); \ VariantUtilityFunctions::m_func(p_args, p_argcount, c); \
} \ } \
static void ptrcall(void *ret, const void **p_args, int p_argcount) { \ static void ptrcall(void *ret, const void **p_args, int p_argcount) { \
Vector<Variant> args; \ Vector<Variant> args; \
for (int i = 0; i < p_argcount; i++) { \ for (int i = 0; i < p_argcount; i++) { \
args.push_back(PtrToArg<Variant>::convert(p_args[i])); \ args.push_back(PtrToArg<Variant>::convert(p_args[i])); \
} \ } \
Vector<const Variant *> argsp; \ Vector<const Variant *> argsp; \
for (int i = 0; i < p_argcount; i++) { \ for (int i = 0; i < p_argcount; i++) { \
argsp.push_back(&args[i]); \ argsp.push_back(&args[i]); \
} \ } \
Variant r; \ Variant r; \
validated_call(&r, (const Variant **)argsp.ptr(), p_argcount); \ validated_call(&r, (const Variant **)argsp.ptr(), p_argcount); \
} \ } \
static int get_argument_count() { \ static int get_argument_count() { \
return 1; \ return 1; \
} \ } \
static Variant::Type get_argument_type(int p_arg) { \ static Variant::Type get_argument_type(int p_arg) { \
return Variant::NIL; \ return Variant::NIL; \
} \ } \
static Variant::Type get_return_type() { \ static Variant::Type get_return_type() { \
return Variant::NIL; \ return Variant::NIL; \
} \ } \
static bool has_return_type() { \ static bool has_return_type() { \
return false; \ return false; \
} \ } \
static bool is_vararg() { \ static bool is_vararg() { \
return true; \ return true; \
} \ } \
static Variant::UtilityFunctionType get_type() { \ static Variant::UtilityFunctionType get_type() { \
return m_category; \ return m_category; \
} \ } \
}; \ }; \
register_utility_function<Func_##m_func>(#m_func, m_args) register_utility_function<Func_##m_func>(#m_func, m_args, varray())
#define FUNCBIND(m_func, m_args, m_category) \ #define FUNCBIND(m_func, m_args, m_category) \
class Func_##m_func { \ class Func_##m_func { \
public: \ public: \
static void call(Variant *r_ret, const Variant **p_args, int p_argcount, Callable::CallError &r_error) { \ static void call(Variant *r_ret, const Variant **p_args, int p_argcount, const Vector<Variant> &p_defvals, Callable::CallError &r_error) { \
call_helper(VariantUtilityFunctions::m_func, p_args, r_error); \ call_helper(VariantUtilityFunctions::m_func, p_args, p_argcount, varray(), r_error); \
} \ } \
static void validated_call(Variant *r_ret, const Variant **p_args, int p_argcount) { \ static void validated_call(Variant *r_ret, const Variant **p_args, int p_argcount) { \
validated_call_helper(VariantUtilityFunctions::m_func, p_args); \ validated_call_helper(VariantUtilityFunctions::m_func, p_args); \
} \ } \
static void ptrcall(void *ret, const void **p_args, int p_argcount) { \ static void ptrcall(void *ret, const void **p_args, int p_argcount) { \
ptr_call_helper(VariantUtilityFunctions::m_func, p_args); \ ptr_call_helper(VariantUtilityFunctions::m_func, p_args); \
} \ } \
static int get_argument_count() { \ static int get_argument_count() { \
return get_arg_count_helper(VariantUtilityFunctions::m_func); \ return get_arg_count_helper(VariantUtilityFunctions::m_func); \
} \ } \
static Variant::Type get_argument_type(int p_arg) { \ static Variant::Type get_argument_type(int p_arg) { \
return get_arg_type_helper(VariantUtilityFunctions::m_func, p_arg); \ return get_arg_type_helper(VariantUtilityFunctions::m_func, p_arg); \
} \ } \
static Variant::Type get_return_type() { \ static Variant::Type get_return_type() { \
return get_ret_type_helper(VariantUtilityFunctions::m_func); \ return get_ret_type_helper(VariantUtilityFunctions::m_func); \
} \ } \
static bool has_return_type() { \ static bool has_return_type() { \
return false; \ return false; \
} \ } \
static bool is_vararg() { return false; } \ static bool is_vararg() { return false; } \
static Variant::UtilityFunctionType get_type() { return m_category; } \ static Variant::UtilityFunctionType get_type() { return m_category; } \
}; \ }; \
register_utility_function<Func_##m_func>(#m_func, m_args) register_utility_function<Func_##m_func>(#m_func, m_args, varray())
struct VariantUtilityFunctionInfo { struct VariantUtilityFunctionInfo {
void (*call_utility)(Variant *r_ret, const Variant **p_args, int p_argcount, Callable::CallError &r_error) = nullptr; void (*call_utility)(Variant *r_ret, const Variant **p_args, int p_argcount, const Vector<Variant> &p_defvals, Callable::CallError &r_error) = nullptr;
Variant::ValidatedUtilityFunction validated_call_utility = nullptr; Variant::ValidatedUtilityFunction validated_call_utility = nullptr;
Variant::PTRUtilityFunction ptr_call_utility = nullptr; Variant::PTRUtilityFunction ptr_call_utility = nullptr;
Vector<Variant> default_arguments;
Vector<String> argnames; Vector<String> argnames;
bool is_vararg = false; bool is_vararg = false;
bool returns_value = false; bool returns_value = false;
@ -1647,7 +1635,7 @@ static OAHashMap<StringName, VariantUtilityFunctionInfo> utility_function_table;
static List<StringName> utility_function_name_table; static List<StringName> utility_function_name_table;
template <typename T> template <typename T>
static void register_utility_function(const String &p_name, const Vector<String> &argnames) { static void register_utility_function(const String &p_name, const Vector<String> &argnames, const Vector<Variant> &p_def_args) {
String name = p_name; String name = p_name;
if (name.begins_with("_")) { if (name.begins_with("_")) {
name = name.substr(1, name.length() - 1); name = name.substr(1, name.length() - 1);
@ -1660,6 +1648,7 @@ static void register_utility_function(const String &p_name, const Vector<String>
bfi.validated_call_utility = T::validated_call; bfi.validated_call_utility = T::validated_call;
bfi.ptr_call_utility = T::ptrcall; bfi.ptr_call_utility = T::ptrcall;
bfi.is_vararg = T::is_vararg(); bfi.is_vararg = T::is_vararg();
bfi.default_arguments = p_def_args;
bfi.argnames = argnames; bfi.argnames = argnames;
bfi.argcount = T::get_argument_count(); bfi.argcount = T::get_argument_count();
if (!bfi.is_vararg) { if (!bfi.is_vararg) {
@ -1786,7 +1775,7 @@ void Variant::_register_variant_utility_functions() {
FUNCBINDR(randf, sarray(), Variant::UTILITY_FUNC_TYPE_RANDOM); FUNCBINDR(randf, sarray(), Variant::UTILITY_FUNC_TYPE_RANDOM);
FUNCBINDR(randi_range, sarray("from", "to"), Variant::UTILITY_FUNC_TYPE_RANDOM); FUNCBINDR(randi_range, sarray("from", "to"), Variant::UTILITY_FUNC_TYPE_RANDOM);
FUNCBINDR(randf_range, sarray("from", "to"), Variant::UTILITY_FUNC_TYPE_RANDOM); FUNCBINDR(randf_range, sarray("from", "to"), Variant::UTILITY_FUNC_TYPE_RANDOM);
FUNCBINDR(randfn, sarray("mean", "deviation"), Variant::UTILITY_FUNC_TYPE_RANDOM); FUNCBINDRD(randfn, sarray("mean", "deviation"), varray(0.0, 1.0), Variant::UTILITY_FUNC_TYPE_RANDOM);
FUNCBIND(seed, sarray("base"), Variant::UTILITY_FUNC_TYPE_RANDOM); FUNCBIND(seed, sarray("base"), Variant::UTILITY_FUNC_TYPE_RANDOM);
FUNCBINDR(rand_from_seed, sarray("seed"), Variant::UTILITY_FUNC_TYPE_RANDOM); FUNCBINDR(rand_from_seed, sarray("seed"), Variant::UTILITY_FUNC_TYPE_RANDOM);
@ -1843,9 +1832,9 @@ void Variant::call_utility_function(const StringName &p_name, Variant *r_ret, co
return; return;
} }
if (unlikely(!bfi->is_vararg && p_argcount < bfi->argcount)) { if (unlikely(!bfi->is_vararg && p_argcount < bfi->argcount - bfi->default_arguments.size())) {
r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS;
r_error.expected = bfi->argcount; r_error.expected = bfi->argcount - bfi->default_arguments.size();
return; return;
} }
@ -1855,7 +1844,7 @@ void Variant::call_utility_function(const StringName &p_name, Variant *r_ret, co
return; return;
} }
bfi->call_utility(r_ret, p_args, p_argcount, r_error); bfi->call_utility(r_ret, p_args, p_argcount, bfi->default_arguments, r_error);
} }
bool Variant::has_utility_function(const StringName &p_name) { bool Variant::has_utility_function(const StringName &p_name) {
@ -1907,6 +1896,7 @@ MethodInfo Variant::get_utility_function_info(const StringName &p_name) {
arg.name = bfi->argnames[i]; arg.name = bfi->argnames[i];
info.arguments.push_back(arg); info.arguments.push_back(arg);
} }
info.default_arguments = bfi->default_arguments;
} }
return info; return info;
} }
@ -1920,6 +1910,24 @@ int Variant::get_utility_function_argument_count(const StringName &p_name) {
return bfi->argcount; return bfi->argcount;
} }
Vector<Variant> Variant::get_utility_function_default_arguments(const StringName &p_name) {
const VariantUtilityFunctionInfo *bfi = utility_function_table.lookup_ptr(p_name);
if (!bfi) {
return varray();
}
return bfi->default_arguments;
}
int Variant::get_utility_function_default_argument_index(const StringName &p_name, int p_arg) {
const VariantUtilityFunctionInfo *bfi = utility_function_table.lookup_ptr(p_name);
if (!bfi) {
return false;
}
return p_arg - (bfi->argcount - bfi->default_arguments.size());
}
Variant::Type Variant::get_utility_function_argument_type(const StringName &p_name, int p_arg) { Variant::Type Variant::get_utility_function_argument_type(const StringName &p_name, int p_arg) {
const VariantUtilityFunctionInfo *bfi = utility_function_table.lookup_ptr(p_name); const VariantUtilityFunctionInfo *bfi = utility_function_table.lookup_ptr(p_name);
if (!bfi) { if (!bfi) {

View File

@ -119,7 +119,7 @@ struct VariantUtilityFunctions {
static void randomize(); static void randomize();
static int64_t randi(); static int64_t randi();
static double randf(); static double randf();
static double randfn(double mean, double deviation); static double randfn(double mean = 0.0, double deviation = 1.0);
static int64_t randi_range(int64_t from, int64_t to); static int64_t randi_range(int64_t from, int64_t to);
static double randf_range(double from, double to); static double randf_range(double from, double to);
static void seed(int64_t s); static void seed(int64_t s);

View File

@ -1033,8 +1033,8 @@
</method> </method>
<method name="randfn"> <method name="randfn">
<return type="float" /> <return type="float" />
<param index="0" name="mean" type="float" /> <param index="0" name="mean" type="float" default="0.0" />
<param index="1" name="deviation" type="float" /> <param index="1" name="deviation" type="float" default="1.0" />
<description> <description>
Returns a [url=https://en.wikipedia.org/wiki/Normal_distribution]normally-distributed[/url], pseudo-random floating-point value from the specified [param mean] and a standard [param deviation]. This is also known as a Gaussian distribution. Returns a [url=https://en.wikipedia.org/wiki/Normal_distribution]normally-distributed[/url], pseudo-random floating-point value from the specified [param mean] and a standard [param deviation]. This is also known as a Gaussian distribution.
[b]Note:[/b] This method uses the [url=https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform]Box-Muller transform[/url] algorithm. [b]Note:[/b] This method uses the [url=https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform]Box-Muller transform[/url] algorithm.

View File

@ -981,6 +981,7 @@ void DocTools::generate(BitField<GenerateFlags> p_flags) {
if (Variant::is_utility_function_vararg(E)) { if (Variant::is_utility_function_vararg(E)) {
md.qualifiers = "vararg"; md.qualifiers = "vararg";
} else { } else {
const Vector<Variant> def_args = Variant::get_utility_function_default_arguments(E);
for (int i = 0; i < Variant::get_utility_function_argument_count(E); i++) { for (int i = 0; i < Variant::get_utility_function_argument_count(E); i++) {
PropertyInfo pi; PropertyInfo pi;
pi.type = Variant::get_utility_function_argument_type(E, i); pi.type = Variant::get_utility_function_argument_type(E, i);
@ -990,6 +991,12 @@ void DocTools::generate(BitField<GenerateFlags> p_flags) {
} }
DocData::ArgumentDoc ad; DocData::ArgumentDoc ad;
DocData::argument_doc_from_arginfo(ad, pi); DocData::argument_doc_from_arginfo(ad, pi);
const int dargidx = Variant::get_utility_function_default_argument_index(E, i);
if (dargidx >= 0) {
ad.default_value = DocData::get_default_value_string(def_args[dargidx]);
}
md.arguments.push_back(ad); md.arguments.push_back(ad);
} }
} }

View File

@ -77,6 +77,7 @@ static MethodInfo info_from_utility_func(const StringName &p_function) {
pi.type = Variant::get_utility_function_argument_type(p_function, i); pi.type = Variant::get_utility_function_argument_type(p_function, i);
info.arguments.push_back(pi); info.arguments.push_back(pi);
} }
info.default_arguments = Variant::get_utility_function_default_arguments(p_function);
} }
return info; return info;

View File

@ -621,6 +621,20 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code
if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(call->function_name) < Variant::VARIANT_MAX) { if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptParser::get_builtin_type(call->function_name) < Variant::VARIANT_MAX) {
gen->write_construct(result, GDScriptParser::get_builtin_type(call->function_name), arguments); gen->write_construct(result, GDScriptParser::get_builtin_type(call->function_name), arguments);
} else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && Variant::has_utility_function(call->function_name)) { } else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && Variant::has_utility_function(call->function_name)) {
int arg_count = Variant::get_utility_function_argument_count(call->function_name);
if (call->arguments.size() < arg_count) {
Vector<Variant> def_args = Variant::get_utility_function_default_arguments(call->function_name);
for (int i = call->arguments.size(); i < arg_count; i++) {
const int dargidx = Variant::get_utility_function_default_argument_index(call->function_name, i);
if (dargidx >= 0) {
arguments.push_back(codegen.add_constant(def_args[dargidx]));
}
}
}
// Variant utility function. // Variant utility function.
gen->write_call_utility(result, call->function_name, arguments); gen->write_call_utility(result, call->function_name, arguments);
} else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptUtilityFunctions::function_exists(call->function_name)) { } else if (!call->is_super && call->callee->type == GDScriptParser::Node::IDENTIFIER && GDScriptUtilityFunctions::function_exists(call->function_name)) {

View File

@ -2167,12 +2167,19 @@ TEST_CASE("[Variant] Utility functions") {
if (Variant::is_utility_function_vararg(E)) { if (Variant::is_utility_function_vararg(E)) {
md.is_vararg = true; md.is_vararg = true;
} else { } else {
const Vector<Variant> def_args = Variant::get_utility_function_default_arguments(E);
for (int i = 0; i < Variant::get_utility_function_argument_count(E); i++) { for (int i = 0; i < Variant::get_utility_function_argument_count(E); i++) {
ArgumentData arg; ArgumentData arg;
arg.type = Variant::get_utility_function_argument_type(E, i); arg.type = Variant::get_utility_function_argument_type(E, i);
arg.name = Variant::get_utility_function_argument_name(E, i); arg.name = Variant::get_utility_function_argument_name(E, i);
arg.position = i; arg.position = i;
const int dargidx = Variant::get_utility_function_default_argument_index(E, i);
if (dargidx >= 0) {
arg.has_defval = true;
arg.defval = def_args[dargidx];
}
md.arguments.push_back(arg); md.arguments.push_back(arg);
} }
} }
@ -2187,6 +2194,13 @@ TEST_CASE("[Variant] Utility functions") {
TEST_COND((arg.name.is_empty() || arg.name.begins_with("_unnamed_arg")), TEST_COND((arg.name.is_empty() || arg.name.begins_with("_unnamed_arg")),
vformat("Unnamed argument in position %d of function '%s'.", arg.position, E.name)); vformat("Unnamed argument in position %d of function '%s'.", arg.position, E.name));
if (arg.has_defval) {
const bool arg_defval_assignable_to_type = arg.type == arg.defval.get_type();
TEST_COND(!arg_defval_assignable_to_type,
vformat("Invalid default value for parameter '%s' of function '%s'.", arg.name, E.name));
}
} }
} }
} }