From 6f7eccc75813c68b2d80cc5f62d0688820e7a068 Mon Sep 17 00:00:00 2001 From: Aaron Franke Date: Wed, 14 Dec 2022 15:16:43 -0600 Subject: [PATCH 1/2] Add a type conversion method to Variant Utility and expose to scripting --- core/variant/variant_utility.cpp | 85 +++++++++++++++++++ doc/classes/@GlobalScope.xml | 17 ++++ modules/gdscript/doc_classes/@GDScript.xml | 3 +- .../gdscript/gdscript_utility_functions.cpp | 4 + 4 files changed, 108 insertions(+), 1 deletion(-) diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp index b51df89becdf..51ea17e2d207 100644 --- a/core/variant/variant_utility.cpp +++ b/core/variant/variant_utility.cpp @@ -752,6 +752,90 @@ int64_t VariantUtilityFunctions::_typeof(const Variant &obj) { return obj.get_type(); } +Variant VariantUtilityFunctions::type_convert(const Variant &p_variant, const Variant::Type p_type) { + switch (p_type) { + case Variant::Type::NIL: + return Variant(); + case Variant::Type::BOOL: + return p_variant.operator bool(); + case Variant::Type::INT: + return p_variant.operator int64_t(); + case Variant::Type::FLOAT: + return p_variant.operator double(); + case Variant::Type::STRING: + return p_variant.operator String(); + case Variant::Type::VECTOR2: + return p_variant.operator Vector2(); + case Variant::Type::VECTOR2I: + return p_variant.operator Vector2i(); + case Variant::Type::RECT2: + return p_variant.operator Rect2(); + case Variant::Type::RECT2I: + return p_variant.operator Rect2i(); + case Variant::Type::VECTOR3: + return p_variant.operator Vector3(); + case Variant::Type::VECTOR3I: + return p_variant.operator Vector3i(); + case Variant::Type::TRANSFORM2D: + return p_variant.operator Transform2D(); + case Variant::Type::VECTOR4: + return p_variant.operator Vector4(); + case Variant::Type::VECTOR4I: + return p_variant.operator Vector4i(); + case Variant::Type::PLANE: + return p_variant.operator Plane(); + case Variant::Type::QUATERNION: + return p_variant.operator Quaternion(); + case Variant::Type::AABB: + return p_variant.operator ::AABB(); + case Variant::Type::BASIS: + return p_variant.operator Basis(); + case Variant::Type::TRANSFORM3D: + return p_variant.operator Transform3D(); + case Variant::Type::PROJECTION: + return p_variant.operator Projection(); + case Variant::Type::COLOR: + return p_variant.operator Color(); + case Variant::Type::STRING_NAME: + return p_variant.operator StringName(); + case Variant::Type::NODE_PATH: + return p_variant.operator NodePath(); + case Variant::Type::RID: + return p_variant.operator ::RID(); + case Variant::Type::OBJECT: + return p_variant.operator Object *(); + case Variant::Type::CALLABLE: + return p_variant.operator Callable(); + case Variant::Type::SIGNAL: + return p_variant.operator Signal(); + case Variant::Type::DICTIONARY: + return p_variant.operator Dictionary(); + case Variant::Type::ARRAY: + return p_variant.operator Array(); + case Variant::Type::PACKED_BYTE_ARRAY: + return p_variant.operator PackedByteArray(); + case Variant::Type::PACKED_INT32_ARRAY: + return p_variant.operator PackedInt32Array(); + case Variant::Type::PACKED_INT64_ARRAY: + return p_variant.operator PackedInt64Array(); + case Variant::Type::PACKED_FLOAT32_ARRAY: + return p_variant.operator PackedFloat32Array(); + case Variant::Type::PACKED_FLOAT64_ARRAY: + return p_variant.operator PackedFloat64Array(); + case Variant::Type::PACKED_STRING_ARRAY: + return p_variant.operator PackedStringArray(); + case Variant::Type::PACKED_VECTOR2_ARRAY: + return p_variant.operator PackedVector2Array(); + case Variant::Type::PACKED_VECTOR3_ARRAY: + return p_variant.operator PackedVector3Array(); + case Variant::Type::PACKED_COLOR_ARRAY: + return p_variant.operator PackedColorArray(); + case Variant::Type::VARIANT_MAX: + ERR_PRINT("Invalid type argument to type_convert(), use the TYPE_* constants. Returning the unconverted Variant."); + } + return p_variant; +} + String VariantUtilityFunctions::str(const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { if (p_arg_count < 1) { r_error.error = Callable::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; @@ -1615,6 +1699,7 @@ void Variant::_register_variant_utility_functions() { FUNCBINDVR(weakref, sarray("obj"), Variant::UTILITY_FUNC_TYPE_GENERAL); FUNCBINDR(_typeof, sarray("variable"), Variant::UTILITY_FUNC_TYPE_GENERAL); + FUNCBINDR(type_convert, sarray("variant", "type"), Variant::UTILITY_FUNC_TYPE_GENERAL); FUNCBINDVARARGS(str, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); FUNCBINDR(error_string, sarray("error"), Variant::UTILITY_FUNC_TYPE_GENERAL); FUNCBINDVARARGV(print, sarray(), Variant::UTILITY_FUNC_TYPE_GENERAL); diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index fa3f6e434e23..3173e46be8d5 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -1364,6 +1364,23 @@ [/codeblock] + + + + + + Converts the given [param variant] to the given [param type], using the [enum Variant.Type] values. This method is generous with how it handles types, it can automatically convert between array types, convert numeric [String]s to [int], and converting most things to [String]. + If the type conversion cannot be done, this method will return the default value for that type, for example converting [Rect2] to [Vector2] will always return [code]Vector2.ZERO[/code]. This method will never show error messages as long as [param type] is a valid Variant type. + The returned value is a [Variant], but the data inside and the [enum Variant.Type] will be the same as the requested type. + [codeblock] + type_convert("Hi!", TYPE_INT) # Returns 0 + type_convert("123", TYPE_INT) # Returns 123 + type_convert(123.4, TYPE_INT) # Returns 123 + type_convert(5, TYPE_VECTOR2) # Returns (0, 0) + type_convert("Hi!", TYPE_NIL) # Returns null + [/codeblock] + + diff --git a/modules/gdscript/doc_classes/@GDScript.xml b/modules/gdscript/doc_classes/@GDScript.xml index 4f1a256ec994..18045e323ca7 100644 --- a/modules/gdscript/doc_classes/@GDScript.xml +++ b/modules/gdscript/doc_classes/@GDScript.xml @@ -57,11 +57,12 @@ [/codeblock] - + + [i]Deprecated.[/i] Use [method @GlobalScope.type_convert] instead. Converts [param what] to [param type] in the best way possible. The [param type] uses the [enum Variant.Type] values. [codeblock] var a = [4, 2.5, 1.2] diff --git a/modules/gdscript/gdscript_utility_functions.cpp b/modules/gdscript/gdscript_utility_functions.cpp index 030950267dee..cf4342c1e2ff 100644 --- a/modules/gdscript/gdscript_utility_functions.cpp +++ b/modules/gdscript/gdscript_utility_functions.cpp @@ -85,6 +85,7 @@ #endif struct GDScriptUtilityFunctionsDefinitions { +#ifndef DISABLE_DEPRECATED static inline void convert(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { VALIDATE_ARG_COUNT(2); VALIDATE_ARG_INT(1); @@ -100,6 +101,7 @@ struct GDScriptUtilityFunctionsDefinitions { Variant::construct(Variant::Type(type), *r_ret, p_args, 1, r_error); } } +#endif // DISABLE_DEPRECATED static inline void type_exists(Variant *r_ret, const Variant **p_args, int p_arg_count, Callable::CallError &r_error) { VALIDATE_ARG_COUNT(1); @@ -703,7 +705,9 @@ static void _register_function(const String &p_name, const MethodInfo &p_method_ PropertyInfo(Variant::NIL, m_name, PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_NIL_IS_VARIANT) void GDScriptUtilityFunctions::register_functions() { +#ifndef DISABLE_DEPRECATED REGISTER_VARIANT_FUNC(convert, true, VARARG("what"), ARG("type", Variant::INT)); +#endif // DISABLE_DEPRECATED REGISTER_FUNC(type_exists, true, Variant::BOOL, ARG("type", Variant::STRING_NAME)); REGISTER_FUNC(_char, true, Variant::STRING, ARG("char", Variant::INT)); REGISTER_VARARG_FUNC(range, false, Variant::ARRAY); From 22e26967676ae9c85ebabba7e0349c04d9252545 Mon Sep 17 00:00:00 2001 From: Aaron Franke Date: Wed, 14 Dec 2022 15:16:43 -0600 Subject: [PATCH 2/2] Add unit tests for type_convert Variant utility function --- tests/core/variant/test_variant_utility.h | 141 ++++++++++++++++++++++ tests/test_main.cpp | 1 + 2 files changed, 142 insertions(+) create mode 100644 tests/core/variant/test_variant_utility.h diff --git a/tests/core/variant/test_variant_utility.h b/tests/core/variant/test_variant_utility.h new file mode 100644 index 000000000000..93458b63f4c9 --- /dev/null +++ b/tests/core/variant/test_variant_utility.h @@ -0,0 +1,141 @@ +/**************************************************************************/ +/* test_variant_utility.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. */ +/**************************************************************************/ + +#ifndef TEST_VARIANT_UTILITY_H +#define TEST_VARIANT_UTILITY_H + +#include "core/variant/variant_utility.h" + +#include "tests/test_macros.h" + +namespace TestVariantUtility { + +TEST_CASE("[VariantUtility] Type conversion") { + Variant converted; + converted = VariantUtilityFunctions::type_convert("Hi!", Variant::Type::NIL); + CHECK(converted.get_type() == Variant::Type::NIL); + CHECK(converted == Variant()); + + converted = VariantUtilityFunctions::type_convert("Hi!", Variant::Type::INT); + CHECK(converted.get_type() == Variant::Type::INT); + CHECK(converted == Variant(0)); + + converted = VariantUtilityFunctions::type_convert("123", Variant::Type::INT); + CHECK(converted.get_type() == Variant::Type::INT); + CHECK(converted == Variant(123)); + + converted = VariantUtilityFunctions::type_convert(123, Variant::Type::STRING); + CHECK(converted.get_type() == Variant::Type::STRING); + CHECK(converted == Variant("123")); + + converted = VariantUtilityFunctions::type_convert(123.4, Variant::Type::INT); + CHECK(converted.get_type() == Variant::Type::INT); + CHECK(converted == Variant(123)); + + converted = VariantUtilityFunctions::type_convert(5, Variant::Type::VECTOR2); + CHECK(converted.get_type() == Variant::Type::VECTOR2); + CHECK(converted == Variant(Vector2(0, 0))); + + converted = VariantUtilityFunctions::type_convert(Vector3(1, 2, 3), Variant::Type::VECTOR2); + CHECK(converted.get_type() == Variant::Type::VECTOR2); + CHECK(converted == Variant(Vector2(1, 2))); + + converted = VariantUtilityFunctions::type_convert(Vector2(1, 2), Variant::Type::VECTOR4); + CHECK(converted.get_type() == Variant::Type::VECTOR4); + CHECK(converted == Variant(Vector4(1, 2, 0, 0))); + + converted = VariantUtilityFunctions::type_convert(Vector4(1.2, 3.4, 5.6, 7.8), Variant::Type::VECTOR3I); + CHECK(converted.get_type() == Variant::Type::VECTOR3I); + CHECK(converted == Variant(Vector3i(1, 3, 5))); + + { + Basis basis = Basis::from_scale(Vector3(1.2, 3.4, 5.6)); + Transform3D transform = Transform3D(basis, Vector3()); + + converted = VariantUtilityFunctions::type_convert(transform, Variant::Type::BASIS); + CHECK(converted.get_type() == Variant::Type::BASIS); + CHECK(converted == basis); + + converted = VariantUtilityFunctions::type_convert(basis, Variant::Type::TRANSFORM3D); + CHECK(converted.get_type() == Variant::Type::TRANSFORM3D); + CHECK(converted == transform); + + converted = VariantUtilityFunctions::type_convert(basis, Variant::Type::STRING); + CHECK(converted.get_type() == Variant::Type::STRING); + CHECK(converted == Variant("[X: (1.2, 0, 0), Y: (0, 3.4, 0), Z: (0, 0, 5.6)]")); + } + + { + Array arr; + arr.push_back(1.2); + arr.push_back(3.4); + arr.push_back(5.6); + + PackedFloat64Array packed; + packed.push_back(1.2); + packed.push_back(3.4); + packed.push_back(5.6); + + converted = VariantUtilityFunctions::type_convert(arr, Variant::Type::PACKED_FLOAT64_ARRAY); + CHECK(converted.get_type() == Variant::Type::PACKED_FLOAT64_ARRAY); + CHECK(converted == packed); + + converted = VariantUtilityFunctions::type_convert(packed, Variant::Type::ARRAY); + CHECK(converted.get_type() == Variant::Type::ARRAY); + CHECK(converted == arr); + } + + { + // Check that using Variant::call_utility_function also works. + Vector args; + Variant data_arg = "Hi!"; + args.push_back(&data_arg); + Variant type_arg = Variant::Type::NIL; + args.push_back(&type_arg); + Callable::CallError call_error; + Variant::call_utility_function("type_convert", &converted, (const Variant **)args.ptr(), 2, call_error); + CHECK(converted.get_type() == Variant::Type::NIL); + CHECK(converted == Variant()); + + type_arg = Variant::Type::INT; + Variant::call_utility_function("type_convert", &converted, (const Variant **)args.ptr(), 2, call_error); + CHECK(converted.get_type() == Variant::Type::INT); + CHECK(converted == Variant(0)); + + data_arg = "123"; + Variant::call_utility_function("type_convert", &converted, (const Variant **)args.ptr(), 2, call_error); + CHECK(converted.get_type() == Variant::Type::INT); + CHECK(converted == Variant(123)); + } +} + +} // namespace TestVariantUtility + +#endif // TEST_VARIANT_UTILITY_H diff --git a/tests/test_main.cpp b/tests/test_main.cpp index 3a80fc8c0040..a43dfaa57f29 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -89,6 +89,7 @@ #include "tests/core/variant/test_array.h" #include "tests/core/variant/test_dictionary.h" #include "tests/core/variant/test_variant.h" +#include "tests/core/variant/test_variant_utility.h" #include "tests/scene/test_animation.h" #include "tests/scene/test_arraymesh.h" #include "tests/scene/test_audio_stream_wav.h"