6

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

const char *cmd[] = {"name1", "value1", NULL};
command(cmd);

In Swift, the API was imported like

func command(_ args: UnsafeMutablePointer<UnsafePointer<Int8>?>!)

After trying hundreds of times using type casting or unsafeAddress(of:) I still cannot get this work. Even though I pass a valid pointer that passed compilation, it crashes at runtime saying invalid memory access (at strlen function). Or maybe it's something about ARC?

let array = ["name1", "value1", nil]

// ???
// args: UnsafeMutablePointer<UnsafePointer<Int8>?>

command(args)
1

2 Answers 2

10

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)) }
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks! Works like a charm :]
while invoking command API i'm getting error...Use of unresolved identifier 'command'; did you mean 'doCommand'?
@Sunilaruru: The question was about calling an existing C function command.
var tool = "/bin/rm" 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)) } var pipe: FILE? = nil status = AuthorizationExecuteWithPrivileges(authRef!, tool, authFlags, cargs, &pipe); is it ok?
0

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
        }
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.