Convert String to UnsafeMutablePointer<char_t> in Swift

2.6k Views Asked by At

I'm working with a third party c API I'm trying to call one of the functions with a simple string. Something like this:

some_c_func("aString");

I get a build error:

Type 'UnsafeMutablePointer<char_t>' does not conform to protocol 'StringLiteralConvertible'

I've seen some suggestions to use utf8 on String or similar conversions, which gets nearly there, but with the following error:

some_c_func("aString".cStringUsingEncoding(NSUTF8StringEncoding));
'UnsafePointer<Int8>' is not convertible to 'UnsafeMutablePointer<char_t>'

How can I create an UnsafeMutablePointer?

3

There are 3 best solutions below

4
Jeffery Thomas On BEST ANSWER

It all depends on what char_t is.

If char_t converts to Int8 then the following will work.

if let cString = str.cStringUsingEncoding(NSUTF8StringEncoding) {
    some_c_func(strdup(cString))
}

This can be collapsed to

some_c_func(strdup(str.cStringUsingEncoding(NSUTF8StringEncoding)!))

WARNING! This second method will cause a crash if func cStringUsingEncoding(_:) returns nil.


Updating for Swift 3, and to fix memory leak

If the C string is only needed in a local scope, then no strdup() is needed.

guard let cString = str.cString(using: .utf8) else {
    return
}

some_c_func(cString)

cString will have the same memory lifecycle as str (well similar at least).

If the C string needs to live outside the local scope, then you will need a copy. That copy will need to be freed.

guard let interimString = str.cString(using: .utf8), let cString = strdup(interimString) else {
    return
}

some_c_func(cString)

//…

free(cString)
0
clearlight On

I haven't tried passing strings like that, but I have a C function that I call from Swift, that takes a lot more parameters than shown here, among which is a reference to a Swift C typecast buffer to hold an error string. The compiler doesn't complain and the function call works. Hopefully this will steer you closer to the answer and you can provide an update with the final answer or someone else can.

    var err = [CChar](count: 256, repeatedValue: 0)
    var rv = somefunc((UnsafeMutablePointer<Int8>)(err))

    if (rv < 0) {
        println("Error \(err)")
        return
    }
1
rewindustry On

it may be simpler than that - many C APIs pass strings around as char * types, and swift treats these as unsafe.

try updating the C API (good) or hack it's header files (bad) to declare these as const char * instead.

in my experience this allows you to pass standard swift String types directly to the C API.

apparently a constant is required, in order to conform to the protocol.