Browse Source

Expose some low level functions and String operators.

bruvzg 2 years ago
parent
commit
abca497b72

+ 107 - 4
binding_generator.py

@@ -367,6 +367,15 @@ def generate_builtin_class_header(builtin_api, size, used_classes, fully_used_cl
         result.append("#include <godot_cpp/variant/char_utils.hpp>")
         result.append("#include <godot_cpp/variant/ucaps.hpp>")
 
+    if class_name == "PackedStringArray":
+        result.append("#include <godot_cpp/variant/string.hpp>")
+    if class_name == "PackedColorArray":
+        result.append("#include <godot_cpp/variant/color.hpp>")
+    if class_name == "PackedVector2Array":
+        result.append("#include <godot_cpp/variant/vector2.hpp>")
+    if class_name == "PackedVector3Array":
+        result.append("#include <godot_cpp/variant/vector3.hpp>")
+
     if class_name == "Array":
         result.append("#include <godot_cpp/variant/array_helpers.hpp>")
 
@@ -584,10 +593,17 @@ def generate_builtin_class_header(builtin_api, size, used_classes, fully_used_cl
         result.append("\tbool operator!=(const wchar_t *p_str) const;")
         result.append("\tbool operator!=(const char16_t *p_str) const;")
         result.append("\tbool operator!=(const char32_t *p_str) const;")
-        result.append("\tString operator+(const char *p_chr);")
-        result.append("\tString operator+(const wchar_t *p_chr);")
-        result.append("\tString operator+(const char16_t *p_chr);")
-        result.append("\tString operator+(const char32_t *p_chr);")
+        result.append("\tString operator+(const char *p_str);")
+        result.append("\tString operator+(const wchar_t *p_str);")
+        result.append("\tString operator+(const char16_t *p_str);")
+        result.append("\tString operator+(const char32_t *p_str);")
+        result.append("\tString operator+(char32_t p_char);")
+        result.append("\tString &operator+=(const String &p_str);")
+        result.append("\tString &operator+=(char32_t p_char);")
+        result.append("\tString &operator+=(const char *p_str);")
+        result.append("\tString &operator+=(const wchar_t *p_str);")
+        result.append("\tString &operator+=(const char32_t *p_str);")
+
         result.append("\tconst char32_t &operator[](int p_index) const;")
         result.append("\tchar32_t &operator[](int p_index);")
         result.append("\tconst char32_t *ptr() const;")
@@ -611,6 +627,72 @@ def generate_builtin_class_header(builtin_api, size, used_classes, fully_used_cl
         result.append(f"\t" + return_type + f" &operator[](int p_index);")
         result.append(f"\tconst " + return_type + f" *ptr() const;")
         result.append(f"\t" + return_type + f" *ptrw();")
+        iterators = """
+    struct Iterator {
+		_FORCE_INLINE_ $TYPE &operator*() const {
+			return *elem_ptr;
+		}
+		_FORCE_INLINE_ $TYPE *operator->() const { return elem_ptr; }
+		_FORCE_INLINE_ Iterator &operator++() {
+			elem_ptr++;
+			return *this;
+		}
+		_FORCE_INLINE_ Iterator &operator--() {
+			elem_ptr--;
+			return *this;
+		}
+
+		_FORCE_INLINE_ bool operator==(const Iterator &b) const { return elem_ptr == b.elem_ptr; }
+		_FORCE_INLINE_ bool operator!=(const Iterator &b) const { return elem_ptr != b.elem_ptr; }
+
+		Iterator($TYPE *p_ptr) { elem_ptr = p_ptr; }
+		Iterator() {}
+		Iterator(const Iterator &p_it) { elem_ptr = p_it.elem_ptr; }
+
+	private:
+		$TYPE *elem_ptr = nullptr;
+	};
+
+	struct ConstIterator {
+		_FORCE_INLINE_ const $TYPE &operator*() const {
+			return *elem_ptr;
+		}
+		_FORCE_INLINE_ const $TYPE *operator->() const { return elem_ptr; }
+		_FORCE_INLINE_ ConstIterator &operator++() {
+			elem_ptr++;
+			return *this;
+		}
+		_FORCE_INLINE_ ConstIterator &operator--() {
+			elem_ptr--;
+			return *this;
+		}
+
+		_FORCE_INLINE_ bool operator==(const ConstIterator &b) const { return elem_ptr == b.elem_ptr; }
+		_FORCE_INLINE_ bool operator!=(const ConstIterator &b) const { return elem_ptr != b.elem_ptr; }
+
+		ConstIterator(const $TYPE *p_ptr) { elem_ptr = p_ptr; }
+		ConstIterator() {}
+		ConstIterator(const ConstIterator &p_it) { elem_ptr = p_it.elem_ptr; }
+
+	private:
+		const $TYPE *elem_ptr = nullptr;
+	};
+
+	_FORCE_INLINE_ Iterator begin() {
+		return Iterator(ptrw());
+	}
+	_FORCE_INLINE_ Iterator end() {
+		return Iterator(ptrw() + size());
+	}
+
+	_FORCE_INLINE_ ConstIterator begin() const {
+		return ConstIterator(ptr());
+	}
+	_FORCE_INLINE_ ConstIterator end() const {
+		return ConstIterator(ptr() + size());
+	}
+"""
+        result.append(iterators.replace("$TYPE", return_type))
 
     if class_name == "Array":
         result.append(f"\tconst Variant &operator[](int p_index) const;")
@@ -636,6 +718,7 @@ def generate_builtin_class_header(builtin_api, size, used_classes, fully_used_cl
         result.append("String operator+(const wchar_t *p_chr, const String &p_str);")
         result.append("String operator+(const char16_t *p_chr, const String &p_str);")
         result.append("String operator+(const char32_t *p_chr, const String &p_str);")
+        result.append("String operator+(char32_t p_char, const String &p_str);")
 
         result.append("String itos(int64_t p_val);")
         result.append("String uitos(uint64_t p_val);")
@@ -1252,6 +1335,26 @@ def generate_engine_class_header(class_api, used_classes, fully_used_classes, us
     result.append("public:")
 
     # Special cases.
+    if class_name == "XMLParser":
+        result.append("\tError _open_buffer(const uint8_t *p_buffer, size_t p_size);")
+
+    if class_name == "FileAccess":
+        result.append("\tuint64_t get_buffer(uint8_t *p_dst, uint64_t p_length) const;")
+        result.append("\tvoid store_buffer(const uint8_t *p_src, uint64_t p_length);")
+
+    if class_name == "WorkerThreadPool":
+        result.append("\tenum {")
+        result.append("\tINVALID_TASK_ID = -1")
+        result.append("\t};")
+        result.append("\ttypedef int64_t TaskID;")
+        result.append("\ttypedef int64_t GroupID;")
+        result.append(
+            "\tTaskID add_native_task(void (*p_func)(void *), void *p_userdata, bool p_high_priority = false, const String &p_description = String());"
+        )
+        result.append(
+            "\tGroupID add_native_group_task(void (*p_func)(void *, uint32_t), void *p_userdata, int p_elements, int p_tasks = -1, bool p_high_priority = false, const String &p_description = String());"
+        )
+
     if class_name == "Object":
         result.append("")
 

File diff suppressed because it is too large
+ 66 - 981
gdextension/extension_api.json


+ 20 - 0
gdextension/gdextension_interface.h

@@ -503,6 +503,26 @@ typedef struct {
 	char32_t *(*string_operator_index)(GDExtensionStringPtr p_self, GDExtensionInt p_index);
 	const char32_t *(*string_operator_index_const)(GDExtensionConstStringPtr p_self, GDExtensionInt p_index);
 
+	void (*string_operator_plus_eq_string)(GDExtensionStringPtr p_self, GDExtensionConstStringPtr p_b);
+	void (*string_operator_plus_eq_char)(GDExtensionStringPtr p_self, char32_t p_b);
+	void (*string_operator_plus_eq_cstr)(GDExtensionStringPtr p_self, const char *p_b);
+	void (*string_operator_plus_eq_wcstr)(GDExtensionStringPtr p_self, const wchar_t *p_b);
+	void (*string_operator_plus_eq_c32str)(GDExtensionStringPtr p_self, const char32_t *p_b);
+
+	/*  XMLParser extra utilities */
+
+	GDExtensionInt (*xml_parser_open_buffer)(GDExtensionObjectPtr p_instance, const uint8_t *p_buffer, size_t p_size);
+
+	/*  FileAccess extra utilities */
+
+	void (*file_access_store_buffer)(GDExtensionObjectPtr p_instance, const uint8_t *p_src, uint64_t p_length);
+	uint64_t (*file_access_get_buffer)(GDExtensionConstObjectPtr p_instance, uint8_t *p_dst, uint64_t p_length);
+
+	/*  WorkerThreadPool extra utilities */
+
+	int64_t (*worker_thread_pool_add_native_group_task)(GDExtensionObjectPtr p_instance, void (*p_func)(void *, uint32_t), void *p_userdata, int p_elements, int p_tasks, bool p_high_priority, GDExtensionConstStringPtr p_description);
+	int64_t (*worker_thread_pool_add_native_task)(GDExtensionObjectPtr p_instance, void (*p_func)(void *), void *p_userdata, bool p_high_priority, GDExtensionConstStringPtr p_description);
+
 	/* Packed array functions */
 
 	uint8_t *(*packed_byte_array_operator_index)(GDExtensionTypePtr p_self, GDExtensionInt p_index); // p_self should be a PackedByteArray

+ 161 - 47
include/godot_cpp/core/math.hpp

@@ -38,20 +38,6 @@
 #include <cmath>
 
 namespace godot {
-namespace Math {
-
-// This epsilon should match the one used by Godot for consistency.
-// Using `f` when `real_t` is float.
-#define CMP_EPSILON 0.00001f
-#define CMP_EPSILON2 (CMP_EPSILON * CMP_EPSILON)
-
-// This epsilon is for values related to a unit size (scalar or vector len).
-#ifdef PRECISE_MATH_CHECKS
-#define UNIT_EPSILON 0.00001
-#else
-// Tolerate some more floating point error normally.
-#define UNIT_EPSILON 0.001
-#endif
 
 #define Math_SQRT12 0.7071067811865475244008443621048490
 #define Math_SQRT2 1.4142135623730950488016887242
@@ -62,32 +48,174 @@ namespace Math {
 #define Math_INF INFINITY
 #define Math_NAN NAN
 
-// Windows badly defines a lot of stuff we'll never use. Undefine it.
-#ifdef _WIN32
-#undef MIN // override standard definition
-#undef MAX // override standard definition
-#undef CLAMP // override standard definition
-#endif
+// Make room for our constexpr's below by overriding potential system-specific macros.
+#undef ABS
+#undef SIGN
+#undef MIN
+#undef MAX
+#undef CLAMP
 
 // Generic ABS function, for math uses please use Math::abs.
-#ifndef ABS
-#define ABS(m_v) (((m_v) < 0) ? (-(m_v)) : (m_v))
-#endif
+template <typename T>
+constexpr T ABS(T m_v) {
+	return m_v < 0 ? -m_v : m_v;
+}
 
-#ifndef SIGN
-#define SIGN(m_v) (((m_v) == 0) ? (0.0) : (((m_v) < 0) ? (-1.0) : (+1.0)))
-#endif
+template <typename T>
+constexpr const T SIGN(const T m_v) {
+	return m_v == 0 ? 0.0f : (m_v < 0 ? -1.0f : +1.0f);
+}
 
-#ifndef MIN
-#define MIN(m_a, m_b) (((m_a) < (m_b)) ? (m_a) : (m_b))
-#endif
+template <typename T, typename T2>
+constexpr auto MIN(const T m_a, const T2 m_b) {
+	return m_a < m_b ? m_a : m_b;
+}
+
+template <typename T, typename T2>
+constexpr auto MAX(const T m_a, const T2 m_b) {
+	return m_a > m_b ? m_a : m_b;
+}
+
+template <typename T, typename T2, typename T3>
+constexpr auto CLAMP(const T m_a, const T2 m_min, const T3 m_max) {
+	return m_a < m_min ? m_min : (m_a > m_max ? m_max : m_a);
+}
+
+// Generic swap template.
+#ifndef SWAP
+#define SWAP(m_x, m_y) __swap_tmpl((m_x), (m_y))
+template <class T>
+inline void __swap_tmpl(T &x, T &y) {
+	T aux = x;
+	x = y;
+	y = aux;
+}
+#endif // SWAP
+
+/* Functions to handle powers of 2 and shifting. */
+
+// Function to find the next power of 2 to an integer.
+static _FORCE_INLINE_ unsigned int next_power_of_2(unsigned int x) {
+	if (x == 0) {
+		return 0;
+	}
+
+	--x;
+	x |= x >> 1;
+	x |= x >> 2;
+	x |= x >> 4;
+	x |= x >> 8;
+	x |= x >> 16;
+
+	return ++x;
+}
+
+// Function to find the previous power of 2 to an integer.
+static _FORCE_INLINE_ unsigned int previous_power_of_2(unsigned int x) {
+	x |= x >> 1;
+	x |= x >> 2;
+	x |= x >> 4;
+	x |= x >> 8;
+	x |= x >> 16;
+	return x - (x >> 1);
+}
+
+// Function to find the closest power of 2 to an integer.
+static _FORCE_INLINE_ unsigned int closest_power_of_2(unsigned int x) {
+	unsigned int nx = next_power_of_2(x);
+	unsigned int px = previous_power_of_2(x);
+	return (nx - x) > (x - px) ? px : nx;
+}
+
+// Get a shift value from a power of 2.
+static inline int get_shift_from_power_of_2(unsigned int p_bits) {
+	for (unsigned int i = 0; i < 32; i++) {
+		if (p_bits == (unsigned int)(1 << i)) {
+			return i;
+		}
+	}
+
+	return -1;
+}
+
+template <class T>
+static _FORCE_INLINE_ T nearest_power_of_2_templated(T x) {
+	--x;
+
+	// The number of operations on x is the base two logarithm
+	// of the number of bits in the type. Add three to account
+	// for sizeof(T) being in bytes.
+	size_t num = get_shift_from_power_of_2(sizeof(T)) + 3;
+
+	// If the compiler is smart, it unrolls this loop.
+	// If it's dumb, this is a bit slow.
+	for (size_t i = 0; i < num; i++) {
+		x |= x >> (1 << i);
+	}
+
+	return ++x;
+}
+
+// Function to find the nearest (bigger) power of 2 to an integer.
+static inline unsigned int nearest_shift(unsigned int p_number) {
+	for (int i = 30; i >= 0; i--) {
+		if (p_number & (1 << i)) {
+			return i + 1;
+		}
+	}
+
+	return 0;
+}
+
+// constexpr function to find the floored log2 of a number
+template <typename T>
+constexpr T floor_log2(T x) {
+	return x < 2 ? x : 1 + floor_log2(x >> 1);
+}
+
+// Get the number of bits needed to represent the number.
+// IE, if you pass in 8, you will get 4.
+// If you want to know how many bits are needed to store 8 values however, pass in (8 - 1).
+template <typename T>
+constexpr T get_num_bits(T x) {
+	return floor_log2(x);
+}
+
+// Swap 16, 32 and 64 bits value for endianness.
+#if defined(__GNUC__)
+#define BSWAP16(x) __builtin_bswap16(x)
+#define BSWAP32(x) __builtin_bswap32(x)
+#define BSWAP64(x) __builtin_bswap64(x)
+#else
+static inline uint16_t BSWAP16(uint16_t x) {
+	return (x >> 8) | (x << 8);
+}
+
+static inline uint32_t BSWAP32(uint32_t x) {
+	return ((x << 24) | ((x << 8) & 0x00FF0000) | ((x >> 8) & 0x0000FF00) | (x >> 24));
+}
 
-#ifndef MAX
-#define MAX(m_a, m_b) (((m_a) > (m_b)) ? (m_a) : (m_b))
+static inline uint64_t BSWAP64(uint64_t x) {
+	x = (x & 0x00000000FFFFFFFF) << 32 | (x & 0xFFFFFFFF00000000) >> 32;
+	x = (x & 0x0000FFFF0000FFFF) << 16 | (x & 0xFFFF0000FFFF0000) >> 16;
+	x = (x & 0x00FF00FF00FF00FF) << 8 | (x & 0xFF00FF00FF00FF00) >> 8;
+	return x;
+}
 #endif
 
-#ifndef CLAMP
-#define CLAMP(m_a, m_min, m_max) (((m_a) < (m_min)) ? (m_min) : (((m_a) > (m_max)) ? m_max : m_a))
+namespace Math {
+
+// This epsilon should match the one used by Godot for consistency.
+// Using `f` when `real_t` is float.
+#define CMP_EPSILON 0.00001f
+#define CMP_EPSILON2 (CMP_EPSILON * CMP_EPSILON)
+
+// This epsilon is for values related to a unit size (scalar or vector len).
+#ifdef PRECISE_MATH_CHECKS
+#define UNIT_EPSILON 0.00001
+#else
+// Tolerate some more floating point error normally.
+#define UNIT_EPSILON 0.001
 #endif
 
 // Functions reproduced as in Godot's source code `math_funcs.h`.
@@ -628,20 +756,6 @@ inline double pingpong(double value, double length) {
 	return (length != 0.0) ? abs(fract((value - length) / (length * 2.0)) * length * 2.0 - length) : 0.0;
 }
 
-inline unsigned int next_power_of_2(unsigned int x) {
-	if (x == 0)
-		return 0;
-
-	--x;
-	x |= x >> 1;
-	x |= x >> 2;
-	x |= x >> 4;
-	x |= x >> 8;
-	x |= x >> 16;
-
-	return ++x;
-}
-
 // This function should be as fast as possible and rounding mode should not matter.
 inline int fast_ftoi(float a) {
 	static int b;

+ 2 - 2
include/godot_cpp/templates/cowdata.hpp

@@ -91,7 +91,7 @@ private:
 	}
 
 	_FORCE_INLINE_ size_t _get_alloc_size(size_t p_elements) const {
-		return Math::next_power_of_2(p_elements * sizeof(T));
+		return next_power_of_2(p_elements * sizeof(T));
 	}
 
 	_FORCE_INLINE_ bool _get_alloc_size_checked(size_t p_elements, size_t *out) const {
@@ -102,7 +102,7 @@ private:
 			*out = 0;
 			return false;
 		}
-		*out = Math::next_power_of_2(o);
+		*out = next_power_of_2(o);
 		if (__builtin_add_overflow(o, static_cast<size_t>(32), &p)) {
 			return false; // No longer allocated here.
 		}

+ 12 - 0
include/godot_cpp/variant/variant.hpp

@@ -322,6 +322,18 @@ struct VariantComparator {
 	static _FORCE_INLINE_ bool compare(const Variant &p_lhs, const Variant &p_rhs) { return p_lhs.hash_compare(p_rhs); }
 };
 
+template <typename... VarArgs>
+String vformat(const String &p_text, const VarArgs... p_args) {
+	Variant args[sizeof...(p_args) + 1] = { p_args..., Variant() }; // +1 makes sure zero sized arrays are also supported.
+	Array args_array;
+	args_array.resize(sizeof...(p_args));
+	for (uint32_t i = 0; i < sizeof...(p_args); i++) {
+		args_array[i] = args[i];
+	}
+
+	return p_text % args_array;
+}
+
 } // namespace godot
 
 #endif // GODOT_VARIANT_HPP

+ 58 - 0
src/classes/low_level.cpp

@@ -0,0 +1,58 @@
+/**************************************************************************/
+/*  low_level.cpp                                                         */
+/**************************************************************************/
+/*                         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.                 */
+/**************************************************************************/
+
+#include <godot_cpp/classes/file_access.hpp>
+#include <godot_cpp/classes/worker_thread_pool.hpp>
+#include <godot_cpp/classes/xml_parser.hpp>
+
+#include <godot_cpp/godot.hpp>
+
+namespace godot {
+Error XMLParser::_open_buffer(const uint8_t *p_buffer, size_t p_size) {
+	return (Error)internal::gde_interface->xml_parser_open_buffer(_owner, p_buffer, p_size);
+}
+
+uint64_t FileAccess::get_buffer(uint8_t *p_dst, uint64_t p_length) const {
+	return internal::gde_interface->file_access_get_buffer(_owner, p_dst, p_length);
+}
+
+void FileAccess::store_buffer(const uint8_t *p_src, uint64_t p_length) {
+	internal::gde_interface->file_access_store_buffer(_owner, p_src, p_length);
+}
+
+WorkerThreadPool::TaskID WorkerThreadPool::add_native_task(void (*p_func)(void *), void *p_userdata, bool p_high_priority, const String &p_description) {
+	return (TaskID)internal::gde_interface->worker_thread_pool_add_native_task(_owner, p_func, p_userdata, p_high_priority, (const GDExtensionStringPtr)&p_description);
+}
+
+WorkerThreadPool::GroupID WorkerThreadPool::add_native_group_task(void (*p_func)(void *, uint32_t), void *p_userdata, int p_elements, int p_tasks, bool p_high_priority, const String &p_description) {
+	return (GroupID)internal::gde_interface->worker_thread_pool_add_native_group_task(_owner, p_func, p_userdata, p_elements, p_tasks, p_high_priority, (const GDExtensionStringPtr)&p_description);
+}
+
+} // namespace godot

+ 41 - 8
src/variant/char_string.cpp

@@ -332,20 +332,49 @@ bool String::operator!=(const char32_t *p_str) const {
 	return *this != String(p_str);
 }
 
-String String::operator+(const char *p_chr) {
-	return *this + String(p_chr);
+String String::operator+(const char *p_str) {
+	return *this + String(p_str);
 }
 
-String String::operator+(const wchar_t *p_chr) {
-	return *this + String(p_chr);
+String String::operator+(const wchar_t *p_str) {
+	return *this + String(p_str);
 }
 
-String String::operator+(const char16_t *p_chr) {
-	return *this + String(p_chr);
+String String::operator+(const char16_t *p_str) {
+	return *this + String(p_str);
 }
 
-String String::operator+(const char32_t *p_chr) {
-	return *this + String(p_chr);
+String String::operator+(const char32_t *p_str) {
+	return *this + String(p_str);
+}
+
+String String::operator+(const char32_t p_char) {
+	return *this + String::chr(p_char);
+}
+
+String &String::operator+=(const String &p_str) {
+	internal::gde_interface->string_operator_plus_eq_string((GDExtensionStringPtr)this, (const GDExtensionStringPtr)&p_str);
+	return *this;
+}
+
+String &String::operator+=(char32_t p_char) {
+	internal::gde_interface->string_operator_plus_eq_char((GDExtensionStringPtr)this, p_char);
+	return *this;
+}
+
+String &String::operator+=(const char *p_str) {
+	internal::gde_interface->string_operator_plus_eq_cstr((GDExtensionStringPtr)this, p_str);
+	return *this;
+}
+
+String &String::operator+=(const wchar_t *p_str) {
+	internal::gde_interface->string_operator_plus_eq_wcstr((GDExtensionStringPtr)this, p_str);
+	return *this;
+}
+
+String &String::operator+=(const char32_t *p_str) {
+	internal::gde_interface->string_operator_plus_eq_c32str((GDExtensionStringPtr)this, p_str);
+	return *this;
 }
 
 const char32_t &String::operator[](int p_index) const {
@@ -412,6 +441,10 @@ String operator+(const char32_t *p_chr, const String &p_str) {
 	return String(p_chr) + p_str;
 }
 
+String operator+(char32_t p_char, const String &p_str) {
+	return String::chr(p_char) + p_str;
+}
+
 StringName::StringName(const char *from) :
 		StringName(String(from)) {}
 

+ 9 - 0
test/demo/main.gd

@@ -54,6 +54,15 @@ func _ready():
 	var array: Array[int] = [1, 2, 3]
 	$Example.test_tarray_arg(array)
 
+	prints("String += operator")
+	prints("  test string +=", $Example.test_string_ops())
+
+	prints("WorkerThreadPool")
+	prints("  test worker_thread_pool", $Example.test_workpool_ops())
+
+	prints("PackedArray iterators")
+	prints("  test packed array iterators", $Example.test_vector_ops())
+
 	prints("Properties")
 	prints("  custom position is", $Example.group_subgroup_custom_position)
 	$Example.group_subgroup_custom_position = Vector2(50, 50)

+ 24 - 0
test/src/example.cpp

@@ -123,6 +123,8 @@ void Example::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("test_tarray"), &Example::test_tarray);
 	ClassDB::bind_method(D_METHOD("test_dictionary"), &Example::test_dictionary);
 	ClassDB::bind_method(D_METHOD("test_node_argument"), &Example::test_node_argument);
+	ClassDB::bind_method(D_METHOD("test_string_ops"), &Example::test_string_ops);
+	ClassDB::bind_method(D_METHOD("test_vector_ops"), &Example::test_vector_ops);
 
 	ClassDB::bind_method(D_METHOD("def_args", "a", "b"), &Example::def_args, DEFVAL(100), DEFVAL(200));
 
@@ -255,6 +257,28 @@ Array Example::test_array() const {
 	return arr;
 }
 
+String Example::test_string_ops() const {
+	String s = String("A");
+	s += "B";
+	s += "C";
+	s += char32_t(0x010E);
+	s = s + "E";
+	return s;
+}
+
+int Example::test_vector_ops() const {
+	PackedInt32Array arr;
+	arr.push_back(10);
+	arr.push_back(20);
+	arr.push_back(30);
+	arr.push_back(45);
+	int ret = 0;
+	for (const int32_t &E : arr) {
+		ret += E;
+	}
+	return ret;
+}
+
 void Example::test_tarray_arg(const TypedArray<int64_t> &p_array) {
 	for (int i = 0; i < p_array.size(); i++) {
 		UtilityFunctions::print(p_array[i]);

+ 2 - 0
test/src/example.h

@@ -101,6 +101,8 @@ public:
 	TypedArray<Vector2> test_tarray() const;
 	Dictionary test_dictionary() const;
 	Example *test_node_argument(Example *p_node) const;
+	String test_string_ops() const;
+	int test_vector_ops() const;
 
 	// Property.
 	void set_custom_position(const Vector2 &pos);

Some files were not shown because too many files changed in this diff