C++ How to access Packed Array from Variant

Godot Version

4.3

Question

Say I have a Variant object in C++ that is of type PACKED_VECTOR2_ARRAY. How do I access this packed array without making a copy of it? It seems that the internal PackedArrayRefBase *packed_array of Variant counts references and holds a pointer to the Vector<T> object, so I expect that I should be able to access and modify the packed array as a reference instead of a full copy.

I see that Variant::_data is private, so I can’t access the PackedArrayRefBase* directly. Also, simply assigning the Variant object to a PackedVector2Array creates a copy rather than giving me a reference.

Here is some code that demonstrates the what I’m struggling with:

PackedVector2Array packedArray;
packedArray.push_back(Vector2(1, 1));

TypedArray<PackedVector2Array> parentArray;
parentArray.push_back(packedArray);

Variant variant = parentArray.get(0);
WARN_PRINT(Variant::get_type_name(variant.get_type())); // prints PackedVector2Array

PackedVector2Array packedArrayCopy = variant;
packedArrayCopy.insert(0, Vector2(2, 2));

PackedVector2Array packedArrayCopy2 = variant;
packedArrayCopy2.insert(0, Vector2(3, 3));

WARN_PRINT(packedArray[0]); // Prints (1, 1)
WARN_PRINT(packedArrayCopy[0]); // Prints (2, 2)
WARN_PRINT(packedArrayCopy2[0]); // Prints (3, 3)

I want the last three lines to all print (3, 3) instead of each being different copies.

As another example, this is trivial in GDScript. The same code, but written in GDScript, will print (3, 3) for all cases:

var packedArray: PackedVector2Array
packedArray.push_back(Vector2(1, 1))

var parentArray: Array[PackedVector2Array]
parentArray.push_back(packedArray)

var variant: Variant = parentArray[0]
print(type_string(typeof(variant))) # Prints PackedVector2Array

var packedArrayCopy: PackedVector2Array = variant
packedArrayCopy.insert(0, Vector2(2, 2))

var packedArrayCopy2: PackedVector2Array = variant
packedArrayCopy2.insert(0, Vector2(3, 3))

print(packedArray[0]); # Prints (3, 3)
print(packedArrayCopy[0]) # Prints (3, 3)
print(packedArrayCopy2[0]) # Prints (3, 3)

GDscript equivalent of your C++ code works like this

var packedArray: PackedVector2Array
packedArray.push_back(Vector2(1, 1))

var parentArray: Array[PackedVector2Array]
parentArray.push_back(packedArray)

var variant: Variant = parentArray[0]
print(type_string(typeof(variant))) # Prints PackedVector2Array

var packedArrayCopy: PackedVector2Array = PackedVector2Array(variant)
packedArrayCopy.insert(0, Vector2(2, 2))

var packedArrayCopy2: PackedVector2Array = PackedVector2Array(variant)
packedArrayCopy2.insert(0, Vector2(3, 3))

print(packedArray[0]); # Prints (1, 1)
print(packedArrayCopy[0]) # Prints (2, 2)
print(packedArrayCopy2[0]) # Prints (3, 3)

I’m not good with c++, may be need to cast variant, like

PackedVector2Array packedArrayCopy = Object::cast_to<PackedVector2Array >(variant)

and it posible will not use constructor PackedVector2Array(from:PackedVector2Array ) which create a copy

try it.

		Variant v = PackedVector2Array();
		v.call("push_back", Vector2(1, 1));
		v.call("push_back", Vector2(1, 2));
		GD::print(v);
1 Like

OK, this is a helpful idea, @VeryUnHappyMan: keeping the PackedVector2Array inside of the Variant object means I will always be interfacing with a reference to the underlying PackedVector2Array instead of a copy. Using Variant::call and related methods allows me to interface with the PackedVector2Array in a similar way to GDScript:

PackedVector2Array packedArray;
packedArray.push_back(Vector2(1, 1));

TypedArray<PackedVector2Array> parentArray;
parentArray.push_back(packedArray);

Variant variant = parentArray[0];
WARN_PRINT(Variant::get_type_name(variant.get_type())); // prints PackedVector2Array
variant.call("insert", 0, Vector2(2, 2));

Variant variant2 = parentArray[0];
WARN_PRINT(Variant::get_type_name(variant2.get_type())); // prints PackedVector2Array
variant2.call("insert", 0, Vector2(3, 3));

WARN_PRINT(parentArray[0]); // Prints [(3, 3), (2, 2), (1, 1)]
WARN_PRINT(variant); // Prints [(3, 3), (2, 2), (1, 1)]
WARN_PRINT(variant2); // Prints [(3, 3), (2, 2), (1, 1)]

And then to iterate over this PackedVector2Array, I can use the Variant::iter_ functions, something like this:

Variant iterator;
bool valid;
variant.iter_init(iterator, valid);
if (valid) {
	do {
		Variant value = variant.iter_get(iterator, valid);
		if (valid) {
		}
		WARN_PRINT(value); // Prints (3, 3), (2, 2), and (1, 1)
	} while (variant.iter_next(iterator, valid));
}
1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.