5

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?

brodney
  • 1,176
  • 2
  • 14
  • 29

3 Answers3

3

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)
Jeffery Thomas
  • 42,202
  • 8
  • 92
  • 117
  • This works, thanks! But it does not (no longer?) requires optional unwrapping. The signature on cStringUsingEncoding is `func cStringUsingEncoding(encoding: UInt) -> UnsafePointer`, and strdup just converts it to UnsafeMutablePointer. – brodney Dec 31 '14 at 03:11
  • @brodney heh, I'm using Xcode 6.1.1 tools (6A2008a): the signature is `func cStringUsingEncoding(encoding: NSStringEncoding) -> [CChar]?` which downgrades to `UnsafePointer?` – Jeffery Thomas Dec 31 '14 at 04:23
  • Using strdup, you will have to free the buffer. defer { free(cString) } – Romain TAILLANDIER Dec 12 '16 at 09:36
  • @RomainTAILLANDIER Thanks for the assist. – Jeffery Thomas Dec 12 '16 at 11:18
1

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.

  • Excellent ! You greatly help me with that. I had many context lost because of converting String to UnsafeMutablePointer and back. strdup looks so dirty in swift, and not easy to dealloc (and where to dealloc). TK – Romain TAILLANDIER Dec 12 '16 at 09:41
0

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
    }
clearlight
  • 12,255
  • 11
  • 57
  • 75