[swift-users] Ugliness bridging Swift String to char *
Andrew Trick
atrick at apple.com
Mon Mar 6 15:09:27 CST 2017
> On Mar 1, 2017, at 7:08 PM, Kenny Leung via swift-users <swift-users at swift.org> wrote:
>
> Hi Jordan.
>
> Thanks for the lengthy answer.
>
>> On Mar 1, 2017, at 6:21 PM, Jordan Rose <jordan_rose at apple.com <mailto:jordan_rose at apple.com>> wrote:
>>
>> Hey, Kenny. The const vs non-const part is important, since both the implicit conversion and cString(using:) are allowed to return a pointer to the internal data being used by the String, and modifying that would be breaking the rules (and could potentially cause a crash). In the case of 'fieldSep', it's unlikely that anyone is going to mutate the contents of the string; it was probably just the original author (you?) not bothering to be const-correct. If you control this struct, a better fix would be to use 'const char *' for the field.
>
> This is the PostgreSQL client library, so I don’t really want to change it. (Although the source is available. Maybe I should submit a patch…)
>
>> That said, that's not the main isuse. The implicit conversion from String to UnsafePointer<CChar> is only valid when the string is used as a function argument, because the conversion might need to allocate temporary storage. In that case, it’s important to know when it’s safe to deallocate that storage. For a function call, that’s when the call returns, but for storing into a struct field it’s completely unbounded. So there’s no implicit conversion there.
>>
>> If you’re willing to limit your use of the pointer value to a single block of code, you can use withCString <https://developer.apple.com/reference/swift/string/1538904-withcstring>:
>>
>> myString.withCString {
>> var opt :PQprintOpt = PQprintOpt()
>> opt.fieldSep = UnsafeMutablePointer(mutating: $0)
>> // use 'opt'
>> }
>
> Unfortunately, this is not always an option, since there are multiple char * files in PQprintOpt. Or could I just nest .withCString calls? Looks ugly, but might work, I guess.
Here are some basic recommendations for converting between String and C string representations:
https://swift.org/migration-guide/se-0107-migrate.html#common-use-cases
The best ways to convert Swift to C strings are:
1. Pass the Swift string as a function argument of type
Unsafe[Mutable]Pointer<Int8>.
2. Use `String.withCString` to create a block of Swift code that can
access the C string.
That doesn't help you if you want to store a pointer to the CString in a property without defining its scope. In that case, you just need to explicitly copy the string.
I like Jordan's recommendation for calling strdup. That's the canonical way of creating a new C string with its own lifetime.
Guillaume's explanation with withMemoryRebound(to:) is also correct, if you want to work at that level.
We probably should provide an API that handles the special case of String literals so you don't need to copy. As Jordan suggested, that we could simply add a `cstring` property to StaticString. Feel free to file a bug for that so it isn't forgotten.
-Andy
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-users/attachments/20170306/aa4a5dd4/attachment.html>
More information about the swift-users
mailing list