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
  • 7,444
  • 9
  • 50
  • 74

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
  • 529,903
  • 94
  • 1,240
  • 1,382