Skip to content

Commit ae96d1b

Browse files
committed
Add RequiredPtr<T> to mark Object * arguments and return values as required
1 parent e567f49 commit ae96d1b

File tree

10 files changed

+218
-4
lines changed

10 files changed

+218
-4
lines changed

core/error/error_macros.h

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,84 @@ void _physics_interpolation_warning(const char *p_function, const char *p_file,
391391
} else \
392392
((void)0)
393393

394+
// Required pointer error macros.
395+
396+
/**
397+
* Try using `ERR_FAIL_REQUIRED_PTR_MSG`.
398+
* Only use this macro if there is no sensible error message.
399+
*
400+
* Ensures a pointer `m_param` is not null and assigns to a local variable named `m_name`.
401+
* If it is null, the current function returns.
402+
*/
403+
#define ERR_FAIL_REQUIRED_PTR(m_name, m_param) \
404+
std::remove_reference_t<decltype(m_param)>::type m_name = m_param._internal_ptr(); \
405+
if (unlikely(m_name == nullptr)) { \
406+
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null."); \
407+
return; \
408+
} else \
409+
((void)0)
410+
411+
/**
412+
* Ensures a pointer `m_param` is not null and assigns to a local variable named `m_name`.
413+
* If it is null, prints `m_msg` and the current function returns.
414+
*/
415+
#define ERR_FAIL_REQUIRED_PTR_MSG(m_name, m_param, m_msg) \
416+
std::remove_reference_t<decltype(m_param)>::type m_name = m_param._internal_ptr(); \
417+
if (unlikely(m_name == nullptr) { \
418+
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg); \
419+
return; \
420+
} else \
421+
((void)0)
422+
423+
/**
424+
* Same as `ERR_FAIL_REQUIRED_PTR_MSG` but also notifies the editor.
425+
*/
426+
#define ERR_FAIL_REQUIRED_PTR_EDMSG(m_name, m_param, m_msg) \
427+
std::remove_reference_t<decltype(m_param)>::type m_name = m_param._internal_ptr(); \
428+
if (unlikely(m_name == nullptr) { \
429+
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg, true); \
430+
return; \
431+
} else \
432+
((void)0)
433+
434+
/**
435+
* Try using `ERR_FAIL_REQUIRED_PTR_V_MSG`.
436+
* Only use this macro if there is no sensible error message.
437+
*
438+
* Ensures a pointer `m_param` is not null and assigns a local variable named `m_name`.
439+
* If it is null, the current function returns `m_retval`.
440+
*/
441+
#define ERR_FAIL_REQUIRED_PTR_V(m_name, m_param, m_retval) \
442+
std::remove_reference_t<decltype(m_param)>::type m_name = m_param._internal_ptr(); \
443+
if (unlikely(m_name == nullptr) { \
444+
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null."); \
445+
return m_retval; \
446+
} else \
447+
((void)0)
448+
449+
/**
450+
* Ensures a pointer `m_param` is not null and assigns a local variable named `m_name`.
451+
* If it is null, prints `m_msg` and the current function returns `m_retval`.
452+
*/
453+
#define ERR_FAIL_REQUIRED_PTR_V_MSG(m_name, m_param, m_retval, m_msg) \
454+
std::remove_reference_t<decltype(m_param)>::type m_name = m_param._internal_ptr(); \
455+
if (unlikely(m_name == nullptr) { \
456+
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg); \
457+
return m_retval; \
458+
} else \
459+
((void)0)
460+
461+
/**
462+
* Same as `ERR_FAIL_REQUIRED_PTR_V_MSG` but also notifies the editor.
463+
*/
464+
#define ERR_FAIL_REQUIRED_PTR_V_EDMSG(m_name, m_param, m_retval, m_msg) \
465+
std::remove_reference_t<decltype(m_param)>::type m_name = m_param._internal_ptr(); \
466+
if (unlikely(m_name == nullptr)) { \
467+
_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Parameter \"" _STR(m_param) "\" is null.", m_msg, true); \
468+
return m_retval; \
469+
} else \
470+
((void)0)
471+
394472
/**
395473
* Try using `ERR_FAIL_COND_MSG`.
396474
* Only use this macro if there is no sensible error message.

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/object/object.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include "core/templates/rb_map.h"
4343
#include "core/templates/safe_refcount.h"
4444
#include "core/variant/callable_bind.h"
45+
#include "core/variant/required_ptr.h"
4546
#include "core/variant/variant.h"
4647

4748
template <typename T>

core/variant/binder_common.h

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

core/variant/method_ptrcall.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,38 @@ struct PtrToArg<const T *> {
179179
}
180180
};
181181

182+
// This is for RequiredPtr.
183+
184+
template <class T>
185+
struct PtrToArg<RequiredPtr<T>> {
186+
typedef T *EncodeT;
187+
188+
_FORCE_INLINE_ static T *convert(const void *p_ptr) {
189+
if (p_ptr == nullptr) {
190+
// Should we show an error?
191+
return RequiredPtr<T>(nullptr);
192+
}
193+
return RequiredPtr<T>(*reinterpret_cast<T *const *>(p_ptr));
194+
}
195+
196+
_FORCE_INLINE_ static void encode(RequiredPtr<T> p_var, void *p_ptr) {
197+
*((T **)p_ptr) = p_var._internal_ptr();
198+
}
199+
};
200+
201+
template <class T>
202+
struct PtrToArg<const RequiredPtr<T> &> {
203+
typedef T *EncodeT;
204+
205+
_FORCE_INLINE_ static RequiredPtr<T> convert(const void *p_ptr) {
206+
if (p_ptr == nullptr) {
207+
// Should we show an error?
208+
return RequiredPtr<T>(nullptr);
209+
}
210+
return RequiredPtr<T>(*reinterpret_cast<T *const *>(p_ptr));
211+
}
212+
};
213+
182214
// This is for ObjectID.
183215

184216
template <>

core/variant/required_ptr.h

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
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+
#ifndef REQUIRED_PTR_H
32+
#define REQUIRED_PTR_H
33+
34+
#include "core/os/memory.h"
35+
36+
template <class T>
37+
class RequiredPtr {
38+
T *value = nullptr;
39+
40+
public:
41+
typedef T *type;
42+
43+
_FORCE_INLINE_ RequiredPtr(T *p_value) {
44+
value = p_value;
45+
}
46+
47+
_FORCE_INLINE_ RequiredPtr(const RequiredPtr &p_other) {
48+
value = p_other.value;
49+
}
50+
51+
_FORCE_INLINE_ RequiredPtr<T> &operator=(const RequiredPtr &p_other) {
52+
value = p_other.value;
53+
}
54+
55+
_FORCE_INLINE_ RequiredPtr(const Variant &p_value) :
56+
RequiredPtr(dynamic_cast<T *>((Object *)p_value)) {}
57+
58+
//
59+
// DO NOT CALL THIS DIRECTLY!
60+
//
61+
// You should use the ERR_FAIL_REQUIRED_PTR*() macros to get the pointer value.
62+
//
63+
_FORCE_INLINE_ T *_internal_ptr() const {
64+
return value;
65+
}
66+
};
67+
68+
#endif // REQUIRED_PTR_H

core/variant/type_info.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ enum Metadata {
5050
METADATA_REAL_IS_DOUBLE,
5151
METADATA_INT_IS_CHAR16,
5252
METADATA_INT_IS_CHAR32,
53+
METADATA_OBJECT_IS_REQUIRED,
5354
};
5455
}
5556

@@ -210,6 +211,27 @@ struct GetTypeInfo<T *, std::enable_if_t<std::is_base_of_v<Object, T>>> {
210211
}
211212
};
212213

214+
template <class T>
215+
class RequiredPtr;
216+
217+
template <typename T>
218+
struct GetTypeInfo<RequiredPtr<T>, std::enable_if_t<std::is_base_of_v<Object, T>>> {
219+
static const Variant::Type VARIANT_TYPE = Variant::OBJECT;
220+
static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_OBJECT_IS_REQUIRED;
221+
static inline PropertyInfo get_class_info() {
222+
return PropertyInfo(StringName(T::get_class_static()));
223+
}
224+
};
225+
226+
template <typename T>
227+
struct GetTypeInfo<const RequiredPtr<T> &, std::enable_if_t<std::is_base_of_v<Object, T>>> {
228+
static const Variant::Type VARIANT_TYPE = Variant::OBJECT;
229+
static const GodotTypeInfo::Metadata METADATA = GodotTypeInfo::METADATA_OBJECT_IS_REQUIRED;
230+
static inline PropertyInfo get_class_info() {
231+
return PropertyInfo(StringName(T::get_class_static()));
232+
}
233+
};
234+
213235
namespace godot {
214236
namespace details {
215237
inline String enum_qualified_name_to_class_info_name(const String &p_qualified_name) {

core/variant/variant_internal.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1097,6 +1097,18 @@ struct VariantInternalAccessor<Object *> {
10971097
static _FORCE_INLINE_ void set(Variant *v, const Object *p_value) { VariantInternal::object_assign(v, p_value); }
10981098
};
10991099

1100+
template <class T>
1101+
struct VariantInternalAccessor<RequiredPtr<T>> {
1102+
static _FORCE_INLINE_ RequiredPtr<T> get(const Variant *v) { return RequiredPtr<T>(Object::cast_to<T>(const_cast<Object *>(*VariantInternal::get_object(v)))); }
1103+
static _FORCE_INLINE_ void set(Variant *v, const RequiredPtr<T> &p_value) { VariantInternal::object_assign(v, p_value.ptr()); }
1104+
};
1105+
1106+
template <class T>
1107+
struct VariantInternalAccessor<const RequiredPtr<T> &> {
1108+
static _FORCE_INLINE_ RequiredPtr<T> get(const Variant *v) { return RequiredPtr<T>(Object::cast_to<T>(*VariantInternal::get_object(v))); }
1109+
static _FORCE_INLINE_ void set(Variant *v, const RequiredPtr<T> &p_value) { VariantInternal::object_assign(v, p_value.ptr()); }
1110+
};
1111+
11001112
template <>
11011113
struct VariantInternalAccessor<Variant> {
11021114
static _FORCE_INLINE_ Variant &get(Variant *v) { return *v; }

scene/main/node.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1644,11 +1644,11 @@ void Node::_add_child_nocheck(Node *p_child, const StringName &p_name, InternalM
16441644
emit_signal(SNAME("child_order_changed"));
16451645
}
16461646

1647-
void Node::add_child(Node *p_child, bool p_force_readable_name, InternalMode p_internal) {
1647+
void Node::add_child(const RequiredPtr<Node> &rp_child, bool p_force_readable_name, InternalMode p_internal) {
16481648
ERR_FAIL_COND_MSG(data.inside_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).");
16491649

16501650
ERR_THREAD_GUARD
1651-
ERR_FAIL_NULL(p_child);
1651+
ERR_FAIL_REQUIRED_PTR(p_child, rp_child);
16521652
ERR_FAIL_COND_MSG(p_child == this, vformat("Can't add child '%s' to itself.", p_child->get_name())); // adding to itself!
16531653
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
16541654
#ifdef DEBUG_ENABLED

scene/main/node.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -459,7 +459,7 @@ class Node : public Object {
459459

460460
InternalMode get_internal_mode() const;
461461

462-
void add_child(Node *p_child, bool p_force_readable_name = false, InternalMode p_internal = INTERNAL_MODE_DISABLED);
462+
void add_child(const RequiredPtr<Node> &rp_child, bool p_force_readable_name = false, InternalMode p_internal = INTERNAL_MODE_DISABLED);
463463
void add_sibling(Node *p_sibling, bool p_force_readable_name = false);
464464
void remove_child(Node *p_child);
465465

0 commit comments

Comments
 (0)