1

I'm calling a C library from Swift 4 and I have troubles converting a [String] to const char *[].

The C API defines this method:

int getDREFs(const char* drefs[], unsigned char count);

which is exposed in Swift as

public func getDREFs(_ drefs: UnsafeMutablePointer<UnsafePointer<Int8>?>!, _ count: UInt8) -> Int32

The Swift wrapper I'm trying to write is the following

public func get(drefs: [String]) throws {

    var cDrefs = [UnsafePointer<Int8>]()
    for dref in drefs {
        cDrefs.append(dref.cString(using: .utf8)!)
    }
    let pDrefs = UnsafeMutablePointer<UnsafePointer<Int8>>(&cDrefs)
    getDREFFs(pDrefs, drefs.count)

}

but the error I get is

Cannot convert value of type 'UnsafeMutablePointer<UnsafePointer<Int8>>' to expected argument type 'UnsafeMutablePointer<UnsafePointer<Int8>?>!'

what am I missing?

Jan
  • 6,948
  • 8
  • 46
  • 65

1 Answers1

2

getDREFSs expects a pointer to an array of optional Int8 pointers. Also the second argument must be converted to UInt8.

So this would compile:

public func get(drefs: [String]) -> Int {
    var cDrefs = [UnsafePointer<Int8>?]()
    for dref in drefs {
        cDrefs.append(dref.cString(using: .utf8))
    }
    let result = getDREFs(&cDrefs, UInt8(drefs.count))
    return Int(result)
}

But a quick test shows that is does not work if called with multiple strings. The reason is that the arrays returned by dref.cString(using: .utf8) can already be deallocated (and the pointer invalid) when the C function is called.

Here is a working version, a slight modification of Convert a Swift Array of String to a to a C string array pointer for this particular case:

public func get(drefs: [String]) -> Int {
    var cargs = drefs.map { UnsafePointer<Int8>(strdup($0)) }
    let result = getDREFs(&cargs, UInt8(drefs.count))
    for ptr in cargs { free(UnsafeMutablePointer(mutating: ptr)) }
    return Int(result)
}
Martin R
  • 510,973
  • 84
  • 1,183
  • 1,314