소스 검색

[Core] Add iteration support to `Array`

A Thousand Ships 1 년 전
부모
커밋
64146cb7f3

+ 2 - 2
core/config/project_settings.cpp

@@ -1330,8 +1330,8 @@ void ProjectSettings::load_scene_groups_cache() {
 		for (const String &E : scene_paths) {
 			Array scene_groups = cf->get_value(E, "groups", Array());
 			HashSet<StringName> cache;
-			for (int i = 0; i < scene_groups.size(); ++i) {
-				cache.insert(scene_groups[i]);
+			for (const Variant &scene_group : scene_groups) {
+				cache.insert(scene_group);
 			}
 			add_scene_groups_cache(E, cache);
 		}

+ 10 - 14
core/extension/extension_api_dump.cpp

@@ -1313,9 +1313,7 @@ static bool compare_value(const String &p_path, const String &p_field, const Var
 	} else if (p_old_value.get_type() == Variant::DICTIONARY && p_new_value.get_type() == Variant::DICTIONARY) {
 		Dictionary old_dict = p_old_value;
 		Dictionary new_dict = p_new_value;
-		Array old_keys = old_dict.keys();
-		for (int i = 0; i < old_keys.size(); i++) {
-			Variant key = old_keys[i];
+		for (const Variant &key : old_dict.keys()) {
 			if (!new_dict.has(key)) {
 				failed = true;
 				print_error(vformat("Validate extension JSON: Error: Field '%s': %s was removed.", p_path, key));
@@ -1328,9 +1326,7 @@ static bool compare_value(const String &p_path, const String &p_field, const Var
 				failed = true;
 			}
 		}
-		Array new_keys = old_dict.keys();
-		for (int i = 0; i < new_keys.size(); i++) {
-			Variant key = new_keys[i];
+		for (const Variant &key : old_dict.keys()) {
 			if (!old_dict.has(key)) {
 				failed = true;
 				print_error(vformat("Validate extension JSON: Error: Field '%s': %s was added with value %s.", p_path, key, new_dict[key]));
@@ -1356,8 +1352,8 @@ static bool compare_dict_array(const Dictionary &p_old_api, const Dictionary &p_
 	Array new_api = p_new_api[p_base_array];
 	HashMap<String, Dictionary> new_api_assoc;
 
-	for (int i = 0; i < new_api.size(); i++) {
-		Dictionary elem = new_api[i];
+	for (const Variant &var : new_api) {
+		Dictionary elem = var;
 		ERR_FAIL_COND_V_MSG(!elem.has(p_name_field), false, vformat("Validate extension JSON: Element of base_array '%s' is missing field '%s'. This is a bug.", base_array, p_name_field));
 		String name = elem[p_name_field];
 		if (p_compare_operators && elem.has("right_type")) {
@@ -1367,8 +1363,8 @@ static bool compare_dict_array(const Dictionary &p_old_api, const Dictionary &p_
 	}
 
 	Array old_api = p_old_api[p_base_array];
-	for (int i = 0; i < old_api.size(); i++) {
-		Dictionary old_elem = old_api[i];
+	for (const Variant &var : old_api) {
+		Dictionary old_elem = var;
 		if (!old_elem.has(p_name_field)) {
 			failed = true;
 			print_error(vformat("Validate extension JSON: JSON file: element of base array '%s' is missing the field: '%s'.", base_array, p_name_field));
@@ -1508,16 +1504,16 @@ static bool compare_sub_dict_array(HashSet<String> &r_removed_classes_registered
 	Array new_api = p_new_api[p_outer];
 	HashMap<String, Dictionary> new_api_assoc;
 
-	for (int i = 0; i < new_api.size(); i++) {
-		Dictionary elem = new_api[i];
+	for (const Variant &var : new_api) {
+		Dictionary elem = var;
 		ERR_FAIL_COND_V_MSG(!elem.has(p_outer_name), false, vformat("Validate extension JSON: Element of base_array '%s' is missing field '%s'. This is a bug.", p_outer, p_outer_name));
 		new_api_assoc.insert(elem[p_outer_name], elem);
 	}
 
 	Array old_api = p_old_api[p_outer];
 
-	for (int i = 0; i < old_api.size(); i++) {
-		Dictionary old_elem = old_api[i];
+	for (const Variant &var : old_api) {
+		Dictionary old_elem = var;
 		if (!old_elem.has(p_outer_name)) {
 			failed = true;
 			print_error(vformat("Validate extension JSON: JSON file: element of base array '%s' is missing the field: '%s'.", p_outer, p_outer_name));

+ 7 - 4
core/io/json.cpp

@@ -86,7 +86,7 @@ String JSON::_stringify(const Variant &p_var, const String &p_indent, int p_cur_
 		case Variant::PACKED_STRING_ARRAY:
 		case Variant::ARRAY: {
 			Array a = p_var;
-			if (a.size() == 0) {
+			if (a.is_empty()) {
 				return "[]";
 			}
 			String s = "[";
@@ -95,12 +95,15 @@ String JSON::_stringify(const Variant &p_var, const String &p_indent, int p_cur_
 			ERR_FAIL_COND_V_MSG(p_markers.has(a.id()), "\"[...]\"", "Converting circular structure to JSON.");
 			p_markers.insert(a.id());
 
-			for (int i = 0; i < a.size(); i++) {
-				if (i > 0) {
+			bool first = true;
+			for (const Variant &var : a) {
+				if (first) {
+					first = false;
+				} else {
 					s += ",";
 					s += end_statement;
 				}
-				s += _make_indent(p_indent, p_cur_indent + 1) + _stringify(a[i], p_indent, p_cur_indent + 1, p_sort_keys, p_markers);
+				s += _make_indent(p_indent, p_cur_indent + 1) + _stringify(var, p_indent, p_cur_indent + 1, p_sort_keys, p_markers);
 			}
 			s += end_statement + _make_indent(p_indent, p_cur_indent) + "]";
 			p_markers.erase(a.id());

+ 2 - 2
core/io/marshalls.cpp

@@ -1729,9 +1729,9 @@ Error encode_variant(const Variant &p_variant, uint8_t *r_buffer, int &r_len, bo
 			}
 			r_len += 4;
 
-			for (int i = 0; i < array.size(); i++) {
+			for (const Variant &var : array) {
 				int len;
-				Error err = encode_variant(array.get(i), buf, len, p_full_objects, p_depth + 1);
+				Error err = encode_variant(var, buf, len, p_full_objects, p_depth + 1);
 				ERR_FAIL_COND_V(err, err);
 				ERR_FAIL_COND_V(len % 4, ERR_BUG);
 				if (buf) {

+ 3 - 5
core/io/resource_format_binary.cpp

@@ -1844,8 +1844,8 @@ void ResourceFormatSaverBinaryInstance::write_variant(Ref<FileAccess> f, const V
 			f->store_32(VARIANT_ARRAY);
 			Array a = p_property;
 			f->store_32(uint32_t(a.size()));
-			for (int i = 0; i < a.size(); i++) {
-				write_variant(f, a[i], resource_map, external_resources, string_map);
+			for (const Variant &var : a) {
+				write_variant(f, var, resource_map, external_resources, string_map);
 			}
 
 		} break;
@@ -2017,9 +2017,7 @@ void ResourceFormatSaverBinaryInstance::_find_resources(const Variant &p_variant
 		case Variant::ARRAY: {
 			Array varray = p_variant;
 			_find_resources(varray.get_typed_script());
-			int len = varray.size();
-			for (int i = 0; i < len; i++) {
-				const Variant &v = varray.get(i);
+			for (const Variant &v : varray) {
 				_find_resources(v);
 			}
 

+ 3 - 2
core/io/resource_loader.cpp

@@ -1035,8 +1035,9 @@ void ResourceLoader::load_translation_remaps() {
 		Array langs = remaps[E];
 		Vector<String> lang_remaps;
 		lang_remaps.resize(langs.size());
-		for (int i = 0; i < langs.size(); i++) {
-			lang_remaps.write[i] = langs[i];
+		String *lang_remaps_ptrw = lang_remaps.ptrw();
+		for (const Variant &lang : langs) {
+			*lang_remaps_ptrw++ = lang;
 		}
 
 		translation_remaps[String(E)] = lang_remaps;

+ 8 - 8
core/object/object.cpp

@@ -142,16 +142,16 @@ MethodInfo MethodInfo::from_dict(const Dictionary &p_dict) {
 		args = p_dict["args"];
 	}
 
-	for (int i = 0; i < args.size(); i++) {
-		Dictionary d = args[i];
+	for (const Variant &arg : args) {
+		Dictionary d = arg;
 		mi.arguments.push_back(PropertyInfo::from_dict(d));
 	}
 	Array defargs;
 	if (p_dict.has("default_args")) {
 		defargs = p_dict["default_args"];
 	}
-	for (int i = 0; i < defargs.size(); i++) {
-		mi.default_arguments.push_back(defargs[i]);
+	for (const Variant &defarg : defargs) {
+		mi.default_arguments.push_back(defarg);
 	}
 
 	if (p_dict.has("return")) {
@@ -1233,8 +1233,8 @@ void Object::_add_user_signal(const String &p_name, const Array &p_args) {
 	MethodInfo mi;
 	mi.name = p_name;
 
-	for (int i = 0; i < p_args.size(); i++) {
-		Dictionary d = p_args[i];
+	for (const Variant &arg : p_args) {
+		Dictionary d = arg;
 		PropertyInfo param;
 
 		if (d.has("name")) {
@@ -1585,8 +1585,8 @@ void Object::_clear_internal_resource_paths(const Variant &p_var) {
 		} break;
 		case Variant::ARRAY: {
 			Array a = p_var;
-			for (int i = 0; i < a.size(); i++) {
-				_clear_internal_resource_paths(a[i]);
+			for (const Variant &var : a) {
+				_clear_internal_resource_paths(var);
 			}
 
 		} break;

+ 6 - 6
core/object/script_language.cpp

@@ -251,8 +251,8 @@ void ScriptServer::init_languages() {
 		if (ProjectSettings::get_singleton()->has_setting("_global_script_classes")) {
 			Array script_classes = GLOBAL_GET("_global_script_classes");
 
-			for (int i = 0; i < script_classes.size(); i++) {
-				Dictionary c = script_classes[i];
+			for (const Variant &script_class : script_classes) {
+				Dictionary c = script_class;
 				if (!c.has("class") || !c.has("language") || !c.has("path") || !c.has("base")) {
 					continue;
 				}
@@ -263,8 +263,8 @@ void ScriptServer::init_languages() {
 #endif
 
 		Array script_classes = ProjectSettings::get_singleton()->get_global_class_list();
-		for (int i = 0; i < script_classes.size(); i++) {
-			Dictionary c = script_classes[i];
+		for (const Variant &script_class : script_classes) {
+			Dictionary c = script_class;
 			if (!c.has("class") || !c.has("language") || !c.has("path") || !c.has("base")) {
 				continue;
 			}
@@ -463,8 +463,8 @@ void ScriptServer::save_global_classes() {
 	Dictionary class_icons;
 
 	Array script_classes = ProjectSettings::get_singleton()->get_global_class_list();
-	for (int i = 0; i < script_classes.size(); i++) {
-		Dictionary d = script_classes[i];
+	for (const Variant &script_class : script_classes) {
+		Dictionary d = script_class;
 		if (!d.has("name") || !d.has("icon")) {
 			continue;
 		}

+ 18 - 18
core/object/script_language_extension.h

@@ -319,8 +319,8 @@ public:
 		}
 		if (r_errors != nullptr && ret.has("errors")) {
 			Array errors = ret["errors"];
-			for (int i = 0; i < errors.size(); i++) {
-				Dictionary err = errors[i];
+			for (const Variant &error : errors) {
+				Dictionary err = error;
 				ERR_CONTINUE(!err.has("line"));
 				ERR_CONTINUE(!err.has("column"));
 				ERR_CONTINUE(!err.has("message"));
@@ -339,8 +339,8 @@ public:
 		if (r_warnings != nullptr && ret.has("warnings")) {
 			ERR_FAIL_COND_V(!ret.has("warnings"), false);
 			Array warnings = ret["warnings"];
-			for (int i = 0; i < warnings.size(); i++) {
-				Dictionary warn = warnings[i];
+			for (const Variant &warning : warnings) {
+				Dictionary warn = warning;
 				ERR_CONTINUE(!warn.has("start_line"));
 				ERR_CONTINUE(!warn.has("end_line"));
 				ERR_CONTINUE(!warn.has("leftmost_column"));
@@ -402,8 +402,8 @@ public:
 
 		if (r_options != nullptr && ret.has("options")) {
 			Array options = ret["options"];
-			for (int i = 0; i < options.size(); i++) {
-				Dictionary op = options[i];
+			for (const Variant &var : options) {
+				Dictionary op = var;
 				CodeCompletionOption option;
 				ERR_CONTINUE(!op.has("kind"));
 				option.kind = CodeCompletionKind(int(op["kind"]));
@@ -502,8 +502,8 @@ public:
 		}
 		if (p_values != nullptr && ret.has("values")) {
 			Array values = ret["values"];
-			for (int i = 0; i < values.size(); i++) {
-				p_values->push_back(values[i]);
+			for (const Variant &value : values) {
+				p_values->push_back(value);
 			}
 		}
 	}
@@ -522,8 +522,8 @@ public:
 		}
 		if (p_values != nullptr && ret.has("values")) {
 			Array values = ret["values"];
-			for (int i = 0; i < values.size(); i++) {
-				p_values->push_back(values[i]);
+			for (const Variant &value : values) {
+				p_values->push_back(value);
 			}
 		}
 	}
@@ -549,8 +549,8 @@ public:
 		}
 		if (p_values != nullptr && ret.has("values")) {
 			Array values = ret["values"];
-			for (int i = 0; i < values.size(); i++) {
-				p_values->push_back(values[i]);
+			for (const Variant &value : values) {
+				p_values->push_back(value);
 			}
 		}
 	}
@@ -562,9 +562,9 @@ public:
 		TypedArray<Dictionary> ret;
 		GDVIRTUAL_REQUIRED_CALL(_debug_get_current_stack_info, ret);
 		Vector<StackInfo> sret;
-		for (int i = 0; i < ret.size(); i++) {
+		for (const Variant &var : ret) {
 			StackInfo si;
-			Dictionary d = ret[i];
+			Dictionary d = var;
 			ERR_CONTINUE(!d.has("file"));
 			ERR_CONTINUE(!d.has("func"));
 			ERR_CONTINUE(!d.has("line"));
@@ -595,8 +595,8 @@ public:
 	virtual void get_public_functions(List<MethodInfo> *p_functions) const override {
 		TypedArray<Dictionary> ret;
 		GDVIRTUAL_REQUIRED_CALL(_get_public_functions, ret);
-		for (int i = 0; i < ret.size(); i++) {
-			MethodInfo mi = MethodInfo::from_dict(ret[i]);
+		for (const Variant &var : ret) {
+			MethodInfo mi = MethodInfo::from_dict(var);
 			p_functions->push_back(mi);
 		}
 	}
@@ -615,8 +615,8 @@ public:
 	virtual void get_public_annotations(List<MethodInfo> *p_annotations) const override {
 		TypedArray<Dictionary> ret;
 		GDVIRTUAL_REQUIRED_CALL(_get_public_annotations, ret);
-		for (int i = 0; i < ret.size(); i++) {
-			MethodInfo mi = MethodInfo::from_dict(ret[i]);
+		for (const Variant &var : ret) {
+			MethodInfo mi = MethodInfo::from_dict(var);
 			p_annotations->push_back(mi);
 		}
 	}

+ 16 - 0
core/variant/array.cpp

@@ -81,6 +81,22 @@ void Array::_unref() const {
 	_p = nullptr;
 }
 
+Array::Iterator Array::begin() {
+	return Iterator(_p->array.ptrw(), _p->read_only);
+}
+
+Array::Iterator Array::end() {
+	return Iterator(_p->array.ptrw() + _p->array.size(), _p->read_only);
+}
+
+Array::ConstIterator Array::begin() const {
+	return ConstIterator(_p->array.ptr(), _p->read_only);
+}
+
+Array::ConstIterator Array::end() const {
+	return ConstIterator(_p->array.ptr() + _p->array.size(), _p->read_only);
+}
+
 Variant &Array::operator[](int p_idx) {
 	if (unlikely(_p->read_only)) {
 		*_p->read_only = _p->array[p_idx];

+ 64 - 0
core/variant/array.h

@@ -46,6 +46,70 @@ class Array {
 	void _unref() const;
 
 public:
+	struct ConstIterator {
+		_FORCE_INLINE_ const Variant &operator*() const;
+		_FORCE_INLINE_ const Variant *operator->() const;
+
+		_FORCE_INLINE_ ConstIterator &operator++();
+		_FORCE_INLINE_ ConstIterator &operator--();
+
+		_FORCE_INLINE_ bool operator==(const ConstIterator &p_other) const { return element_ptr == p_other.element_ptr; }
+		_FORCE_INLINE_ bool operator!=(const ConstIterator &p_other) const { return element_ptr == p_other.element_ptr; }
+
+		_FORCE_INLINE_ ConstIterator(const Variant *p_element_ptr, Variant *p_read_only = nullptr) :
+				element_ptr(p_element_ptr), read_only(p_read_only) {}
+		_FORCE_INLINE_ ConstIterator() {}
+		_FORCE_INLINE_ ConstIterator(const ConstIterator &p_other) :
+				element_ptr(p_other.element_ptr), read_only(p_other.read_only) {}
+
+		_FORCE_INLINE_ ConstIterator &operator=(const ConstIterator &p_other) {
+			element_ptr = p_other.element_ptr;
+			read_only = p_other.read_only;
+			return *this;
+		}
+
+	private:
+		const Variant *element_ptr = nullptr;
+		Variant *read_only = nullptr;
+	};
+
+	struct Iterator {
+		_FORCE_INLINE_ Variant &operator*() const;
+		_FORCE_INLINE_ Variant *operator->() const;
+
+		_FORCE_INLINE_ Iterator &operator++();
+		_FORCE_INLINE_ Iterator &operator--();
+
+		_FORCE_INLINE_ bool operator==(const Iterator &p_other) const { return element_ptr == p_other.element_ptr; }
+		_FORCE_INLINE_ bool operator!=(const Iterator &p_other) const { return element_ptr != p_other.element_ptr; }
+
+		_FORCE_INLINE_ Iterator(Variant *p_element_ptr, Variant *p_read_only = nullptr) :
+				element_ptr(p_element_ptr), read_only(p_read_only) {}
+		_FORCE_INLINE_ Iterator() {}
+		_FORCE_INLINE_ Iterator(const Iterator &p_other) :
+				element_ptr(p_other.element_ptr), read_only(p_other.read_only) {}
+
+		_FORCE_INLINE_ Iterator &operator=(const Iterator &p_other) {
+			element_ptr = p_other.element_ptr;
+			read_only = p_other.read_only;
+			return *this;
+		}
+
+		operator ConstIterator() const {
+			return ConstIterator(element_ptr, read_only);
+		}
+
+	private:
+		Variant *element_ptr = nullptr;
+		Variant *read_only = nullptr;
+	};
+
+	Iterator begin();
+	Iterator end();
+
+	ConstIterator begin() const;
+	ConstIterator end() const;
+
 	void _ref(const Array &p_from) const;
 
 	Variant &operator[](int p_idx);

+ 52 - 0
core/variant/variant.h

@@ -865,4 +865,56 @@ Callable Callable::bind(VarArgs... p_args) const {
 	return bindp(sizeof...(p_args) == 0 ? nullptr : (const Variant **)argptrs, sizeof...(p_args));
 }
 
+Variant &Array::Iterator::operator*() const {
+	if (unlikely(read_only)) {
+		*read_only = *element_ptr;
+		return *read_only;
+	}
+	return *element_ptr;
+}
+
+Variant *Array::Iterator::operator->() const {
+	if (unlikely(read_only)) {
+		*read_only = *element_ptr;
+		return read_only;
+	}
+	return element_ptr;
+}
+
+Array::Iterator &Array::Iterator::operator++() {
+	element_ptr++;
+	return *this;
+}
+
+Array::Iterator &Array::Iterator::operator--() {
+	element_ptr--;
+	return *this;
+}
+
+const Variant &Array::ConstIterator::operator*() const {
+	if (unlikely(read_only)) {
+		*read_only = *element_ptr;
+		return *read_only;
+	}
+	return *element_ptr;
+}
+
+const Variant *Array::ConstIterator::operator->() const {
+	if (unlikely(read_only)) {
+		*read_only = *element_ptr;
+		return read_only;
+	}
+	return element_ptr;
+}
+
+Array::ConstIterator &Array::ConstIterator::operator++() {
+	element_ptr++;
+	return *this;
+}
+
+Array::ConstIterator &Array::ConstIterator::operator--() {
+	element_ptr--;
+	return *this;
+}
+
 #endif // VARIANT_H

+ 6 - 4
core/variant/variant_parser.cpp

@@ -2012,12 +2012,14 @@ Error VariantWriter::write(const Variant &p_variant, StoreStringFunc p_store_str
 				p_recursion_count++;
 
 				p_store_string_func(p_store_string_ud, "[");
-				int len = array.size();
-				for (int i = 0; i < len; i++) {
-					if (i > 0) {
+				bool first = true;
+				for (const Variant &var : array) {
+					if (first) {
+						first = false;
+					} else {
 						p_store_string_func(p_store_string_ud, ", ");
 					}
-					write(array[i], p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count);
+					write(var, p_store_string_func, p_store_string_ud, p_encode_res_func, p_encode_res_ud, p_recursion_count);
 				}
 
 				p_store_string_func(p_store_string_ud, "]");

+ 6 - 8
scene/resources/resource_format_text.cpp

@@ -273,8 +273,8 @@ Ref<PackedScene> ResourceLoaderText::_parse_node_tag(VariantParser::ResourcePars
 
 			if (next_tag.fields.has("groups")) {
 				Array groups = next_tag.fields["groups"];
-				for (int i = 0; i < groups.size(); i++) {
-					packed_scene->get_state()->add_node_group(node_id, packed_scene->get_state()->add_name(groups[i]));
+				for (const Variant &group : groups) {
+					packed_scene->get_state()->add_node_group(node_id, packed_scene->get_state()->add_name(group));
 				}
 			}
 
@@ -352,8 +352,8 @@ Ref<PackedScene> ResourceLoaderText::_parse_node_tag(VariantParser::ResourcePars
 			}
 
 			Vector<int> bind_ints;
-			for (int i = 0; i < binds.size(); i++) {
-				bind_ints.push_back(packed_scene->get_state()->add_value(binds[i]));
+			for (const Variant &bind : binds) {
+				bind_ints.push_back(packed_scene->get_state()->add_value(bind));
 			}
 
 			packed_scene->get_state()->add_connection(
@@ -1952,10 +1952,8 @@ void ResourceFormatSaverTextInstance::_find_resources(const Variant &p_variant,
 		case Variant::ARRAY: {
 			Array varray = p_variant;
 			_find_resources(varray.get_typed_script());
-			int len = varray.size();
-			for (int i = 0; i < len; i++) {
-				const Variant &v = varray.get(i);
-				_find_resources(v);
+			for (const Variant &var : varray) {
+				_find_resources(var);
 			}
 
 		} break;

+ 48 - 0
tests/core/variant/test_array.h

@@ -545,6 +545,54 @@ TEST_CASE("[Array] Recursive self comparison") {
 	a2.clear();
 }
 
+TEST_CASE("[Array] Iteration") {
+	Array a1 = build_array(1, 2, 3);
+	Array a2 = build_array(1, 2, 3);
+
+	int idx = 0;
+	for (Variant &E : a1) {
+		CHECK_EQ(int(a2[idx]), int(E));
+		idx++;
+	}
+
+	idx = 0;
+
+	for (const Variant &E : (const Array &)a1) {
+		CHECK_EQ(int(a2[idx]), int(E));
+		idx++;
+	}
+
+	a1.clear();
+}
+
+TEST_CASE("[Array] Iteration and modification") {
+	Array a1 = build_array(1, 2, 3);
+	Array a2 = build_array(2, 3, 4);
+	Array a3 = build_array(1, 2, 3);
+	Array a4 = build_array(1, 2, 3);
+	a3.make_read_only();
+
+	int idx = 0;
+	for (Variant &E : a1) {
+		E = a2[idx];
+		idx++;
+	}
+
+	CHECK_EQ(a1, a2);
+
+	// Ensure read-only is respected.
+	idx = 0;
+	for (Variant &E : a3) {
+		E = a2[idx];
+	}
+
+	CHECK_EQ(a3, a4);
+
+	a1.clear();
+	a2.clear();
+	a4.clear();
+}
+
 } // namespace TestArray
 
 #endif // TEST_ARRAY_H