3

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

  • There are quite a few interesting things going on here... What problems did you encounter while trying various approaches? – Anatoli P May 31 '18 at 01:02
  • 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?>!'. – George davies May 31 '18 at 20:21
  • 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 https://stackoverflow.com/help/mcve. In addition to the resources I mentioned in my answer, you might also want to carefully read through https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/#//apple_ref/doc/uid/TP40014216-CH2-ID0. By now have you been able to read/write var2 and var3? Is var1 the only remaining problem? – Anatoli P Jun 01 '18 at 00:27
  • 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?>!'. 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 Jun 01 '18 at 17:37
  • @AnatoliP : edited question with all attempts and type of error returned. – George davies Jun 01 '18 at 17:50
  • good point about the title focusing on `char**`, which would be `var1`. In cases like this I would suggest just narrowing down the question to `var1`, so there is less of a chance of misunderstanding. I'll play around with `var1`, should be doable. – Anatoli P Jun 02 '18 at 00:28
  • @AnatoliP : Sure looking forward to it. – George davies Jun 05 '18 at 20:07
  • @AnatoliP : thank you for taking time to implement the functionality. The following code assignment was successful, though passing this structure to other functions which will access the parameters say one of the strings describing the list of var1 strings is NULL. Can you make sense of it. Really appreciate the effort! – George davies Jun 05 '18 at 20:46
  • Sorry, @George, I don't know the library you are trying to use and there are too many possibilities to even try to speculate. You may want to come up with a very brief specific example that shows the problem and ask a new question. That way you are more likely to get more assistance faster. – Anatoli P Jun 06 '18 at 00:17
  • @AnatoliP : The library is very simple/operational. It's just making sure that list does not contain empty string. I believe it might be the pointer may not be correctly pointing to string or if it's correctly mapped, We need to make sure it does not contains empty string. I've accepted your answer, I hope to get to bottom of this, let me know if you've any thoughts! – George davies Jun 06 '18 at 21:19
  • is the problem a NULL pointer or an empty string? Anyway, you may want to do some debugging and once you can narrow down the problem(s) so that you can write a simple code example to reproduce them, post it as part of a new question. Or maybe pose separate questions if there are several distinct problems. – Anatoli P Jun 07 '18 at 02:05

1 Answers1

0

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.

Anatoli P
  • 4,791
  • 1
  • 18
  • 22