3
votes

I'm trying to interact with an old C terminal app/library from Swift. I've successfully integrated the source code and bridged the headers from C to Swift. The code compiles and runs, I can access all functions from C - library into swift.

There is a structure in C - library which I need to initialize[Function already exists which takes pointer] and assign values to structure variables[Manually].

C-structure:

Struct args{
char ** var1;
unsigned char * var2;
char * var3;
}

and Initialization function call:

init(args * ptr);

How to call the function inside swift and assign values to var1 and var2?

1.Will following snippet successfully initialize the structure?

let Ptr = UnsafeMutablePointer<args>.allocate(capacity: 1)
var args = args()
Ptr.pointee = args
init(Ptr)

2.How to assign values to var1, var2 & var3 assuming we successfully initialize?

They are mapped as:

var1: UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>!
var2: UnsafeMutablePointer<Uint8>!
var3: UnsafeMutablePointer<Int8>!    

For example var1 = {"a", "b"}, var2 = {1,2,3} and var3 = "a"

I've tested following links and did not work:

How to pass an array of Swift strings to a C function taking a char ** parameter : gives 'inout[UnsafeMutablePointer?] to type UnsafeMutablePointer?>!' error

Convert a Swift Array of String to a to a C string array pointer : gives 'inout[UnsafeMutablePointer?] to type UnsafeMutablePointer?>!' error

No built-in support for arrays of C strings : this one needs more efforts and hoping to get easier version

github - Swift wrappers for C functions taking char** arguments : gives 'inout[UnsafeMutablePointer] to type UnsafeMutablePointer?>!' error

1
There are quite a few interesting things going on here... What problems did you encounter while trying various approaches?Anatoli P
Sure, most of the issues were related to mismatch of casting/mapping of data types. I'm still new to swift types so tried few: UnsafeMutablePointer CChar/Int8 type, ? & ! meaning, strdup for string assignment, Also checked COpaquePointer which seems to be deprecated in new version. The common error for char** assignment was 'Cannot convert x to UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>!'.George davies
Looks like you really need to study some documentation. This site is for short, specific questions/answers, not for tutorial requests and cannot be a substitute for working through technical documentation. Please see stackoverflow.com/help/mcve. In addition to the resources I mentioned in my answer, you might also want to carefully read through developer.apple.com/library/content/documentation/Swift/…. By now have you been able to read/write var2 and var3? Is var1 the only remaining problem?Anatoli P
Actually, I've been going through all posts and seen these too. My title was clear about mapping char** to respective type in swift. The summary was to explain the surroundings and get people's opinion on ways to implement. var2 & var3 successful as before, this question was mainly targeted for var1 which gives me error 'Cannot convert x to UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>!'. The reference links included have similar scope and question type, just that it did not work for me as I'm dealing with OPTIONAL type[?/!].George davies
@AnatoliP : edited question with all attempts and type of error returned.George davies

1 Answers

0
votes

This is quite a broad question, so here are some references and observations with a few examples. Hopefully these are helpful.

Please see Apple's documentation for UnsafeMutablePointer struct and also String and NSString:

https://developer.apple.com/documentation/swift/unsafemutablepointer

https://developer.apple.com/documentation/swift/string

https://developer.apple.com/documentation/foundation/nsstring

Another useful reading is Apple's docs about C and Swift interop: https://developer.apple.com/documentation/swift/imported_c_and_objective_c_apis

In this answer I'm also leaving out a lot of memory management aspects as well as things such as keeping track of the size of var1 and var2 arrays, since I don't know the specifics of your library.

Regarding the snippet for initializing the structure, you can't use the type name as the variable name and init will confuse the Swift compiler because it's reserved for naming class initializers. Let's name the variable myArgs instead of args and assume the C library initialization function is named initialize; if it's indeed init, one can easily write a wrapper named differently. Another problem with the snippet is that myArgs will remain unchanged after initialization, Ptr will actually get initialized, so you would have to use Ptr to access the initialized args structure. Thus we can omit Ptr and use implicit bridging to pass myArgs to the initialization function. The snippet becomes

var myArgs = args()
initialize(&myArgs)

Now you can access the members as follows:

// Assuming var1 is an array of at least 2 C strings.
// See Swift documentation about optionals on how to deal with 
// cases when this assumption breaks down
let s1 = String(cString: myArgs.var1[0]!)  // 1st element of var1
let s2 = String(cString: myArgs.var1[1]!)  // 2nd element of var1
myArgs.var2.pointee                   // 1st element of var2
(myArgs.var2 + 1).pointee             // 2nd element of var2
let s = String(cString: myArgs.var3)  // value of var3

Now let's set var1 to be {"aa", "bbb"}:

            var var1Buffer = 
UnsafeMutablePointer<UnsafeMutablePointer<Int8>?>.allocate(capacity: 2)
            var var1a : NSString = "aa"
            var var1b : NSString = "bbb"
            var var1aBuffer = UnsafeMutablePointer<Int8>.allocate(
        capacity: var1a.length + 1)
            var var1bBuffer = UnsafeMutablePointer<Int8>.allocate(
        capacity: var1b.length + 1)
            if (var1a.getCString(var1aBuffer, maxLength: var1a.length + 1,
    encoding: String.Encoding.utf8.rawValue)
                && var1b.getCString(var1bBuffer, maxLength: var1b.length + 1,
    encoding: String.Encoding.utf8.rawValue)) {
                var1Buffer[0] = var1aBuffer
                var1Buffer[1] = var1bBuffer
                myArgs.var1 = var1Buffer
            } else { print("Encoding failed...")}

Here is an example of setting var2 to be an array of 5 elements equal to 200:

var var2Buffer = UnsafeMutablePointer<UInt8>.allocate(capacity: 5);
var2Buffer.initialize(repeating: 200, count: 5)
myArgs.var2 = var2Buffer

And setting the value of var3:

let newVar3 : NSString = "This is new variable 3"
var var3Buffer = UnsafeMutablePointer<Int8>.allocate(capacity: newVar3.length + 1)
if (newVar3.getCString(var3Buffer, maxLength: newVar3.length + 1, encoding: String.Encoding.utf8.rawValue)) {
    myArgs.var3 = var3Buffer
} else { print("Encoding failed...") }

The above examples assume UTF8 encoding.