1

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?

1 Answer 1

1

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.

Sign up to request clarification or add additional context in comments.

1 Comment

I can't alter the C code, but that updated withArrayOfCString looks like it will do the trick. I haven't finished writing enough other code to fully test it, but at least XCode is no longer complaining about type conversion. I think there were just too many levels of nesting and indirection for me to follow.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.