Some C functions expect a char **
argument, like int main(int argc, char **argv)
. While Swift can automatically handle Char *
, it doesn't handle char **
. What's the best way to call such a function?
Asked
Active
Viewed 50 times
0

Revanth Kausikan
- 673
- 1
- 9
- 21

RopeySim
- 423
- 4
- 11
1 Answers
0
There are several "withXXX" methods around, but these require closures and get messy when there are multiple arrays. Still others use a separate "dealloc" function you must call when done. This goes against the Swift way. However, using a class just to get a deinit opportunity is wrong.
This class provides a pointer that works with char** and automatically deallocates the memory, even though it's a struct (using a little trick with a mapped data with deallocator).
public struct CStringArray {
public let pointer: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>
public let count: Int
private var data: Data
public init(_ array: [String]) {
let count = array.count
// Allocate memory to hold the CStrings and a terminating nil
let pointer = UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>.allocate(capacity: count + 1)
pointer.initialize(repeating: nil, count: count + 1) // Implicit terminating nil at the end of the array
// Populate the allocated memory with pointers to CStrings
for (e, s) in array.enumerated() {
pointer[e] = strdup(s)
}
// This uses the deallocator available on the data structure as a solution to the fact that structs do not have `deinit`
self.data = Data(bytesNoCopy: pointer, count: MemoryLayout<UnsafeMutablePointer<CChar>>.size * count, deallocator: .custom({_,_ in
for i in 0...count - 1 {
free(pointer[i])
}
pointer.deallocate()
}))
self.pointer = pointer
self.count = array.count
}
public subscript(index: Data.Index) -> UnsafeMutablePointer<CChar>? {
get {
precondition(index >= 0 && index < count, "Index out of range")
return pointer[index]
}
}
public subscript(index: Data.Index) -> String? {
get {
precondition(index >= 0 && index < count, "Index out of range")
if let pointee = pointer[index] {
return String(cString: pointee)
}
return nil
}
}
}

RopeySim
- 423
- 4
- 11
-
"goes against Swift" is very subjective. And it is really contrasting with the fact that you are using `array.forEach` with a manual offset, instead of `enumerated`. – Sulthan Apr 05 '20 at 09:15