From 37248d7c87cc4e7350cb29159f5c22a602d489e7 Mon Sep 17 00:00:00 2001 From: Adam Scott Date: Fri, 20 Jan 2023 14:36:52 -0500 Subject: [PATCH] Add `@GlobalScope` `is_same(a, b)` and `Variant::identity_compare()` --- core/variant/variant.cpp | 40 +++++++ core/variant/variant.h | 1 + core/variant/variant_utility.cpp | 7 ++ doc/classes/@GlobalScope.xml | 25 ++++ tests/core/variant/test_variant.h | 190 ++++++++++++++++++++++++++++++ 5 files changed, 263 insertions(+) diff --git a/core/variant/variant.cpp b/core/variant/variant.cpp index ca42738b05d..672b0308064 100644 --- a/core/variant/variant.cpp +++ b/core/variant/variant.cpp @@ -3492,6 +3492,46 @@ bool Variant::hash_compare(const Variant &p_variant, int recursion_count) const } } +bool Variant::identity_compare(const Variant &p_variant) const { + if (type != p_variant.type) { + return false; + } + + switch (type) { + case OBJECT: { + return _get_obj().obj == p_variant._get_obj().obj; + } break; + + case DICTIONARY: { + const Dictionary &l = *(reinterpret_cast(_data._mem)); + const Dictionary &r = *(reinterpret_cast(p_variant._data._mem)); + return l.id() == r.id(); + } break; + + case ARRAY: { + const Array &l = *(reinterpret_cast(_data._mem)); + const Array &r = *(reinterpret_cast(p_variant._data._mem)); + return l.id() == r.id(); + } break; + + case PACKED_BYTE_ARRAY: + case PACKED_INT32_ARRAY: + case PACKED_INT64_ARRAY: + case PACKED_FLOAT32_ARRAY: + case PACKED_FLOAT64_ARRAY: + case PACKED_STRING_ARRAY: + case PACKED_VECTOR2_ARRAY: + case PACKED_VECTOR3_ARRAY: + case PACKED_COLOR_ARRAY: { + return _data.packed_array == p_variant._data.packed_array; + } break; + + default: { + return hash_compare(p_variant); + } + } +} + bool StringLikeVariantComparator::compare(const Variant &p_lhs, const Variant &p_rhs) { if (p_lhs.hash_compare(p_rhs)) { return true; diff --git a/core/variant/variant.h b/core/variant/variant.h index b9294de77d9..b2f31a6d57b 100644 --- a/core/variant/variant.h +++ b/core/variant/variant.h @@ -748,6 +748,7 @@ public: uint32_t recursive_hash(int recursion_count) const; bool hash_compare(const Variant &p_variant, int recursion_count = 0) const; + bool identity_compare(const Variant &p_variant) const; bool booleanize() const; String stringify(int recursion_count = 0) const; String to_json_string() const; diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp index fe7150bca98..042ebe368af 100644 --- a/core/variant/variant_utility.cpp +++ b/core/variant/variant_utility.cpp @@ -1007,9 +1007,14 @@ struct VariantUtilityFunctions { static inline uint64_t rid_allocate_id() { return RID_AllocBase::_gen_id(); } + static inline RID rid_from_int64(uint64_t p_base) { return RID::from_uint64(p_base); } + + static inline bool is_same(const Variant &p_a, const Variant &p_b) { + return p_a.identity_compare(p_b); + } }; #ifdef DEBUG_METHODS_ENABLED @@ -1601,6 +1606,8 @@ void Variant::_register_variant_utility_functions() { FUNCBINDR(rid_allocate_id, Vector(), Variant::UTILITY_FUNC_TYPE_GENERAL); FUNCBINDR(rid_from_int64, sarray("base"), Variant::UTILITY_FUNC_TYPE_GENERAL); + + FUNCBINDR(is_same, sarray("a", "b"), Variant::UTILITY_FUNC_TYPE_GENERAL); } void Variant::_unregister_variant_utility_functions() { diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index 2be15d51000..a859f36f3e4 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -525,6 +525,31 @@ Returns [code]true[/code] if [param x] is a NaN ("Not a Number" or invalid) value. + + + + + + Returns [code]true[/code], for value types, if [param a] and [param b] share the same value. Returns [code]true[/code], for reference types, if the references of [param a] and [param b] are the same. + [codeblock] + # Vector2 is a value type + var vec2_a = Vector2(0, 0) + var vec2_b = Vector2(0, 0) + var vec2_c = Vector2(1, 1) + is_same(vec2_a, vec2_a) # true + is_same(vec2_a, vec2_b) # true + is_same(vec2_a, vec2_c) # false + + # Array is a reference type + var arr_a = [] + var arr_b = [] + is_same(arr_a, arr_a) # true + is_same(arr_a, arr_b) # false + [/codeblock] + These are [Variant] value types: [code]null[/code], [bool], [int], [float], [String], [StringName], [Vector2], [Vector2i], [Vector3], [Vector3i], [Vector4], [Vector4i], [Rect2], [Rect2i], [Transform2D], [Transform3D], [Plane], [Quaternion], [AABB], [Basis], [Projection], [Color], [NodePath], [RID], [Callable] and [Signal]. + These are [Variant] reference types: [Object], [Dictionary], [Array], [PackedByteArray], [PackedInt32Array], [PackedInt64Array], [PackedFloat32Array], [PackedFloat64Array], [PackedStringArray], [PackedVector2Array], [PackedVector3Array] and [PackedColorArray]. + + diff --git a/tests/core/variant/test_variant.h b/tests/core/variant/test_variant.h index 85b53264f27..dfbaa36af8f 100644 --- a/tests/core/variant/test_variant.h +++ b/tests/core/variant/test_variant.h @@ -868,6 +868,196 @@ TEST_CASE("[Variant] Basic comparison") { CHECK_NE(Variant(Dictionary()), Variant()); } +TEST_CASE("[Variant] Identity comparison") { + // Value types are compared by value + Variant aabb = AABB(); + CHECK(aabb.identity_compare(aabb)); + CHECK(aabb.identity_compare(AABB())); + CHECK_FALSE(aabb.identity_compare(AABB(Vector3(1, 2, 3), Vector3(1, 2, 3)))); + + Variant basis = Basis(); + CHECK(basis.identity_compare(basis)); + CHECK(basis.identity_compare(Basis())); + CHECK_FALSE(basis.identity_compare(Basis(Quaternion(Vector3(1, 2, 3).normalized(), 45)))); + + Variant bool_var = true; + CHECK(bool_var.identity_compare(bool_var)); + CHECK(bool_var.identity_compare(true)); + CHECK_FALSE(bool_var.identity_compare(false)); + + Variant callable = Callable(); + CHECK(callable.identity_compare(callable)); + CHECK(callable.identity_compare(Callable())); + CHECK_FALSE(callable.identity_compare(Callable(ObjectID(), StringName("lambda")))); + + Variant color = Color(); + CHECK(color.identity_compare(color)); + CHECK(color.identity_compare(Color())); + CHECK_FALSE(color.identity_compare(Color(255, 0, 255))); + + Variant float_var = 1.0; + CHECK(float_var.identity_compare(float_var)); + CHECK(float_var.identity_compare(1.0)); + CHECK_FALSE(float_var.identity_compare(2.0)); + + Variant int_var = 1; + CHECK(int_var.identity_compare(int_var)); + CHECK(int_var.identity_compare(1)); + CHECK_FALSE(int_var.identity_compare(2)); + + Variant nil = Variant(); + CHECK(nil.identity_compare(nil)); + CHECK(nil.identity_compare(Variant())); + CHECK_FALSE(nil.identity_compare(true)); + + Variant node_path = NodePath("godot"); + CHECK(node_path.identity_compare(node_path)); + CHECK(node_path.identity_compare(NodePath("godot"))); + CHECK_FALSE(node_path.identity_compare(NodePath("waiting"))); + + Variant plane = Plane(); + CHECK(plane.identity_compare(plane)); + CHECK(plane.identity_compare(Plane())); + CHECK_FALSE(plane.identity_compare(Plane(Vector3(1, 2, 3), 42))); + + Variant projection = Projection(); + CHECK(projection.identity_compare(projection)); + CHECK(projection.identity_compare(Projection())); + CHECK_FALSE(projection.identity_compare(Projection(Transform3D(Basis(Vector3(1, 2, 3).normalized(), 45), Vector3(1, 2, 3))))); + + Variant quaternion = Quaternion(); + CHECK(quaternion.identity_compare(quaternion)); + CHECK(quaternion.identity_compare(Quaternion())); + CHECK_FALSE(quaternion.identity_compare(Quaternion(Vector3(1, 2, 3).normalized(), 45))); + + Variant rect2 = Rect2(); + CHECK(rect2.identity_compare(rect2)); + CHECK(rect2.identity_compare(Rect2())); + CHECK_FALSE(rect2.identity_compare(Rect2(Point2(Vector2(1, 2)), Size2(Vector2(1, 2))))); + + Variant rect2i = Rect2i(); + CHECK(rect2i.identity_compare(rect2i)); + CHECK(rect2i.identity_compare(Rect2i())); + CHECK_FALSE(rect2i.identity_compare(Rect2i(Point2i(Vector2i(1, 2)), Size2i(Vector2i(1, 2))))); + + Variant rid = RID(); + CHECK(rid.identity_compare(rid)); + CHECK(rid.identity_compare(RID())); + CHECK_FALSE(rid.identity_compare(RID::from_uint64(123))); + + Variant signal = Signal(); + CHECK(signal.identity_compare(signal)); + CHECK(signal.identity_compare(Signal())); + CHECK_FALSE(signal.identity_compare(Signal(ObjectID(), StringName("lambda")))); + + Variant str = "godot"; + CHECK(str.identity_compare(str)); + CHECK(str.identity_compare("godot")); + CHECK_FALSE(str.identity_compare("waiting")); + + Variant str_name = StringName("godot"); + CHECK(str_name.identity_compare(str_name)); + CHECK(str_name.identity_compare(StringName("godot"))); + CHECK_FALSE(str_name.identity_compare(StringName("waiting"))); + + Variant transform2d = Transform2D(); + CHECK(transform2d.identity_compare(transform2d)); + CHECK(transform2d.identity_compare(Transform2D())); + CHECK_FALSE(transform2d.identity_compare(Transform2D(45, Vector2(1, 2)))); + + Variant transform3d = Transform3D(); + CHECK(transform3d.identity_compare(transform3d)); + CHECK(transform3d.identity_compare(Transform3D())); + CHECK_FALSE(transform3d.identity_compare(Transform3D(Basis(Quaternion(Vector3(1, 2, 3).normalized(), 45)), Vector3(1, 2, 3)))); + + Variant vect2 = Vector2(); + CHECK(vect2.identity_compare(vect2)); + CHECK(vect2.identity_compare(Vector2())); + CHECK_FALSE(vect2.identity_compare(Vector2(1, 2))); + + Variant vect2i = Vector2i(); + CHECK(vect2i.identity_compare(vect2i)); + CHECK(vect2i.identity_compare(Vector2i())); + CHECK_FALSE(vect2i.identity_compare(Vector2i(1, 2))); + + Variant vect3 = Vector3(); + CHECK(vect3.identity_compare(vect3)); + CHECK(vect3.identity_compare(Vector3())); + CHECK_FALSE(vect3.identity_compare(Vector3(1, 2, 3))); + + Variant vect3i = Vector3i(); + CHECK(vect3i.identity_compare(vect3i)); + CHECK(vect3i.identity_compare(Vector3i())); + CHECK_FALSE(vect3i.identity_compare(Vector3i(1, 2, 3))); + + Variant vect4 = Vector4(); + CHECK(vect4.identity_compare(vect4)); + CHECK(vect4.identity_compare(Vector4())); + CHECK_FALSE(vect4.identity_compare(Vector4(1, 2, 3, 4))); + + Variant vect4i = Vector4i(); + CHECK(vect4i.identity_compare(vect4i)); + CHECK(vect4i.identity_compare(Vector4i())); + CHECK_FALSE(vect4i.identity_compare(Vector4i(1, 2, 3, 4))); + + // Reference types are compared by reference + Variant array = Array(); + CHECK(array.identity_compare(array)); + CHECK_FALSE(array.identity_compare(Array())); + + Variant dictionary = Dictionary(); + CHECK(dictionary.identity_compare(dictionary)); + CHECK_FALSE(dictionary.identity_compare(Dictionary())); + + Variant packed_byte_array = PackedByteArray(); + CHECK(packed_byte_array.identity_compare(packed_byte_array)); + CHECK_FALSE(packed_byte_array.identity_compare(PackedByteArray())); + + Variant packed_color_array = PackedColorArray(); + CHECK(packed_color_array.identity_compare(packed_color_array)); + CHECK_FALSE(packed_color_array.identity_compare(PackedColorArray())); + + Variant packed_float32_array = PackedFloat32Array(); + CHECK(packed_float32_array.identity_compare(packed_float32_array)); + CHECK_FALSE(packed_float32_array.identity_compare(PackedFloat32Array())); + + Variant packed_float64_array = PackedFloat64Array(); + CHECK(packed_float64_array.identity_compare(packed_float64_array)); + CHECK_FALSE(packed_float64_array.identity_compare(PackedFloat64Array())); + + Variant packed_int32_array = PackedInt32Array(); + CHECK(packed_int32_array.identity_compare(packed_int32_array)); + CHECK_FALSE(packed_int32_array.identity_compare(PackedInt32Array())); + + Variant packed_int64_array = PackedInt64Array(); + CHECK(packed_int64_array.identity_compare(packed_int64_array)); + CHECK_FALSE(packed_int64_array.identity_compare(PackedInt64Array())); + + Variant packed_string_array = PackedStringArray(); + CHECK(packed_string_array.identity_compare(packed_string_array)); + CHECK_FALSE(packed_string_array.identity_compare(PackedStringArray())); + + Variant packed_vector2_array = PackedVector2Array(); + CHECK(packed_vector2_array.identity_compare(packed_vector2_array)); + CHECK_FALSE(packed_vector2_array.identity_compare(PackedVector2Array())); + + Variant packed_vector3_array = PackedVector3Array(); + CHECK(packed_vector3_array.identity_compare(packed_vector3_array)); + CHECK_FALSE(packed_vector3_array.identity_compare(PackedVector3Array())); + + Object obj_one = Object(); + Variant obj_one_var = &obj_one; + Object obj_two = Object(); + Variant obj_two_var = &obj_two; + CHECK(obj_one_var.identity_compare(obj_one_var)); + CHECK_FALSE(obj_one_var.identity_compare(obj_two_var)); + + Variant obj_null_one_var = Variant((Object *)nullptr); + Variant obj_null_two_var = Variant((Object *)nullptr); + CHECK(obj_null_one_var.identity_compare(obj_null_one_var)); + CHECK(obj_null_one_var.identity_compare(obj_null_two_var)); +} + TEST_CASE("[Variant] Nested array comparison") { Array a1 = build_array(1, build_array(2, 3)); Array a2 = build_array(1, build_array(2, 3));