-
Notifications
You must be signed in to change notification settings - Fork 206
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
[Swift language feature] Implement Swift.String
wrapper in C#
#2983
[Swift language feature] Implement Swift.String
wrapper in C#
#2983
Conversation
Swift.String
wrapper in C#
I think we should have a discussion if we want to project string (which is what this PR does) or if we want to always marshal string. Personal preference would be to marshal string whenever possible (params, return value), but I guess we need the projection just like we will likely need boxes for other primitive types (mainly due to protocol conformance). |
Co-authored-by: Vitek Karas <[email protected]>
Let's collect input on this PR.
Could you provide more details?
Yes + it is lowered as |
More details on the personal preference: Label("Your name please:") // Assuming Label init takes a string parameter, which it doesn't, but it's a good example When projected to C# I would want to be able to write: new Label("Your name please:"); Basically I should not be forced to write things like: new Label(new SwiftString("Your name please:")); But we might be able to achieve this with implicit casts. |
Yes, this is a UX decision. The preferred Swift encoding is UTF-8: https://www.swift.org/blog/utf8-string/. Currently, we access it through ContiguousArray and use a closure to access the array’s contiguous storage. |
…runtimelab into swift-bindings/swift-string
@@ -257,3 +257,18 @@ public func sumArray(array: Array<Int32>) -> Int32 | |||
{ | |||
return array.reduce(0, +) | |||
} | |||
|
|||
public func getString() -> String |
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.
I understand this is here as it requires some swift code. But I dont think the label 'struct tests' describes it very well. Maybe we should create a new test file?
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.
Yes, sounds good.
/// <summary> | ||
/// Represents Foundation.Data type. | ||
/// </summary> | ||
public struct Data |
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.
NIT: This is used outside of string extensively right? I think we encountered pointers using that when projecting crypto kit - maybe it deserves it own file
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.
Yes, sounds good.
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.
Moved and created a tracking issue: #2992.
string? str = null; | ||
|
||
var arr = PInvoke_GetUtf8ContiguousArray(_payload); | ||
PInvoke_WithUnsafeBytes(bytes => |
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.
How much do you care about performance?
This will allocate a marshalled delegate on every call that is quite expensive operation. It would be a lot more efficient to use function pointers and use the context
argument to pass the local context.
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.
Yes, this is a good point. We should implement the correct layout and memory management for the closure context. Also, we should be able to reduce the number of calls using a thin wrapper.
Created a tracking issue: #2990
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.
Doing this properly using function pointers is like 10-line edit on the new code added in this PR...
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.
Ok, let's fix that in this PR.
} | ||
} | ||
|
||
public unsafe delegate IntPtr CallbackDelegate(IntPtr param); |
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.
Should the callback have default calling convention or Swift calling convention? (Related to my other comment about function pointers.)
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.
Also, should the callback signature have the closure context
as the argument?
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.
Yes.
ToString
has been refactored to use function pointers. The length
is passed through the closure context to the callback via the self register. A utf8 buffer is allocated, and the content of the contiguous array is copied into it. The buffer is then marshalled using PtrToStringUTF8
, and the native buffer is deallocated.
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.
This is a better way to do it without an extra copy
Co-authored-by: Jan Kotas <[email protected]>
Co-authored-by: Jan Kotas <[email protected]>
Changed |
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.
Thanks!
@jkotas Thanks for help! If you have any additional feedback regarding the function pointers, please let me know and we can address them in a follow-up. |
That's just an annoying C# warning. It is fine to disable it. We have it disabled globally in dotnet/runtime. We use the pattern that I have suggested in many places. One example from many: https://github.com/dotnet/runtime/blob/c6fd0543b67aec813b16fc9458afd2ec2716178b/src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Nls.cs#L370-L403 There is nothing wrong with taking an address of a struct on stack that contains object references. You just need to be careful about what you are doing - like you need to be careful everywhere else in unsafe code. GCHandle and the reference type instead of struct are just a bunch of unnecessary overhead. |
Description
This PR projects
Swift.String
used in products(for:).In Swift, String is defined as
frozen struct String
with an internal payload ofSwift.Data
. The projection is a thin C# wrapper aroundSwift.Data
, with methods to convert betweenUTF-8
andUTF-16
encodings.Out of Scope
Swift.String
.Fixes #2831