Convert a Swift Array of String to a to a C string array pointer

前端 未结 2 973
走了就别回头了
走了就别回头了 2020-12-04 01:42

I\'m on Swift 3, and I need to interact with an C API, which accepts a NULL-terminated list of strings, for example

con         


        
相关标签:
2条回答
  • 2020-12-04 02:22

    You can proceed similarly as in How to pass an array of Swift strings to a C function taking a char ** parameter. It is a bit different because of the different const-ness of the argument array, and because there is a terminating nil (which must not be passed to strdup()).

    This is how it should work:

    let array: [String?] = ["name1", "name2", nil]
    
    // Create [UnsafePointer<Int8>]:
    var cargs = array.map { $0.flatMap { UnsafePointer<Int8>(strdup($0)) } }
    // Call C function:
    let result = command(&cargs)
    // Free the duplicated strings:
    for ptr in cargs { free(UnsafeMutablePointer(mutating: ptr)) }
    
    0 讨论(0)
  • 2020-12-04 02:48

    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
            var e = 0
            array.forEach {
                pointer[e] = strdup($0)
                e += 1
            }
    
            // 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
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题