String module error in gdextension

Godot Version

4.3

Question

I found that the module_Variant method for the String type in GDExtension did not return the correct result. They all returned an incorrect result <null>. Could this be a bug? I haven’t found any posts mentioning a similar issue.

I tried the following code. The output on the left is correct, while the result on the right is incorrect.

		Variant iv = (int64_t) 10;
		Variant dv = 99.9;
		Variant bv = true;
		GD::print(iv," ",String("%s") % (int64_t)10, "  ", String("%s") % iv);
		GD::print(dv," ",String("%s") % 99.9, "  ", String("%s") % dv);
		GD::print(bv," ",String("%s") % true, "  ", String("%s") % bv);
10 10  <null>
99.9 99.9  <null>
true true  <null>

They each used different overloaded operators, as shown below.

[incorrect]
String operator%(const Variant &p_other) const;
[correct]
String operator%(bool p_other) const;
String operator%(int64_t p_other) const;
String operator%(double p_other) const;
source_code
String String::operator%(const Variant &p_other) const {
	return internal::_call_builtin_operator_ptr<String>(_method_bindings.operator_module_Variant, (GDExtensionConstTypePtr)&opaque, (GDExtensionConstTypePtr)&p_other);
}
String String::operator%(bool p_other) const {
	int8_t p_other_encoded;
	PtrToArg<bool>::encode(p_other, &p_other_encoded);
	return internal::_call_builtin_operator_ptr<String>(_method_bindings.operator_module_bool, (GDExtensionConstTypePtr)&opaque, (GDExtensionConstTypePtr)&p_other_encoded);
}

String String::operator%(int64_t p_other) const {
	int64_t p_other_encoded;
	PtrToArg<int64_t>::encode(p_other, &p_other_encoded);
	return internal::_call_builtin_operator_ptr<String>(_method_bindings.operator_module_int, (GDExtensionConstTypePtr)&opaque, (GDExtensionConstTypePtr)&p_other_encoded);
}

String String::operator%(double p_other) const {
	double p_other_encoded;
	PtrToArg<double>::encode(p_other, &p_other_encoded);
	return internal::_call_builtin_operator_ptr<String>(_method_bindings.operator_module_float, (GDExtensionConstTypePtr)&opaque, (GDExtensionConstTypePtr)&p_other_encoded);
}

So, I had to resort to the most cumbersome way, using a switch statement to solve the issue where the Variant type couldn’t be handled correctly.

Solution
switch (p_value.get_type())
		{
		case Variant::NIL:
			s = str % nullptr;
			break;
		case Variant::BOOL:
			s = str % bool(p_value);
			break;
		case Variant::INT:
			s = str % int64_t(p_value);
			break;
		case Variant::FLOAT:
			s = str % double(p_value);
			break;
		case Variant::STRING:
			s = str % String(p_value);
			break;
		case Variant::VECTOR2:
			s = str % Vector2(p_value);
			break;
		case Variant::VECTOR2I:
			s = str % Vector2i(p_value);
			break;
		case Variant::RECT2:
			s = str % Rect2(p_value);
			break;
		case Variant::RECT2I:
			s = str % Rect2i(p_value);
			break;
		case Variant::VECTOR3:
			s = str % Vector3(p_value);
			break;
		case Variant::VECTOR3I:
			s = str % Vector3i(p_value);
			break;
		case Variant::TRANSFORM2D:
			s = str % Transform2D(p_value);
			break;
		case Variant::VECTOR4:
			s = str % Vector4(p_value);
			break;
		case Variant::VECTOR4I:
			s = str % Vector4i(p_value);
			break;
		case Variant::PLANE:
			s = str % Plane(p_value);
			break;
		case Variant::QUATERNION:
			s = str % Quaternion(p_value);
			break;
		case Variant::AABB:
			s = str % AABB(p_value);
			break;
		case Variant::BASIS:
			s = str % Basis(p_value);
			break;
		case Variant::TRANSFORM3D:
			s = str % Transform3D(p_value);
			break;
		case Variant::PROJECTION:
			s = str % Projection(p_value);
			break;
		case Variant::COLOR:
			s = str % Color(p_value);
			break;
		case Variant::STRING_NAME:
			s = str % StringName(p_value);
			break;
		case Variant::NODE_PATH:
			s = str % NodePath(p_value);
			break;
		case Variant::RID:
			s = str % RID(p_value);
			break;
		case Variant::OBJECT:
		{
			Object *obj = p_value;
			s = str % obj;
			break;
		}
		case Variant::CALLABLE:
			s = str % Callable(p_value);
			break;
		case Variant::SIGNAL:
			s = str % Signal(p_value);
			break;
		case Variant::DICTIONARY:
			s = str % Dictionary(p_value);
			break;
		case Variant::ARRAY:
			s = str % Array(p_value);
			break;
		case Variant::PACKED_BYTE_ARRAY:
			s = str % PackedByteArray(p_value);
			break;
		case Variant::PACKED_INT32_ARRAY:
			s = str % PackedInt32Array(p_value);
			break;
		case Variant::PACKED_INT64_ARRAY:
			s = str % PackedInt64Array(p_value);
			break;
		case Variant::PACKED_FLOAT32_ARRAY:
			s = str % PackedFloat32Array(p_value);
			break;
		case Variant::PACKED_FLOAT64_ARRAY:
			s = str % PackedFloat64Array(p_value);
			break;
		case Variant::PACKED_STRING_ARRAY:
			s = str % PackedStringArray(p_value);
			break;
		case Variant::PACKED_VECTOR2_ARRAY:
			s = str % PackedVector2Array(p_value);
			break;
		case Variant::PACKED_VECTOR3_ARRAY:
			s = str % PackedVector3Array(p_value);
			break;
		case Variant::PACKED_COLOR_ARRAY:
			s = str % PackedColorArray(p_value);
			break;
		case Variant::PACKED_VECTOR4_ARRAY:
			s = str % PackedVector4Array(p_value);
			break;
		default:
			s = str % p_value;
			break;
		}