How to pass an array of Swift strings to a C function taking a char ** parameter
Solution 1:
The C function
int initialize(int argc, char **argv);
is mapped to Swift as
func initialize(argc: Int32, argv: UnsafeMutablePointer<UnsafeMutablePointer<Int8>>) -> Int32
This is a possible solution:
let args = ["-c", "1.2.3.4", "-p", "8000"]
// Create [UnsafeMutablePointer<Int8>]:
var cargs = args.map { strdup($0) }
// Call C function:
let result = initialize(Int32(args.count), &cargs)
// Free the duplicated strings:
for ptr in cargs { free(ptr) }
It uses the fact that in strdup($0)
the Swift string $0
is automatically converted to a C string,
as explained in String value to UnsafePointer<UInt8> function parameter behavior.
Solution 2:
Building on Martin’s answer, if you find yourself doing this a lot, you could wrap the dup/free part into a function in a similar style to String.withCString
:
import Darwin
func withCStrings
<R, S: SequenceType where S.Generator.Element == String>
(strings: S, @noescape body: (UnsafeBufferPointer<UnsafeMutablePointer<Int8>>) -> R)
-> R {
let cstrings = map(strings) { strdup($0) } + [nil]
let result = cstrings.withUnsafeBufferPointer(body)
for ptr in cstrings { free(ptr) }
return result
}
let execvargs = ["/usr/bin/say"] + dropFirst(Process.arguments)
let execvresult = withCStrings(execvargs) {
execv($0[0], $0.baseAddress)
}