Son of a Beach
Son of a Beach

Reputation: 1779

Swift conversion of Array of Strings to C array of C Strings not working

The C function I wish to call is documented as:

GDALVectorTranslateOptions* GDALVectorTranslateOptionsNew(char** papszArgv, GDALVectorTranslateOptionsForBinary* psOptionsForBinary)

...and is imported to swift as:

public func GDALVectorTranslateOptionsNew(_ papszArgv: UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>!, _ psOptionsForBinary: OpaquePointer!) -> OpaquePointer!

I have been unable to convert my swift array of strings into a C array of C strings in such a way that does not produce an error.

I have tried various methods found during research, and they all end up with similar errors being produced. My current method is to use the following function to convert the Swift array of strings to be used within a closure:

public func withArrayOfCStrings<R>(_ args: [String], _ body: ([UnsafeMutablePointer<CChar>?]) -> R) -> R {
    var cStrings = args.map { strdup($0) }
    cStrings.append(nil)
    
    defer {
        cStrings.forEach { free($0) }
    }
    
    return body(cStrings)
}

I'm attempting to use it all like this:

    var opts = ["blah1", "blah2"]
        
    withArrayOfCStrings(opts) { (cOpts) in
        let translateOpts = GDALVectorTranslateOptionsNew(cOpts, nil)
    }

But I get the error:

Cannot convert value of type '[UnsafeMutablePointer?]' (aka 'Array<Optional<UnsafeMutablePointer>>') to expected argument type 'UnsafeMutablePointer<UnsafeMutablePointer?>?'

How should I be doing this?

Upvotes: 1

Views: 165

Answers (1)

Martin R
Martin R

Reputation: 539685

That withArrayOfCStrings function works with C functions taking a char * const * argument, i.e. a pointer to constant pointers to characters.

If you change the C declaration to

GDALVectorTranslateOptions* GDALVectorTranslateOptionsNew(char* const* papszArgv,
    GDALVectorTranslateOptionsForBinary* psOptionsForBinary)

then it compiles and runs as expected.

If you do not have the option to modify the C code then you can change the helper method to

public func withArrayOfCStrings<R>(_ args: [String],
                                    _ body: (UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>) -> R) -> R {
    var cStrings = args.map { strdup($0) }
    cStrings.append(nil)
    
    defer {
        cStrings.forEach { free($0) }
    }
    
    return body(&cStrings)
}

The body is now called with a UnsafeMutablePointer<UnsafeMutablePointer<Int8>?> argument, which corresponds to a C char ** argument.

Upvotes: 1

Related Questions