-
-
Notifications
You must be signed in to change notification settings - Fork 21.3k
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
Expose String.resize()
#79177
Expose String.resize()
#79177
Conversation
a2e7f19
to
aa71a6c
Compare
I guess it makes sense to expose size as well. |
How does length/size work with multi-byte characters? I assume size() counts raw bytes, right? |
<return type="int" /> | ||
<param index="0" name="size" type="int" /> | ||
<description> | ||
Resizes this string to the new size. The size should be one more than desired string length (in order to accommodate the null terminator). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't this make the String data mutable?
In most programming languages, Strings are immutable. I think this is true for all String methods in GDScript except for this one. Wouldn't it be better to return a copy that's resized?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In GDScript, you can already do:
var s = "abc"
s[2] = 3
print(s) # will print "ab3"
This was one of the points that @bruvzg brought up on my original PR (which aimed expose this only to GDExtension and not GDScript).
Wouldn't it be better to return a copy that's resized?
Unfortunately, this would defeat the purpose of having access to resize()
from GDExtension. The specific use-case that triggered the quest to expose resize was to avoid an unnecessary copy in text_server_adv when compiling it as a GDExtension.
String stores its data as an array of |
Strings have to be immutable in GDScript AFAIK, as they are COW you can't modify them via methods but only by assignment, this is why there aren't any mutable methods to them (and why several mutable methods were removed previously, like |
Strings aren't immutable, there's an |
My bad, not sure what has changed in core to make that possible |
I think it was always the case, at least it is in 3.5. |
Does this actually work for properties though? That's the issue with mutability, not that variant of it can be mutated Test for example: class TestClass:
var s0 := "abc"
func foo():
var t0 := TestClass.new()
print(t0.s0) # Prints "abc"
t0.s0.resize(3)
print(t0.s0) # Still prints "abc", not "ab" |
This is why some methods were removed from This also affects arrays, like: var arr := ["abc"]
print(arr[0])
arr[0].resize(3)
print(arr[0]) |
Yes and no, it will change the value of a variable in the object, but won't trigger any extra code in the setter function (same is true for any type which doesn't have |
It won't change the value though, see the above code, I've verified that, and this is an established limitation The only reason the [] operator works is because it uses a proxy See: #70995 |
Well, if it's not desired and there are issues with GDScript (seems like a GDScript bug to me), I guess it's better to use #79156 instead. |
Maybe it should not be exposed to scripting in this case? |
Agreed, but exposing a method that is confusing in GDScript shouldn't be done, imo even with a warning it'll be annoying Unsure how to expose resize just for GDExtension, unless you mean |
I agree that exposing Also I don't think we should expose |
That still won't work correctly unless it's returning a string by copy instead of mutating |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The resize
method, other than the mutability issues, needs to be implemented so it handles null termination correctly, the current version creates defective strings, test:
var s0 := "abcd"
var s1 := "ab"
s0.resize(3)
print(hash(s0))
print(hash(s1))
These will print different results
Haven't tested but this implies that there are some concerns with how accessing the raw string will work
In fact you will notice that the hash changes each time you run (more or less), implying it keeps going after the end of the string, this likely risks crashing if it doesn't catch a zero correctly, and if you see the hash
function of String
you'll see it uses null check to end the hash. This is because in this case "abcd\0" becomes "abc" instead of "ab\0"
Edit: It also breaks if you change the size to be larger, as it doesn't ensure zeros
It is also impossible to manually null-terminate strings that have been shortened, you can't do:
a.resize(2)
a[2] = ""
Though you can manually terminate strings that have been lengthened, though you still can't terminate them at the very end (where there's no null to begin with, as it is not zeroed)
Thanks, Everyone, for all the great discussion and insight! I'm a little confused about the conclusion, though. Is the consensus that we should return to #79156? I wouldn't mind, I personally liked that approach better anyway. :-) Or, that we should make a better |
It wouldn't be possible to do a "modify in place" For GDExtension it depends on the usecase if it should do dedicated null handling IMO, the limitation here is that from GDScript it's cumbersome and weird to handle nulls internally without having them automatically (see above for the actually impossible situations), but GDExtension might be closer to bare metal to have the performance critical version It would probably not be good to have the default method do any zeroing, as it would likely be expected to insert null elsewhere |
Alright, I'll re-open PR #79156 and close this one! |
This supersedes PR #79156 (which didn't expose
String.resize()
the normal way, but instead added a function togdextension_interface.h
so it wouldn't be exposed to GDScript)The one thing I'm concerned about (now that this would be exposed to GDScript) is that
resize()
takes the array size of the string (so, including the null terminator character), butlength()
returns the string length (so, without the null terminator aka 1 less than the array length).Will this be too confusing to users? Would it make sense to also expose
size()
so thatstr.resize(str.size() + 2)
clearly makes the string 2 characters longer? Or, will having bothsize()
andlength()
just be even more confusing? Or, should the version ofString.resize()
that's exposed be made to work with string length, rather than array length?Fixes godot-cpp issue godotengine/godot-cpp#1141 which originally came up when trying to compile the text_server_adv module in Godot as a GDExtension. See: #77532