Skip to content

Commit 369afc7

Browse files
committed
Merge pull request godotengine#86079 from dsnopek/required-args
Add `RequiredParam<T>` and `RequiredResult<T>` to mark `Object *` arguments and return values as required
2 parents f1de9c4 + 090a454 commit 369afc7

19 files changed

Lines changed: 444 additions & 69 deletions

core/core_bind.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ void ResourceLoader::_bind_methods() {
167167

168168
////// ResourceSaver //////
169169

170-
Error ResourceSaver::save(const Ref<Resource> &p_resource, const String &p_path, BitField<SaverFlags> p_flags) {
170+
Error ResourceSaver::save(RequiredParam<Resource> p_resource, const String &p_path, BitField<SaverFlags> p_flags) {
171171
return ::ResourceSaver::save(p_resource, p_path, p_flags);
172172
}
173173

core/core_bind.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ class ResourceSaver : public Object {
109109

110110
static ResourceSaver *get_singleton() { return singleton; }
111111

112-
Error save(const Ref<Resource> &p_resource, const String &p_path, BitField<SaverFlags> p_flags);
112+
Error save(RequiredParam<Resource> p_resource, const String &p_path, BitField<SaverFlags> p_flags);
113113
Error set_uid(const String &p_path, ResourceUID::ID p_uid);
114114
Vector<String> get_recognized_extensions(const Ref<Resource> &p_resource);
115115
void add_resource_format_saver(Ref<ResourceFormatSaver> p_format_saver, bool p_at_front);

core/extension/extension_api_dump.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ static String get_property_info_type_name(const PropertyInfo &p_info) {
8888
}
8989

9090
static String get_type_meta_name(const GodotTypeInfo::Metadata metadata) {
91-
static const char *argmeta[13] = { "none", "int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "float", "double", "char16", "char32" };
91+
static const char *argmeta[14] = { "none", "int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "float", "double", "char16", "char32", "required" };
9292
return argmeta[metadata];
9393
}
9494

core/extension/gdextension_interface.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1942,6 +1942,10 @@
19421942
{
19431943
"name": "GDEXTENSION_METHOD_ARGUMENT_METADATA_INT_IS_CHAR32",
19441944
"value": 12
1945+
},
1946+
{
1947+
"name": "GDEXTENSION_METHOD_ARGUMENT_METADATA_OBJECT_IS_REQUIRED",
1948+
"value": 13
19451949
}
19461950
]
19471951
},

core/io/resource_saver.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,8 @@ void ResourceFormatSaver::_bind_methods() {
9797
GDVIRTUAL_BIND(_recognize_path, "resource", "path");
9898
}
9999

100-
Error ResourceSaver::save(const Ref<Resource> &p_resource, const String &p_path, uint32_t p_flags) {
101-
ERR_FAIL_COND_V_MSG(p_resource.is_null(), ERR_INVALID_PARAMETER, vformat("Can't save empty resource to path '%s'.", p_path));
100+
Error ResourceSaver::save(RequiredParam<Resource> rp_resource, const String &p_path, uint32_t p_flags) {
101+
EXTRACT_PARAM_OR_FAIL_V_MSG(p_resource, rp_resource, ERR_INVALID_PARAMETER, vformat("Can't save empty resource to path '%s'.", p_path));
102102
String path = p_path;
103103
if (path.is_empty()) {
104104
path = p_resource->get_path();

core/io/resource_saver.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ class ResourceSaver {
8383
FLAG_REPLACE_SUBRESOURCE_PATHS = 64,
8484
};
8585

86-
static Error save(const Ref<Resource> &p_resource, const String &p_path = "", uint32_t p_flags = (uint32_t)FLAG_NONE);
86+
static Error save(RequiredParam<Resource> p_resource, const String &p_path = "", uint32_t p_flags = (uint32_t)FLAG_NONE);
8787
static void get_recognized_extensions(const Ref<Resource> &p_resource, List<String> *p_extensions);
8888
static void add_resource_format_saver(Ref<ResourceFormatSaver> p_format_saver, bool p_at_front = false);
8989
static void remove_resource_format_saver(Ref<ResourceFormatSaver> p_format_saver);

core/object/object.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "core/templates/hash_set.h"
4040
#include "core/templates/list.h"
4141
#include "core/templates/safe_refcount.h"
42+
#include "core/variant/required_ptr.h"
4243
#include "core/variant/variant.h"
4344

4445
template <typename T>

core/variant/binder_common.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
#include "core/templates/simple_type.h"
3737
#include "core/typedefs.h"
3838
#include "core/variant/method_ptrcall.h"
39+
#include "core/variant/required_ptr.h"
3940
#include "core/variant/type_info.h"
4041
#include "core/variant/variant.h"
4142
#include "core/variant/variant_internal.h"

core/variant/method_ptrcall.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,42 @@ struct PtrToArg<const T *> {
269269
}
270270
};
271271

272+
// This is for RequiredParam.
273+
274+
template <class T>
275+
struct PtrToArg<RequiredParam<T>> {
276+
typedef typename RequiredParam<T>::ptr_type EncodeT;
277+
278+
_FORCE_INLINE_ static RequiredParam<T> convert(const void *p_ptr) {
279+
if (p_ptr == nullptr) {
280+
return RequiredParam<T>::_err_return_dont_use();
281+
}
282+
return RequiredParam<T>(*reinterpret_cast<T *const *>(p_ptr));
283+
}
284+
285+
_FORCE_INLINE_ static void encode(const RequiredParam<T> &p_var, void *p_ptr) {
286+
*((typename RequiredParam<T>::ptr_type *)p_ptr) = p_var._internal_ptr_dont_use();
287+
}
288+
};
289+
290+
// This is for RequiredResult.
291+
292+
template <class T>
293+
struct PtrToArg<RequiredResult<T>> {
294+
typedef typename RequiredResult<T>::ptr_type EncodeT;
295+
296+
_FORCE_INLINE_ static RequiredResult<T> convert(const void *p_ptr) {
297+
if (p_ptr == nullptr) {
298+
return RequiredResult<T>::_err_return_dont_use();
299+
}
300+
return RequiredResult<T>(*reinterpret_cast<T *const *>(p_ptr));
301+
}
302+
303+
_FORCE_INLINE_ static void encode(const RequiredResult<T> &p_var, void *p_ptr) {
304+
*((typename RequiredResult<T>::ptr_type *)p_ptr) = p_var._internal_ptr_dont_use();
305+
}
306+
};
307+
272308
// This is for ObjectID.
273309

274310
template <>

core/variant/required_ptr.h

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
/**************************************************************************/
2+
/* required_ptr.h */
3+
/**************************************************************************/
4+
/* This file is part of: */
5+
/* GODOT ENGINE */
6+
/* https://godotengine.org */
7+
/**************************************************************************/
8+
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9+
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10+
/* */
11+
/* Permission is hereby granted, free of charge, to any person obtaining */
12+
/* a copy of this software and associated documentation files (the */
13+
/* "Software"), to deal in the Software without restriction, including */
14+
/* without limitation the rights to use, copy, modify, merge, publish, */
15+
/* distribute, sublicense, and/or sell copies of the Software, and to */
16+
/* permit persons to whom the Software is furnished to do so, subject to */
17+
/* the following conditions: */
18+
/* */
19+
/* The above copyright notice and this permission notice shall be */
20+
/* included in all copies or substantial portions of the Software. */
21+
/* */
22+
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23+
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24+
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25+
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26+
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27+
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28+
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29+
/**************************************************************************/
30+
31+
#pragma once
32+
33+
#include "core/variant/variant.h"
34+
35+
template <typename T>
36+
class RequiredResult {
37+
static_assert(!is_fully_defined_v<T> || std::is_base_of_v<Object, T>, "T must be an Object subtype");
38+
39+
public:
40+
using element_type = T;
41+
using ptr_type = std::conditional_t<std::is_base_of_v<RefCounted, T>, Ref<T>, T *>;
42+
43+
private:
44+
ptr_type _value = ptr_type();
45+
46+
_FORCE_INLINE_ RequiredResult() = default;
47+
48+
public:
49+
RequiredResult(const RequiredResult &p_other) = default;
50+
RequiredResult(RequiredResult &&p_other) = default;
51+
RequiredResult &operator=(const RequiredResult &p_other) = default;
52+
RequiredResult &operator=(RequiredResult &&p_other) = default;
53+
54+
_FORCE_INLINE_ RequiredResult(std::nullptr_t) :
55+
RequiredResult() {}
56+
_FORCE_INLINE_ RequiredResult &operator=(std::nullptr_t) { _value = nullptr; }
57+
58+
// These functions should not be called directly, they are only for internal use.
59+
_FORCE_INLINE_ ptr_type _internal_ptr_dont_use() const { return _value; }
60+
_FORCE_INLINE_ static RequiredResult<T> _err_return_dont_use() { return RequiredResult<T>(); }
61+
62+
template <typename T_Other, std::enable_if_t<std::is_base_of_v<T, T_Other>, int> = 0>
63+
_FORCE_INLINE_ RequiredResult(const RequiredResult<T_Other> &p_other) :
64+
_value(p_other._value) {}
65+
template <typename T_Other, std::enable_if_t<std::is_base_of_v<T, T_Other>, int> = 0>
66+
_FORCE_INLINE_ RequiredResult &operator=(const RequiredResult<T_Other> &p_other) {
67+
_value = p_other._value;
68+
return *this;
69+
}
70+
71+
template <typename T_Other, std::enable_if_t<std::is_base_of_v<T, T_Other>, int> = 0>
72+
_FORCE_INLINE_ RequiredResult(T_Other *p_ptr) :
73+
_value(p_ptr) {}
74+
template <typename T_Other, std::enable_if_t<std::is_base_of_v<T, T_Other>, int> = 0>
75+
_FORCE_INLINE_ RequiredResult &operator=(T_Other *p_ptr) {
76+
_value = p_ptr;
77+
return *this;
78+
}
79+
80+
template <typename T_Other, std::enable_if_t<std::is_base_of_v<T, T_Other>, int> = 0>
81+
_FORCE_INLINE_ RequiredResult(const Ref<T_Other> &p_ref) :
82+
_value(p_ref) {}
83+
template <typename T_Other, std::enable_if_t<std::is_base_of_v<T, T_Other>, int> = 0>
84+
_FORCE_INLINE_ RequiredResult &operator=(const Ref<T_Other> &p_ref) {
85+
_value = p_ref;
86+
return *this;
87+
}
88+
89+
template <typename T_Other, std::enable_if_t<std::is_base_of_v<T, T_Other>, int> = 0>
90+
_FORCE_INLINE_ RequiredResult(Ref<T_Other> &&p_ref) :
91+
_value(std::move(p_ref)) {}
92+
template <typename T_Other, std::enable_if_t<std::is_base_of_v<T, T_Other>, int> = 0>
93+
_FORCE_INLINE_ RequiredResult &operator=(Ref<T_Other> &&p_ref) {
94+
_value = std::move(p_ref);
95+
return &this;
96+
}
97+
98+
template <typename U = T, std::enable_if_t<std::is_base_of_v<RefCounted, U>, int> = 0>
99+
_FORCE_INLINE_ RequiredResult(const Variant &p_variant) :
100+
_value(static_cast<T *>(p_variant.get_validated_object())) {}
101+
template <typename U = T, std::enable_if_t<std::is_base_of_v<RefCounted, U>, int> = 0>
102+
_FORCE_INLINE_ RequiredResult &operator=(const Variant &p_variant) {
103+
_value = static_cast<T *>(p_variant.get_validated_object());
104+
return *this;
105+
}
106+
107+
template <typename U = T, std::enable_if_t<!std::is_base_of_v<RefCounted, U>, int> = 0>
108+
_FORCE_INLINE_ RequiredResult(const Variant &p_variant) :
109+
_value(static_cast<T *>(p_variant.operator Object *())) {}
110+
template <typename U = T, std::enable_if_t<!std::is_base_of_v<RefCounted, U>, int> = 0>
111+
_FORCE_INLINE_ RequiredResult &operator=(const Variant &p_variant) {
112+
_value = static_cast<T *>(p_variant.operator Object *());
113+
return *this;
114+
}
115+
116+
template <typename U = T, std::enable_if_t<std::is_base_of_v<RefCounted, U>, int> = 0>
117+
_FORCE_INLINE_ element_type *ptr() const {
118+
return *_value;
119+
}
120+
121+
template <typename U = T, std::enable_if_t<!std::is_base_of_v<RefCounted, U>, int> = 0>
122+
_FORCE_INLINE_ element_type *ptr() const {
123+
return _value;
124+
}
125+
126+
_FORCE_INLINE_ operator ptr_type() {
127+
return _value;
128+
}
129+
130+
_FORCE_INLINE_ operator Variant() const {
131+
return Variant(_value);
132+
}
133+
134+
_FORCE_INLINE_ element_type *operator*() const {
135+
return ptr();
136+
}
137+
138+
_FORCE_INLINE_ element_type *operator->() const {
139+
return ptr();
140+
}
141+
};
142+
143+
template <typename T>
144+
class RequiredParam {
145+
static_assert(!is_fully_defined_v<T> || std::is_base_of_v<Object, T>, "T must be an Object subtype");
146+
147+
public:
148+
using element_type = T;
149+
using ptr_type = std::conditional_t<std::is_base_of_v<RefCounted, T>, Ref<T>, T *>;
150+
151+
private:
152+
ptr_type _value = ptr_type();
153+
154+
_FORCE_INLINE_ RequiredParam() = default;
155+
156+
public:
157+
// These functions should not be called directly, they are only for internal use.
158+
_FORCE_INLINE_ ptr_type _internal_ptr_dont_use() const { return _value; }
159+
_FORCE_INLINE_ bool _is_null_dont_use() const {
160+
if constexpr (std::is_base_of_v<RefCounted, T>) {
161+
return _value.is_null();
162+
} else {
163+
return _value == nullptr;
164+
}
165+
}
166+
_FORCE_INLINE_ static RequiredParam<T> _err_return_dont_use() { return RequiredParam<T>(); }
167+
168+
// Prevent erroneously assigning null values by explicitly removing nullptr constructor/assignment.
169+
RequiredParam(std::nullptr_t) = delete;
170+
RequiredParam &operator=(std::nullptr_t) = delete;
171+
172+
RequiredParam(const RequiredParam &p_other) = default;
173+
RequiredParam(RequiredParam &&p_other) = default;
174+
RequiredParam &operator=(const RequiredParam &p_other) = default;
175+
RequiredParam &operator=(RequiredParam &&p_other) = default;
176+
177+
template <typename T_Other, std::enable_if_t<std::is_base_of_v<T, T_Other>, int> = 0>
178+
_FORCE_INLINE_ RequiredParam(const RequiredParam<T_Other> &p_other) :
179+
_value(p_other._internal_ptr_dont_use()) {}
180+
template <typename T_Other, std::enable_if_t<std::is_base_of_v<T, T_Other>, int> = 0>
181+
_FORCE_INLINE_ RequiredParam &operator=(const RequiredParam<T_Other> &p_other) {
182+
_value = p_other._internal_ptr_dont_use();
183+
return *this;
184+
}
185+
186+
template <typename T_Other, std::enable_if_t<std::is_base_of_v<T, T_Other>, int> = 0>
187+
_FORCE_INLINE_ RequiredParam(T_Other *p_ptr) :
188+
_value(p_ptr) {}
189+
template <typename T_Other, std::enable_if_t<std::is_base_of_v<T, T_Other>, int> = 0>
190+
_FORCE_INLINE_ RequiredParam &operator=(T_Other *p_ptr) {
191+
_value = p_ptr;
192+
return *this;
193+
}
194+
195+
template <typename T_Other, std::enable_if_t<std::is_base_of_v<T, T_Other>, int> = 0>
196+
_FORCE_INLINE_ RequiredParam(const Ref<T_Other> &p_ref) :
197+
_value(p_ref) {}
198+
template <typename T_Other, std::enable_if_t<std::is_base_of_v<T, T_Other>, int> = 0>
199+
_FORCE_INLINE_ RequiredParam &operator=(const Ref<T_Other> &p_ref) {
200+
_value = p_ref;
201+
return *this;
202+
}
203+
204+
template <typename T_Other, std::enable_if_t<std::is_base_of_v<T, T_Other>, int> = 0>
205+
_FORCE_INLINE_ RequiredParam(Ref<T_Other> &&p_ref) :
206+
_value(std::move(p_ref)) {}
207+
template <typename T_Other, std::enable_if_t<std::is_base_of_v<T, T_Other>, int> = 0>
208+
_FORCE_INLINE_ RequiredParam &operator=(Ref<T_Other> &&p_ref) {
209+
_value = std::move(p_ref);
210+
return &this;
211+
}
212+
213+
template <typename U = T, std::enable_if_t<std::is_base_of_v<RefCounted, U>, int> = 0>
214+
_FORCE_INLINE_ RequiredParam(const Variant &p_variant) :
215+
_value(static_cast<T *>(p_variant.get_validated_object())) {}
216+
template <typename U = T, std::enable_if_t<std::is_base_of_v<RefCounted, U>, int> = 0>
217+
_FORCE_INLINE_ RequiredParam &operator=(const Variant &p_variant) {
218+
_value = static_cast<T *>(p_variant.get_validated_object());
219+
return *this;
220+
}
221+
222+
template <typename U = T, std::enable_if_t<!std::is_base_of_v<RefCounted, U>, int> = 0>
223+
_FORCE_INLINE_ RequiredParam(const Variant &p_variant) :
224+
_value(static_cast<T *>(p_variant.operator Object *())) {}
225+
template <typename U = T, std::enable_if_t<!std::is_base_of_v<RefCounted, U>, int> = 0>
226+
_FORCE_INLINE_ RequiredParam &operator=(const Variant &p_variant) {
227+
_value = static_cast<T *>(p_variant.operator Object *());
228+
return *this;
229+
}
230+
};
231+
232+
#define TMPL_EXTRACT_PARAM_OR_FAIL(m_name, m_param, m_retval, m_msg, m_editor) \
233+
if (unlikely(m_param._is_null_dont_use())) { \
234+
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Required object \"" _STR(m_param) "\" is null.", m_msg, m_editor); \
235+
return m_retval; \
236+
} \
237+
typename std::decay_t<decltype(m_param)>::ptr_type m_name = m_param._internal_ptr_dont_use(); \
238+
static_assert(true)
239+
240+
// These macros are equivalent to the ERR_FAIL_NULL*() family of macros, only for RequiredParam<T> instead of raw pointers.
241+
#define EXTRACT_PARAM_OR_FAIL(m_name, m_param) TMPL_EXTRACT_PARAM_OR_FAIL(m_name, m_param, void(), "", false)
242+
#define EXTRACT_PARAM_OR_FAIL_MSG(m_name, m_param, m_msg) TMPL_EXTRACT_PARAM_OR_FAIL(m_name, m_param, void(), m_msg, false)
243+
#define EXTRACT_PARAM_OR_FAIL_EDMSG(m_name, m_param, m_msg) TMPL_EXTRACT_PARAM_OR_FAIL(m_name, m_param, void(), m_msg, true)
244+
#define EXTRACT_PARAM_OR_FAIL_V(m_name, m_param, m_retval) TMPL_EXTRACT_PARAM_OR_FAIL(m_name, m_param, m_retval, "", false)
245+
#define EXTRACT_PARAM_OR_FAIL_V_MSG(m_name, m_param, m_retval, m_msg) TMPL_EXTRACT_PARAM_OR_FAIL(m_name, m_param, m_retval, m_msg, false)
246+
#define EXTRACT_PARAM_OR_FAIL_V_EDMSG(m_name, m_param, m_retval, m_msg) TMPL_EXTRACT_PARAM_OR_FAIL(m_name, m_param, m_retval, m_msg, true)

0 commit comments

Comments
 (0)