diff --git a/core/error/error_macros.h b/core/error/error_macros.h index 6ddcce3506ed..4b7a769538b3 100644 --- a/core/error/error_macros.h +++ b/core/error/error_macros.h @@ -175,7 +175,9 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, #define ERR_FAIL_INDEX_V(m_index, m_size, m_retval) \ if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \ + GODOT_DEPRECATED_BEGIN \ return m_retval; \ + GODOT_DEPRECATED_END \ } else \ ((void)0) @@ -186,7 +188,9 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, #define ERR_FAIL_INDEX_V_MSG(m_index, m_size, m_retval, m_msg) \ if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg); \ + GODOT_DEPRECATED_BEGIN \ return m_retval; \ + GODOT_DEPRECATED_END \ } else \ ((void)0) @@ -196,7 +200,9 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, #define ERR_FAIL_INDEX_V_EDMSG(m_index, m_size, m_retval, m_msg) \ if (unlikely((m_index) < 0 || (m_index) >= (m_size))) { \ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \ + GODOT_DEPRECATED_BEGIN \ return m_retval; \ + GODOT_DEPRECATED_END \ } else \ ((void)0) @@ -278,7 +284,9 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, #define ERR_FAIL_UNSIGNED_INDEX_V(m_index, m_size, m_retval) \ if (unlikely((m_index) >= (m_size))) { \ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size)); \ + GODOT_DEPRECATED_BEGIN \ return m_retval; \ + GODOT_DEPRECATED_END \ } else \ ((void)0) @@ -289,7 +297,9 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, #define ERR_FAIL_UNSIGNED_INDEX_V_MSG(m_index, m_size, m_retval, m_msg) \ if (unlikely((m_index) >= (m_size))) { \ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg); \ + GODOT_DEPRECATED_BEGIN \ return m_retval; \ + GODOT_DEPRECATED_END \ } else \ ((void)0) @@ -299,7 +309,9 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, #define ERR_FAIL_UNSIGNED_INDEX_V_EDMSG(m_index, m_size, m_retval, m_msg) \ if (unlikely((m_index) >= (m_size))) { \ _err_print_index_error(FUNCTION_STR, __FILE__, __LINE__, m_index, m_size, _STR(m_index), _STR(m_size), m_msg, true); \ + GODOT_DEPRECATED_BEGIN \ return m_retval; \ + GODOT_DEPRECATED_END \ } else \ ((void)0) @@ -381,7 +393,9 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, #define ERR_FAIL_NULL_V(m_param, m_retval) \ if (unlikely(m_param == nullptr)) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null."); \ + GODOT_DEPRECATED_BEGIN \ return m_retval; \ + GODOT_DEPRECATED_END \ } else \ ((void)0) @@ -392,7 +406,9 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, #define ERR_FAIL_NULL_V_MSG(m_param, m_retval, m_msg) \ if (unlikely(m_param == nullptr)) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg); \ + GODOT_DEPRECATED_BEGIN \ return m_retval; \ + GODOT_DEPRECATED_END \ } else \ ((void)0) @@ -402,7 +418,9 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, #define ERR_FAIL_NULL_V_EDMSG(m_param, m_retval, m_msg) \ if (unlikely(m_param == nullptr)) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg, true); \ + GODOT_DEPRECATED_BEGIN \ return m_retval; \ + GODOT_DEPRECATED_END \ } else \ ((void)0) @@ -458,7 +476,9 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, #define ERR_FAIL_COND_V(m_cond, m_retval) \ if (unlikely(m_cond)) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Returning: " _STR(m_retval)); \ + GODOT_DEPRECATED_BEGIN \ return m_retval; \ + GODOT_DEPRECATED_END \ } else \ ((void)0) @@ -472,7 +492,9 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, #define ERR_FAIL_COND_V_MSG(m_cond, m_retval, m_msg) \ if (unlikely(m_cond)) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Returning: " _STR(m_retval), m_msg); \ + GODOT_DEPRECATED_BEGIN \ return m_retval; \ + GODOT_DEPRECATED_END \ } else \ ((void)0) @@ -482,7 +504,9 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, #define ERR_FAIL_COND_V_EDMSG(m_cond, m_retval, m_msg) \ if (unlikely(m_cond)) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Condition \"" _STR(m_cond) "\" is true. Returning: " _STR(m_retval), m_msg, true); \ + GODOT_DEPRECATED_BEGIN \ return m_retval; \ + GODOT_DEPRECATED_END \ } else \ ((void)0) @@ -636,7 +660,9 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, #define ERR_FAIL_V(m_retval) \ if (true) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed. Returning: " _STR(m_retval)); \ + GODOT_DEPRECATED_BEGIN \ return m_retval; \ + GODOT_DEPRECATED_END \ } else \ ((void)0) @@ -649,7 +675,9 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, #define ERR_FAIL_V_MSG(m_retval, m_msg) \ if (true) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed. Returning: " _STR(m_retval), m_msg); \ + GODOT_DEPRECATED_BEGIN \ return m_retval; \ + GODOT_DEPRECATED_END \ } else \ ((void)0) @@ -659,7 +687,9 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file, #define ERR_FAIL_V_EDMSG(m_retval, m_msg) \ if (true) { \ _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Method/function failed. Returning: " _STR(m_retval), m_msg, true); \ + GODOT_DEPRECATED_BEGIN \ return m_retval; \ + GODOT_DEPRECATED_END \ } else \ ((void)0) diff --git a/core/extension/extension_api_dump.cpp b/core/extension/extension_api_dump.cpp index 0eaa1b850ab1..916cbb0fdb9c 100644 --- a/core/extension/extension_api_dump.cpp +++ b/core/extension/extension_api_dump.cpp @@ -88,7 +88,7 @@ static String get_property_info_type_name(const PropertyInfo &p_info) { } static String get_type_meta_name(const GodotTypeInfo::Metadata metadata) { - static const char *argmeta[13] = { "none", "int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "float", "double", "char16", "char32" }; + static const char *argmeta[14] = { "none", "int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "float", "double", "char16", "char32", "required" }; return argmeta[metadata]; } diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h index 8b52c5e5e640..67fc446186a8 100644 --- a/core/extension/gdextension_interface.h +++ b/core/extension/gdextension_interface.h @@ -432,6 +432,7 @@ typedef enum { GDEXTENSION_METHOD_ARGUMENT_METADATA_REAL_IS_DOUBLE, GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_CHAR16, GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_CHAR32, + GDEXTENSION_METHOD_ARGUMENT_METADATA_OBJECT_IS_REQUIRED, } GDExtensionClassMethodArgumentMetadata; typedef void (*GDExtensionClassMethodCall)(void *method_userdata, GDExtensionClassInstancePtr p_instance, const GDExtensionConstVariantPtr *p_args, GDExtensionInt p_argument_count, GDExtensionVariantPtr r_return, GDExtensionCallError *r_error); diff --git a/core/io/resource_saver.cpp b/core/io/resource_saver.cpp index 6e267a624435..da64aa4a22bb 100644 --- a/core/io/resource_saver.cpp +++ b/core/io/resource_saver.cpp @@ -97,8 +97,8 @@ void ResourceFormatSaver::_bind_methods() { GDVIRTUAL_BIND(_recognize_path, "resource", "path"); } -Error ResourceSaver::save(const Ref &p_resource, const String &p_path, uint32_t p_flags) { - ERR_FAIL_COND_V_MSG(p_resource.is_null(), ERR_INVALID_PARAMETER, vformat("Can't save empty resource to path '%s'.", p_path)); +Error ResourceSaver::save(const RequiredParam &rp_resource, const String &p_path, uint32_t p_flags) { + EXTRACT_REQUIRED_PARAM_OR_FAIL_V_MSG(p_resource, rp_resource, ERR_INVALID_PARAMETER, vformat("Can't save empty resource to path '%s'.", p_path)); String path = p_path; if (path.is_empty()) { path = p_resource->get_path(); diff --git a/core/io/resource_saver.h b/core/io/resource_saver.h index 5602a3a422ff..6f8cf5d62ce1 100644 --- a/core/io/resource_saver.h +++ b/core/io/resource_saver.h @@ -83,7 +83,7 @@ class ResourceSaver { FLAG_REPLACE_SUBRESOURCE_PATHS = 64, }; - static Error save(const Ref &p_resource, const String &p_path = "", uint32_t p_flags = (uint32_t)FLAG_NONE); + static Error save(const RequiredParam &p_resource, const String &p_path = "", uint32_t p_flags = (uint32_t)FLAG_NONE); static void get_recognized_extensions(const Ref &p_resource, List *p_extensions); static void add_resource_format_saver(Ref p_format_saver, bool p_at_front = false); static void remove_resource_format_saver(Ref p_format_saver); diff --git a/core/object/object.h b/core/object/object.h index 1fa64e68556d..1d267873e63b 100644 --- a/core/object/object.h +++ b/core/object/object.h @@ -39,6 +39,7 @@ #include "core/templates/hash_set.h" #include "core/templates/list.h" #include "core/templates/safe_refcount.h" +#include "core/variant/required_ptr.h" #include "core/variant/variant.h" template diff --git a/core/typedefs.h b/core/typedefs.h index 73ae13d76287..dee77d475e55 100644 --- a/core/typedefs.h +++ b/core/typedefs.h @@ -446,6 +446,15 @@ inline constexpr bool is_zero_constructible_v = is_zero_constructible::value; #define GODOT_MSVC_WARNING_PUSH_AND_IGNORE(m_warning) #endif +#define GODOT_DEPRECATED_BEGIN \ + GODOT_CLANG_WARNING_PUSH_AND_IGNORE("-Wdeprecated-declarations") \ + GODOT_GCC_WARNING_PUSH_AND_IGNORE("-Wdeprecated-declarations") \ + GODOT_MSVC_WARNING_PUSH_AND_IGNORE(4996) +#define GODOT_DEPRECATED_END \ + GODOT_CLANG_WARNING_POP \ + GODOT_GCC_WARNING_POP \ + GODOT_MSVC_WARNING_POP + template struct is_fully_defined : std::false_type {}; diff --git a/core/variant/binder_common.h b/core/variant/binder_common.h index 0b8f0000fb94..08eb38eaac9e 100644 --- a/core/variant/binder_common.h +++ b/core/variant/binder_common.h @@ -36,6 +36,7 @@ #include "core/templates/simple_type.h" #include "core/typedefs.h" #include "core/variant/method_ptrcall.h" +#include "core/variant/required_ptr.h" #include "core/variant/type_info.h" #include "core/variant/variant.h" #include "core/variant/variant_internal.h" diff --git a/core/variant/method_ptrcall.h b/core/variant/method_ptrcall.h index 639a3d2933e7..63c75f0f31cf 100644 --- a/core/variant/method_ptrcall.h +++ b/core/variant/method_ptrcall.h @@ -269,6 +269,51 @@ struct PtrToArg { } }; +// This is for RequiredParam. + +template +struct PtrToArg> { + typedef typename RequiredParam::value_type EncodeT; + + _FORCE_INLINE_ static RequiredParam convert(const void *p_ptr) { + if (p_ptr == nullptr) { + // Should we show an error? + GODOT_DEPRECATED_BEGIN + return RequiredParam::err_return(); + GODOT_DEPRECATED_END + } + return RequiredParam(*reinterpret_cast(p_ptr)); + } + + _FORCE_INLINE_ static void encode(const RequiredParam &p_var, void *p_ptr) { + GODOT_DEPRECATED_BEGIN + *((typename RequiredParam::value_type *)p_ptr) = p_var._internal_ptr(); + GODOT_DEPRECATED_END + } +}; + +// This is for RequiredResult. + +template +struct PtrToArg> { + typedef typename RequiredResult::value_type EncodeT; + + _FORCE_INLINE_ static RequiredResult convert(const void *p_ptr) { + if (p_ptr == nullptr) { + GODOT_DEPRECATED_BEGIN + return RequiredResult::err_return(); + GODOT_DEPRECATED_END + } + return RequiredResult(*reinterpret_cast(p_ptr)); + } + + _FORCE_INLINE_ static void encode(const RequiredResult &p_var, void *p_ptr) { + GODOT_DEPRECATED_BEGIN + *((typename RequiredResult::value_type *)p_ptr) = p_var._internal_ptr(); + GODOT_DEPRECATED_END + } +}; + // This is for ObjectID. template <> diff --git a/core/variant/required_ptr.h b/core/variant/required_ptr.h new file mode 100644 index 000000000000..f1bb593da05b --- /dev/null +++ b/core/variant/required_ptr.h @@ -0,0 +1,258 @@ +/**************************************************************************/ +/* required_ptr.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* 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. */ +/**************************************************************************/ + +#pragma once + +#include "core/variant/variant.h" + +template +class RequiredResult { + // We can't explicitly check if the type is an Object here, as it might've been forward-declared. + // However, we can at least assert that it's an unqualified, non-builtin type. + static_assert(!std::is_scalar_v, "T may not be a scalar type"); + static_assert(!std::is_reference_v, "T may not be a reference type"); + static_assert(!std::is_const_v && !std::is_volatile_v, "T may not be cv-qualified"); + +public: + using element_type = T; + using value_type = std::conditional_t, Ref, T *>; + +private: + value_type _value; + + _FORCE_INLINE_ RequiredResult() { + if constexpr (!std::is_base_of_v) { + _value = nullptr; + } + } + +public: + RequiredResult(const RequiredResult &p_other) = default; + RequiredResult(RequiredResult &&p_other) = default; + RequiredResult &operator=(const RequiredResult &p_other) = default; + RequiredResult &operator=(RequiredResult &&p_other) = default; + + _FORCE_INLINE_ RequiredResult(std::nullptr_t) : + RequiredResult() {} + _FORCE_INLINE_ RequiredResult &operator=(std::nullptr_t) { _value = nullptr; } + + [[nodiscard, deprecated("Should not be called directly; only for internal use.")]] + _FORCE_INLINE_ value_type _internal_ptr() const { return _value; } + [[nodiscard, deprecated("Should not be called directly; only for internal use.")]] + _FORCE_INLINE_ static RequiredResult err_return() { return RequiredResult(); } + + template , int> = 0> + _FORCE_INLINE_ RequiredResult(const RequiredResult &p_other) : + _value(p_other._value) {} + template , int> = 0> + _FORCE_INLINE_ RequiredResult &operator=(const RequiredResult &p_other) { + _value = p_other._value; + return *this; + } + + template , int> = 0> + _FORCE_INLINE_ RequiredResult(const T_Other *p_ptr) : + _value(const_cast *>(p_ptr)) {} + template , int> = 0> + _FORCE_INLINE_ RequiredResult &operator=(const T_Other *p_ptr) { + _value = const_cast *>(p_ptr); + return *this; + } + + template , int> = 0> + _FORCE_INLINE_ RequiredResult(const Ref &p_ref) : + _value(p_ref) {} + template , int> = 0> + _FORCE_INLINE_ RequiredResult &operator=(const Ref &p_ref) { + _value = p_ref; + return *this; + } + + template , int> = 0> + _FORCE_INLINE_ RequiredResult(Ref &&p_ref) : + _value(std::move(p_ref)) {} + template , int> = 0> + _FORCE_INLINE_ RequiredResult &operator=(Ref &&p_ref) { + _value = std::move(p_ref); + return &this; + } + + _FORCE_INLINE_ RequiredResult(const Variant &p_variant) : + _value(static_cast(p_variant.get_validated_object())) {} + _FORCE_INLINE_ RequiredResult &operator=(const Variant &p_variant) { + _value = static_cast(p_variant.get_validated_object()); + return *this; + } + + template , int> = 0> + _FORCE_INLINE_ element_type *ptr() const { + return *_value; + } + + template , int> = 0> + _FORCE_INLINE_ element_type *ptr() const { + return _value; + } + + _FORCE_INLINE_ operator value_type() { + return _value; + } + + _FORCE_INLINE_ operator Variant() const { + return Variant(_value); + } + + _FORCE_INLINE_ element_type *operator*() const { + return ptr(); + } + + _FORCE_INLINE_ element_type *operator->() const { + return ptr(); + } +}; + +template +class RequiredParam { + // We can't explicitly check if the type is an Object here, as it might've been forward-declared. + // However, we can at least assert that it's an unqualified, non-builtin type. + static_assert(!std::is_scalar_v, "T may not be a scalar type"); + static_assert(!std::is_reference_v, "T may not be a reference type"); + static_assert(!std::is_const_v && !std::is_volatile_v, "T may not be cv-qualified"); + +public: + using element_type = T; + using value_type = std::conditional_t, Ref, T *>; + +private: + value_type _value; + + _FORCE_INLINE_ RequiredParam() { + if constexpr (!std::is_base_of_v) { + _value = nullptr; + } + } + +public: + [[nodiscard, deprecated("Should not be called directly; only used in EXTRACT_REQUIRED_PARAM_OR_FAIL* macros.")]] + _FORCE_INLINE_ value_type _internal_ptr() const { return _value; } + [[nodiscard, deprecated("Should not be called directly; only used in EXTRACT_REQUIRED_PARAM_OR_FAIL* macros.")]] + _FORCE_INLINE_ bool _is_null() const { + if constexpr (std::is_base_of_v) { + return _value.is_null(); + } else { + return _value == nullptr; + } + } + + // Allow null construction if and only if returning from an error macro. + [[nodiscard, deprecated("Should not be called directly; must be used as return argument in error macro.")]] + _FORCE_INLINE_ static RequiredParam err_return() { return RequiredParam(); } + + // Prevent erroneously assigning null values by explicitly removing nullptr constructor/assignment. + RequiredParam(std::nullptr_t) = delete; + RequiredParam &operator=(std::nullptr_t) = delete; + + RequiredParam(const RequiredParam &p_other) = default; + RequiredParam(RequiredParam &&p_other) = default; + RequiredParam &operator=(const RequiredParam &p_other) = default; + RequiredParam &operator=(RequiredParam &&p_other) = default; + + GODOT_DEPRECATED_BEGIN + template , int> = 0> + _FORCE_INLINE_ RequiredParam(const RequiredParam &p_other) : + _value(p_other._internal_ptr()) {} + template , int> = 0> + _FORCE_INLINE_ RequiredParam &operator=(const RequiredParam &p_other) { + _value = p_other._internal_ptr(); + return *this; + } + GODOT_DEPRECATED_END + + template , int> = 0> + _FORCE_INLINE_ RequiredParam(const T_Other *p_ptr) : + _value(const_cast *>(p_ptr)) {} + template , int> = 0> + _FORCE_INLINE_ RequiredParam &operator=(const T_Other *p_ptr) { + _value = const_cast *>(p_ptr); + return *this; + } + + template , int> = 0> + _FORCE_INLINE_ RequiredParam(const Ref &p_ref) : + _value(p_ref) {} + template , int> = 0> + _FORCE_INLINE_ RequiredParam &operator=(const Ref &p_ref) { + _value = p_ref; + return *this; + } + + template , int> = 0> + _FORCE_INLINE_ RequiredParam(Ref &&p_ref) : + _value(std::move(p_ref)) {} + template , int> = 0> + _FORCE_INLINE_ RequiredParam &operator=(Ref &&p_ref) { + _value = std::move(p_ref); + return &this; + } + + _FORCE_INLINE_ RequiredParam(const Variant &p_variant) : + _value(static_cast(p_variant.get_validated_object())) {} + _FORCE_INLINE_ RequiredParam &operator=(const Variant &p_variant) { + _value = static_cast(p_variant.get_validated_object()); + return *this; + } +}; + +#define TMPL_EXTRACT_REQUIRED_PARAM_OR_FAIL(m_name, m_param, m_retval, m_msg, m_editor) \ + GODOT_DEPRECATED_BEGIN \ + if (unlikely(m_param._is_null())) { \ + _err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Required object \"" _STR(m_param) "\" is null.", m_msg, m_editor); \ + return m_retval; \ + } \ + std::conditional_t< \ + std::is_base_of_v::element_type>, \ + Ref::element_type>, \ + std::decay_t::element_type *> \ + m_name = m_param._internal_ptr(); \ + GODOT_DEPRECATED_END \ + static_assert(true) + +#define EXTRACT_REQUIRED_PARAM_OR_FAIL(m_name, m_param) TMPL_EXTRACT_REQUIRED_PARAM_OR_FAIL(m_name, m_param, void(), "", false) +#define EXTRACT_REQUIRED_PARAM_OR_FAIL_MSG(m_name, m_param, m_msg) TMPL_EXTRACT_REQUIRED_PARAM_OR_FAIL(m_name, m_param, void(), m_msg, false) +#define EXTRACT_REQUIRED_PARAM_OR_FAIL_EDMSG(m_name, m_param, m_msg) TMPL_EXTRACT_REQUIRED_PARAM_OR_FAIL(m_name, m_param, void(), m_msg, true) +#define EXTRACT_REQUIRED_PARAM_OR_FAIL_V(m_name, m_param, m_retval) TMPL_EXTRACT_REQUIRED_PARAM_OR_FAIL(m_name, m_param, m_retval, "", false) +#define EXTRACT_REQUIRED_PARAM_OR_FAIL_V_MSG(m_name, m_param, m_retval, m_msg) TMPL_EXTRACT_REQUIRED_PARAM_OR_FAIL(m_name, m_param, m_retval, m_msg, false) +#define EXTRACT_REQUIRED_PARAM_OR_FAIL_V_EDMSG(m_name, m_param, m_retval, m_msg) TMPL_EXTRACT_REQUIRED_PARAM_OR_FAIL(m_name, m_param, m_retval, m_msg, true) + +// Technically zero-constructible, but not recommended. +template +struct is_zero_constructible> : std::true_type {}; +template +struct is_zero_constructible> : std::true_type {}; diff --git a/core/variant/type_info.h b/core/variant/type_info.h index b6e4f21c1ce3..1c58dd9dce7e 100644 --- a/core/variant/type_info.h +++ b/core/variant/type_info.h @@ -52,6 +52,7 @@ enum Metadata { METADATA_REAL_IS_DOUBLE, METADATA_INT_IS_CHAR16, METADATA_INT_IS_CHAR32, + METADATA_OBJECT_IS_REQUIRED, }; } @@ -182,6 +183,44 @@ struct GetTypeInfo>> { } }; +template +class RequiredParam; + +template +class RequiredResult; + +template +struct GetTypeInfo, std::enable_if_t>> { + static const Variant::Type VARIANT_TYPE = Variant::OBJECT; + static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_OBJECT_IS_REQUIRED; + + template , int> = 0> + static inline PropertyInfo get_class_info() { + return PropertyInfo(Variant::OBJECT, String(), PROPERTY_HINT_RESOURCE_TYPE, T::get_class_static()); + } + + template , int> = 0> + static inline PropertyInfo get_class_info() { + return PropertyInfo(StringName(T::get_class_static())); + } +}; + +template +struct GetTypeInfo, std::enable_if_t>> { + static const Variant::Type VARIANT_TYPE = Variant::OBJECT; + static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_OBJECT_IS_REQUIRED; + + template , int> = 0> + static inline PropertyInfo get_class_info() { + return PropertyInfo(Variant::OBJECT, String(), PROPERTY_HINT_RESOURCE_TYPE, T::get_class_static()); + } + + template , int> = 0> + static inline PropertyInfo get_class_info() { + return PropertyInfo(StringName(T::get_class_static())); + } +}; + namespace GodotTypeInfo { namespace Internal { inline String enum_qualified_name_to_class_info_name(const String &p_qualified_name) { diff --git a/core/variant/variant_internal.h b/core/variant/variant_internal.h index 68b3f7e3c0e9..0cdab6f2824b 100644 --- a/core/variant/variant_internal.h +++ b/core/variant/variant_internal.h @@ -596,6 +596,30 @@ struct VariantInternalAccessor { static _FORCE_INLINE_ void set(Variant *v, const Object *p_value) { VariantInternal::object_assign(v, p_value); } }; +template +struct VariantInternalAccessor> { + static _FORCE_INLINE_ RequiredParam get(const Variant *v) { return RequiredParam(Object::cast_to(const_cast(*VariantInternal::get_object(v)))); } + static _FORCE_INLINE_ void set(Variant *v, const RequiredParam &p_value) { VariantInternal::object_assign(v, p_value.ptr()); } +}; + +template +struct VariantInternalAccessor &> { + static _FORCE_INLINE_ RequiredParam get(const Variant *v) { return RequiredParam(Object::cast_to(*VariantInternal::get_object(v))); } + static _FORCE_INLINE_ void set(Variant *v, const RequiredParam &p_value) { VariantInternal::object_assign(v, p_value.ptr()); } +}; + +template +struct VariantInternalAccessor> { + static _FORCE_INLINE_ RequiredResult get(const Variant *v) { return RequiredResult(Object::cast_to(const_cast(*VariantInternal::get_object(v)))); } + static _FORCE_INLINE_ void set(Variant *v, const RequiredResult &p_value) { VariantInternal::object_assign(v, p_value.ptr()); } +}; + +template +struct VariantInternalAccessor &> { + static _FORCE_INLINE_ RequiredResult get(const Variant *v) { return RequiredResult(Object::cast_to(*VariantInternal::get_object(v))); } + static _FORCE_INLINE_ void set(Variant *v, const RequiredResult &p_value) { VariantInternal::object_assign(v, p_value.ptr()); } +}; + template <> struct VariantInternalAccessor { static _FORCE_INLINE_ Variant &get(Variant *v) { return *v; } diff --git a/scene/animation/tween.cpp b/scene/animation/tween.cpp index 42abf97f874b..08c272471162 100644 --- a/scene/animation/tween.cpp +++ b/scene/animation/tween.cpp @@ -95,7 +95,7 @@ void Tween::_stop_internal(bool p_reset) { } } -Ref Tween::tween_property(const Object *p_target, const NodePath &p_property, Variant p_to, double p_duration) { +RequiredResult Tween::tween_property(const Object *p_target, const NodePath &p_property, Variant p_to, double p_duration) { ERR_FAIL_NULL_V(p_target, nullptr); CHECK_VALID(); @@ -118,7 +118,7 @@ Ref Tween::tween_property(const Object *p_target, const NodePat return tweener; } -Ref Tween::tween_interval(double p_time) { +RequiredResult Tween::tween_interval(double p_time) { CHECK_VALID(); Ref tweener; @@ -127,7 +127,7 @@ Ref Tween::tween_interval(double p_time) { return tweener; } -Ref Tween::tween_callback(const Callable &p_callback) { +RequiredResult Tween::tween_callback(const Callable &p_callback) { CHECK_VALID(); Ref tweener; @@ -136,7 +136,7 @@ Ref Tween::tween_callback(const Callable &p_callback) { return tweener; } -Ref Tween::tween_method(const Callable &p_callback, const Variant p_from, Variant p_to, double p_duration) { +RequiredResult Tween::tween_method(const Callable &p_callback, const Variant p_from, Variant p_to, double p_duration) { CHECK_VALID(); if (!Animation::validate_type_match(p_from, p_to)) { @@ -149,7 +149,7 @@ Ref Tween::tween_method(const Callable &p_callback, const Variant return tweener; } -Ref Tween::tween_subtween(const Ref &p_subtween) { +RequiredResult Tween::tween_subtween(const Ref &p_subtween) { CHECK_VALID(); // Ensure that the subtween being added is not null. @@ -221,7 +221,7 @@ void Tween::clear() { tweeners.clear(); } -Ref Tween::bind_node(const Node *p_node) { +RequiredResult Tween::bind_node(const Node *p_node) { ERR_FAIL_NULL_V(p_node, this); bound_node = p_node->get_instance_id(); @@ -229,7 +229,7 @@ Ref Tween::bind_node(const Node *p_node) { return this; } -Ref Tween::set_process_mode(TweenProcessMode p_mode) { +RequiredResult Tween::set_process_mode(TweenProcessMode p_mode) { process_mode = p_mode; return this; } @@ -238,7 +238,7 @@ Tween::TweenProcessMode Tween::get_process_mode() const { return process_mode; } -Ref Tween::set_pause_mode(TweenPauseMode p_mode) { +RequiredResult Tween::set_pause_mode(TweenPauseMode p_mode) { pause_mode = p_mode; return this; } @@ -247,7 +247,7 @@ Tween::TweenPauseMode Tween::get_pause_mode() const { return pause_mode; } -Ref Tween::set_ignore_time_scale(bool p_ignore) { +RequiredResult Tween::set_ignore_time_scale(bool p_ignore) { ignore_time_scale = p_ignore; return this; } @@ -256,13 +256,13 @@ bool Tween::is_ignoring_time_scale() const { return ignore_time_scale; } -Ref Tween::set_parallel(bool p_parallel) { +RequiredResult Tween::set_parallel(bool p_parallel) { default_parallel = p_parallel; parallel_enabled = p_parallel; return this; } -Ref Tween::set_loops(int p_loops) { +RequiredResult Tween::set_loops(int p_loops) { loops = p_loops; return this; } @@ -275,12 +275,12 @@ int Tween::get_loops_left() const { } } -Ref Tween::set_speed_scale(float p_speed) { +RequiredResult Tween::set_speed_scale(float p_speed) { speed_scale = p_speed; return this; } -Ref Tween::set_trans(TransitionType p_trans) { +RequiredResult Tween::set_trans(TransitionType p_trans) { default_transition = p_trans; return this; } @@ -289,7 +289,7 @@ Tween::TransitionType Tween::get_trans() const { return default_transition; } -Ref Tween::set_ease(EaseType p_ease) { +RequiredResult Tween::set_ease(EaseType p_ease) { default_ease = p_ease; return this; } @@ -298,12 +298,12 @@ Tween::EaseType Tween::get_ease() const { return default_ease; } -Ref Tween::parallel() { +RequiredResult Tween::parallel() { parallel_enabled = true; return this; } -Ref Tween::chain() { +RequiredResult Tween::chain() { parallel_enabled = false; return this; } @@ -554,7 +554,7 @@ double PropertyTweener::_get_custom_interpolated_value(const Variant &p_value) { return result; } -Ref PropertyTweener::from(const Variant &p_value) { +RequiredResult PropertyTweener::from(const Variant &p_value) { Ref tween = _get_tween(); ERR_FAIL_COND_V(tween.is_null(), nullptr); @@ -568,32 +568,32 @@ Ref PropertyTweener::from(const Variant &p_value) { return this; } -Ref PropertyTweener::from_current() { +RequiredResult PropertyTweener::from_current() { do_continue = false; return this; } -Ref PropertyTweener::as_relative() { +RequiredResult PropertyTweener::as_relative() { relative = true; return this; } -Ref PropertyTweener::set_trans(Tween::TransitionType p_trans) { +RequiredResult PropertyTweener::set_trans(Tween::TransitionType p_trans) { trans_type = p_trans; return this; } -Ref PropertyTweener::set_ease(Tween::EaseType p_ease) { +RequiredResult PropertyTweener::set_ease(Tween::EaseType p_ease) { ease_type = p_ease; return this; } -Ref PropertyTweener::set_custom_interpolator(const Callable &p_method) { +RequiredResult PropertyTweener::set_custom_interpolator(const Callable &p_method) { custom_method = p_method; return this; } -Ref PropertyTweener::set_delay(double p_delay) { +RequiredResult PropertyTweener::set_delay(double p_delay) { delay = p_delay; return this; } @@ -731,7 +731,7 @@ IntervalTweener::IntervalTweener() { ERR_FAIL_MSG("IntervalTweener can't be created directly. Use the tween_interval() method in Tween."); } -Ref CallbackTweener::set_delay(double p_delay) { +RequiredResult CallbackTweener::set_delay(double p_delay) { delay = p_delay; return this; } @@ -781,17 +781,17 @@ CallbackTweener::CallbackTweener() { ERR_FAIL_MSG("CallbackTweener can't be created directly. Use the tween_callback() method in Tween."); } -Ref MethodTweener::set_delay(double p_delay) { +RequiredResult MethodTweener::set_delay(double p_delay) { delay = p_delay; return this; } -Ref MethodTweener::set_trans(Tween::TransitionType p_trans) { +RequiredResult MethodTweener::set_trans(Tween::TransitionType p_trans) { trans_type = p_trans; return this; } -Ref MethodTweener::set_ease(Tween::EaseType p_ease) { +RequiredResult MethodTweener::set_ease(Tween::EaseType p_ease) { ease_type = p_ease; return this; } @@ -912,7 +912,7 @@ bool SubtweenTweener::step(double &r_delta) { return true; } -Ref SubtweenTweener::set_delay(double p_delay) { +RequiredResult SubtweenTweener::set_delay(double p_delay) { delay = p_delay; return this; } diff --git a/scene/animation/tween.h b/scene/animation/tween.h index ab1d7da6ae7e..e4f779901396 100644 --- a/scene/animation/tween.h +++ b/scene/animation/tween.h @@ -143,11 +143,11 @@ class Tween : public RefCounted { virtual String _to_string() override; public: - Ref tween_property(const Object *p_target, const NodePath &p_property, Variant p_to, double p_duration); - Ref tween_interval(double p_time); - Ref tween_callback(const Callable &p_callback); - Ref tween_method(const Callable &p_callback, const Variant p_from, Variant p_to, double p_duration); - Ref tween_subtween(const Ref &p_subtween); + RequiredResult tween_property(const Object *p_target, const NodePath &p_property, Variant p_to, double p_duration); + RequiredResult tween_interval(double p_time); + RequiredResult tween_callback(const Callable &p_callback); + RequiredResult tween_method(const Callable &p_callback, const Variant p_from, Variant p_to, double p_duration); + RequiredResult tween_subtween(const Ref &p_subtween); void append(Ref p_tweener); bool custom_step(double p_delta); @@ -160,25 +160,25 @@ class Tween : public RefCounted { bool is_valid(); void clear(); - Ref bind_node(const Node *p_node); - Ref set_process_mode(TweenProcessMode p_mode); + RequiredResult bind_node(const Node *p_node); + RequiredResult set_process_mode(TweenProcessMode p_mode); TweenProcessMode get_process_mode() const; - Ref set_pause_mode(TweenPauseMode p_mode); + RequiredResult set_pause_mode(TweenPauseMode p_mode); TweenPauseMode get_pause_mode() const; - Ref set_ignore_time_scale(bool p_ignore = true); + RequiredResult set_ignore_time_scale(bool p_ignore = true); bool is_ignoring_time_scale() const; - Ref set_parallel(bool p_parallel); - Ref set_loops(int p_loops); + RequiredResult set_parallel(bool p_parallel); + RequiredResult set_loops(int p_loops); int get_loops_left() const; - Ref set_speed_scale(float p_speed); - Ref set_trans(TransitionType p_trans); + RequiredResult set_speed_scale(float p_speed); + RequiredResult set_trans(TransitionType p_trans); TransitionType get_trans() const; - Ref set_ease(EaseType p_ease); + RequiredResult set_ease(EaseType p_ease); EaseType get_ease() const; - Ref parallel(); - Ref chain(); + RequiredResult parallel(); + RequiredResult chain(); static real_t run_equation(TransitionType p_trans_type, EaseType p_ease_type, real_t t, real_t b, real_t c, real_t d); static Variant interpolate_variant(const Variant &p_initial_val, const Variant &p_delta_val, double p_time, double p_duration, Tween::TransitionType p_trans, Tween::EaseType p_ease); @@ -203,13 +203,13 @@ class PropertyTweener : public Tweener { double _get_custom_interpolated_value(const Variant &p_value); public: - Ref from(const Variant &p_value); - Ref from_current(); - Ref as_relative(); - Ref set_trans(Tween::TransitionType p_trans); - Ref set_ease(Tween::EaseType p_ease); - Ref set_custom_interpolator(const Callable &p_method); - Ref set_delay(double p_delay); + RequiredResult from(const Variant &p_value); + RequiredResult from_current(); + RequiredResult as_relative(); + RequiredResult set_trans(Tween::TransitionType p_trans); + RequiredResult set_ease(Tween::EaseType p_ease); + RequiredResult set_custom_interpolator(const Callable &p_method); + RequiredResult set_delay(double p_delay); void set_tween(const Ref &p_tween) override; void start() override; @@ -259,7 +259,7 @@ class CallbackTweener : public Tweener { GDCLASS(CallbackTweener, Tweener); public: - Ref set_delay(double p_delay); + RequiredResult set_delay(double p_delay); bool step(double &r_delta) override; @@ -280,9 +280,9 @@ class MethodTweener : public Tweener { GDCLASS(MethodTweener, Tweener); public: - Ref set_trans(Tween::TransitionType p_trans); - Ref set_ease(Tween::EaseType p_ease); - Ref set_delay(double p_delay); + RequiredResult set_trans(Tween::TransitionType p_trans); + RequiredResult set_ease(Tween::EaseType p_ease); + RequiredResult set_delay(double p_delay); void set_tween(const Ref &p_tween) override; bool step(double &r_delta) override; @@ -315,7 +315,7 @@ class SubtweenTweener : public Tweener { void start() override; bool step(double &r_delta) override; - Ref set_delay(double p_delay); + RequiredResult set_delay(double p_delay); SubtweenTweener(const Ref &p_subtween); SubtweenTweener(); diff --git a/scene/main/node.cpp b/scene/main/node.cpp index 5db915c58a86..583bd27eea71 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -1690,11 +1690,11 @@ void Node::_add_child_nocheck(Node *p_child, const StringName &p_name, InternalM emit_signal(SNAME("child_order_changed")); } -void Node::add_child(Node *p_child, bool p_force_readable_name, InternalMode p_internal) { +void Node::add_child(const RequiredParam &rp_child, bool p_force_readable_name, InternalMode p_internal) { ERR_FAIL_COND_MSG(data.tree && !Thread::is_main_thread(), "Adding children to a node inside the SceneTree is only allowed from the main thread. Use call_deferred(\"add_child\",node)."); ERR_THREAD_GUARD - ERR_FAIL_NULL(p_child); + EXTRACT_REQUIRED_PARAM_OR_FAIL(p_child, rp_child); ERR_FAIL_COND_MSG(p_child == this, vformat("Can't add child '%s' to itself.", p_child->get_name())); // adding to itself! ERR_FAIL_COND_MSG(p_child->data.parent, vformat("Can't add child '%s' to '%s', already has a parent '%s'.", p_child->get_name(), get_name(), p_child->data.parent->get_name())); //Fail if node has a parent #ifdef DEBUG_ENABLED @@ -2648,7 +2648,7 @@ void Node::_propagate_replace_owner(Node *p_owner, Node *p_by_owner) { data.blocked--; } -Ref Node::create_tween() { +RequiredResult Node::create_tween() { ERR_THREAD_GUARD_V(Ref()); SceneTree *tree = data.tree; diff --git a/scene/main/node.h b/scene/main/node.h index 974a6b5b54df..66ff1ef5f096 100644 --- a/scene/main/node.h +++ b/scene/main/node.h @@ -509,7 +509,7 @@ class Node : public Object { InternalMode get_internal_mode() const; - void add_child(Node *p_child, bool p_force_readable_name = false, InternalMode p_internal = INTERNAL_MODE_DISABLED); + void add_child(const RequiredParam &rp_child, bool p_force_readable_name = false, InternalMode p_internal = INTERNAL_MODE_DISABLED); void add_sibling(Node *p_sibling, bool p_force_readable_name = false); void remove_child(Node *p_child); @@ -605,7 +605,7 @@ class Node : public Object { } } - Ref create_tween(); + RequiredResult create_tween(); void print_tree(); void print_tree_pretty(); diff --git a/scene/main/scene_tree.cpp b/scene/main/scene_tree.cpp index 07c47e8fc7b3..7781851423e2 100644 --- a/scene/main/scene_tree.cpp +++ b/scene/main/scene_tree.cpp @@ -1744,7 +1744,7 @@ Ref SceneTree::create_timer(double p_delay_sec, bool p_process_a return stt; } -Ref SceneTree::create_tween() { +RequiredResult SceneTree::create_tween() { _THREAD_SAFE_METHOD_ Ref tween; tween.instantiate(this); diff --git a/scene/main/scene_tree.h b/scene/main/scene_tree.h index 7383a1996d9d..ef9a14f76d95 100644 --- a/scene/main/scene_tree.h +++ b/scene/main/scene_tree.h @@ -428,7 +428,7 @@ class SceneTree : public MainLoop { void unload_current_scene(); Ref create_timer(double p_delay_sec, bool p_process_always = true, bool p_process_in_physics = false, bool p_ignore_time_scale = false); - Ref create_tween(); + RequiredResult create_tween(); void remove_tween(const Ref &p_tween); TypedArray get_processed_tweens(); diff --git a/tests/core/object/test_object.h b/tests/core/object/test_object.h index be98990884ff..8245c0a6f81c 100644 --- a/tests/core/object/test_object.h +++ b/tests/core/object/test_object.h @@ -596,4 +596,28 @@ TEST_CASE("[Object] Destruction at the end of the call chain is safe") { "Object was tail-deleted without crashes."); } +int required_param_compare(const Ref &p_ref, const RequiredParam &p_required) { + EXTRACT_REQUIRED_PARAM_OR_FAIL_V(extract, p_required, false); + ERR_FAIL_COND_V(p_ref->get_reference_count() != extract->get_reference_count(), -1); + return p_ref->get_reference_count(); +} + +TEST_CASE("[Object] RequiredParam Ref") { + Ref ref; + ref.instantiate(); + + RequiredParam required = ref; + EXTRACT_REQUIRED_PARAM_OR_FAIL(extract, required); + + static_assert(std::is_same_v); + + CHECK_EQ(ref->get_reference_count(), extract->get_reference_count()); + + const int count = required_param_compare(ref, ref); + CHECK_NE(count, -1); + CHECK_NE(count, ref->get_reference_count()); + + CHECK_EQ(ref->get_reference_count(), extract->get_reference_count()); +} + } // namespace TestObject