Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bind Array and Packed*Array get and set functions #95930

Merged
merged 1 commit into from
Sep 27, 2024

Conversation

aaronfranke
Copy link
Member

@aaronfranke aaronfranke commented Aug 22, 2024

These must be bound to ensure feature parity between both Array and Packed*Array, in-engine and GDExtension.

Current situation:

In Engine GDExtension & scripting
Array [] Get
Array [] Set
Array Func Get
Array Func Set
Packed*Arrays [] Get
Packed*Arrays [] Set
Packed*Arrays Func Get
Packed*Arrays Func Set

With this PR (assuming GDExtension C++ extension API JSON is updated):

In Engine GDExtension & scripting
Array [] Get
Array [] Set
Array Func Get
Array Func Set
Packed*Arrays [] Get
Packed*Arrays [] Set
Packed*Arrays Func Get
Packed*Arrays Func Set

This means that you can safely use .get() and .set() and have it be compatible with everything. Before this PR, you would have to use a mish-mash of the [] operator and the functions in order to make it work in both places (you'd have to use [] most places but you'd have to use the function for Packed*Arrays setting). I'm labeling as an enhancement because it could be worked around before but making this consistent will help out a lot.

For Array Func Get/Set, it was a simple matter of adding two bind_method lines and then writing the docs.

For Packed*Arrays Func Get in GDExtension, I had to add an extra shim function in the bindings to resolve this ambiguity:

more than one instance of overloaded function "vc_method_call" matches the argument list:C/C++(308)
variant_call.cpp(2393, 2): function template "void vc_method_call(R (T::*method)(P...), Variant *base, const Variant **p_args, int p_argcount, Variant &r_ret, const Vector<Variant> &p_defvals, Callable::CallError &r_error)" (declared at line 56)
variant_call.cpp(2393, 2): function template "void vc_method_call(R (T::*method)(P...) const, Variant *base, const Variant **p_args, int p_argcount, Variant &r_ret, const Vector<Variant> &p_defvals, Callable::CallError &r_error)" (declared at line 61)
variant_call.cpp(2393, 2): argument types are: (<unknown-type>, Variant *, const Variant **, int, Variant, const Vector<Variant>, Callable::CallError)

For the case of Packed*Array [] Set in engine: The only value returned by [] is const so it can't be used for setting. I'm not sure if I should change this so I've left it as-is in this PR. The error it gives if you try:

no operator "=" matches these operands C/C++(349) operand types are: const Color = Color

@aaronfranke aaronfranke added this to the 4.4 milestone Aug 22, 2024
@aaronfranke aaronfranke requested review from a team as code owners August 22, 2024 03:29
@aaronfranke aaronfranke requested a review from a team August 22, 2024 03:34
Copy link
Contributor

@dsnopek dsnopek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change makes sense to me!

For the case of Packed*Array [] Set in engine: The only value returned by [] is const so it can't be used for setting. I'm not sure if I should change this so I've left it as-is in this PR.

This is an interesting case. The current pattern in Godot is to use vector.write[i] = x; to make it very explicit when we're doing copy-on-write. Is this really needed? And, if so, perhaps we should be replicating this pattern in godot-cpp? I don't know we've been accidentally triggering CoW when getting items out of a packed array in godot-cpp?

@aaronfranke
Copy link
Member Author

Actually, backporting this to 3.x may fix a bug report I opened a long time ago: #40046

@Bromeon
Copy link
Contributor

Bromeon commented Sep 16, 2024

Before this PR, you would have to use a mish-mash of the [] operator and the functions in order to make it work in both places (you'd have to use [] most places but you'd have to use the function for Packed*Arrays setting).

Could you elaborate? Why couldn't one use this operator for packed array setting?

/**
* @name packed_byte_array_operator_index
* @since 4.1
*
* Gets a pointer to a byte in a PackedByteArray.
*
* @param p_self A pointer to a PackedByteArray object.
* @param p_index The index of the byte to get.
*
* @return A pointer to the requested byte.
*/
typedef uint8_t *(*GDExtensionInterfacePackedByteArrayOperatorIndex)(GDExtensionTypePtr p_self, GDExtensionInt p_index);

And for arrays, these? Plus the equivalent const versions for getting?

/**
* @name array_operator_index
* @since 4.1
*
* Gets a pointer to a Variant in an Array.
*
* @param p_self A pointer to an Array object.
* @param p_index The index of the Variant to get.
*
* @return A pointer to the requested Variant.
*/
typedef GDExtensionVariantPtr (*GDExtensionInterfaceArrayOperatorIndex)(GDExtensionTypePtr p_self, GDExtensionInt p_index);

@aaronfranke
Copy link
Member Author

@Bromeon This code doesn't compile:

PackedInt32Array arr;
arr.resize(5);
arr[0] = 7;
error: cannot assign to return value because function 'operator[]' returns a const value
        arr[0] = 7;
        ~~~~~~ ^
./core/templates/vector.h:97:23: note: function 'operator[]' which returns const-qualified type 'const int &' declared here
        _FORCE_INLINE_ const T &operator[](Size p_index) const { return _cowdata.get(p_index); }
                             ^~~

@Bromeon
Copy link
Contributor

Bromeon commented Sep 16, 2024

But this is something to be fixed in the C++ code... where does GDExtension come into play here?

It seems both mut/const index operators are already exposed, for both packed and normal arrays?

@aaronfranke
Copy link
Member Author

@Bromeon As per the table in the OP, [] works in all cases in GDExtension, and the get/set methods work in all cases in the engine. I am trying to write code that works both in GDExtension and in the engine.

@Bromeon
Copy link
Contributor

Bromeon commented Sep 16, 2024

Sorry if I keep asking stupid questions 😬 maybe I'm missing something about the inner workings, but why can the missing get/set not be implemented via [] if that one is present? Or is it mostly because of the way how godot-cpp generates code, and it would feel more "natural" to have auto-generated get/set without special-casing it?

Because functionally, the API in GDExtension is already present, so duplicating it may raise more questions than it answers -- from the perspective of someone who writes a new binding. Or not?

@aaronfranke
Copy link
Member Author

aaronfranke commented Sep 16, 2024

@Bromeon Sorry, what do you mean by implemented? get/set are already implemented, this PR exposes it. If Godot and godot-cpp don't expose get/set, you can't call them. godot-cpp doesn't generate get/set from [].

If you mean using [] instead of get/set, the [] operator doesn't work for setting on packed arrays in engine code, as mentioned above. So you can't use [] everywhere. And without this PR, you also can't use get/set everywhere.

Anyway, the stated goal of GDExtension is to allow code to be written similarly to engine code, extending the engine. This was the whole point of rebranding it from the old name GDNative. This PR brings us closer to that goal.

@akien-mga akien-mga changed the title Bind Array and Packed*Array get and set functions Bind Array and Packed*Array get and set functions Sep 17, 2024
@dsnopek
Copy link
Contributor

dsnopek commented Sep 17, 2024

@aaronfranke I think the point that @Bromeon is trying to make, is that we don't necessarily need to bind Array's get() and set() functions in order for godot-cpp to have them, because we could manually write an implementation in godot-cpp that uses the already existing support for Array's [] operator which can both get and set values. We already do manually write some functions, so this wouldn't be totally out of place - this is something we could do. It depends on if we want to prioritize minimizing the interface or not. Personally, I could go either way.

This doesn't apply to the functions that are in GDExtension but missing Godot, though - those either need to be added to Godot or removed from godot-cpp in order to have parity.

@akien-mga
Copy link
Member

So we're happy to merge as is? Needs a rebase.

@akien-mga akien-mga merged commit 83b2ca3 into godotengine:master Sep 27, 2024
19 checks passed
@akien-mga
Copy link
Member

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants