Copying swift string to fixed size char[][]

I have a C struct like this.

struct someStruct { 
char path[10][MAXPATHLEN];
};

I'd like to copy a list of Swift strings into the char[10][] array.

For me it's very challenging to handle two dimensional char array in Swift. Could anyone share some code which can work with Swift 5? Thanks!

Accepted Reply

Handling fixed-sized array in a C-struct is one thing Swift is not good at.


You can write something like this in Swift:

func fillStringsToSomeStruct(_ ptr: UnsafeMutablePointer<someStruct>, _ strings: [String]) {
    let pathPtr = UnsafeMutableRawPointer(ptr)
        + MemoryLayout<someStruct>.offset(of: \someStruct.path)!
    assert(strings.count == 10, "strings must have 10 elements")
    for i in 0..<10 {
        let pathN = pathPtr + i * Int(MAXPATHLEN)
        strlcpy(pathN.assumingMemoryBound(to: CChar.self), strings[i], Int(MAXPATHLEN))
    }
}


Testing helper in Objective-C:

MyClass.h

#import <Foundation/Foundation.h>
#import "someStruct.h"

NS_ASSUME_NONNULL_BEGIN

@interface MyClass : NSObject
- (void)dumpSomeStruct:(struct someStruct *)someStructPtr;

@end

NS_ASSUME_NONNULL_END

MyClass.m

#import "MyClass.h"

@implementation MyClass

- (void)dumpSomeStruct:(struct someStruct *)someStructPtr {
    for( int i = 0; i < 10; ++i ) {
        NSLog(@"[%d] %s", i, someStructPtr->path[i]);
    }
}

@end


Testing code:

var someVar = someStruct()

let strings = ["This", "is", "a", "test", "string", "Array", "containing", "10", "elements", "."]

fillStringsToSomeStruct(&someVar, strings)

let myObj = MyClass()
myObj.dumpSomeStruct(&someVar)


Output:

2019-08-10 05:42:31.129755+0900 StrCpyArray[88658:7320007] [0] This

2019-08-10 05:42:31.129900+0900 StrCpyArray[88658:7320007] [1] is

2019-08-10 05:42:31.129909+0900 StrCpyArray[88658:7320007] [2] a

2019-08-10 05:42:31.129923+0900 StrCpyArray[88658:7320007] [3] test

2019-08-10 05:42:31.129931+0900 StrCpyArray[88658:7320007] [4] string

2019-08-10 05:42:31.129937+0900 StrCpyArray[88658:7320007] [5] Array

2019-08-10 05:42:31.129948+0900 StrCpyArray[88658:7320007] [6] containing

2019-08-10 05:42:31.129954+0900 StrCpyArray[88658:7320007] [7] 10

2019-08-10 05:42:31.129960+0900 StrCpyArray[88658:7320007] [8] elements

2019-08-10 05:42:31.129966+0900 StrCpyArray[88658:7320007] [9] .


I would recommend to write an Objective-C wrapper for such structs, than manipulating them directly in Swift.

Replies

I may miss what is the problem.

Why do you want a 2D array in Swift ?

Have you an array of Swift Strings that you want to convert to an array of array of Chars in Swift ?

Why don't you just keep the array of Strings ?

Note that fixed size arrays do not exist in Swift.


If issue is to mix C and Swift, have a look here:

https://stackoverflow.com/questions/28203675/is-it-possible-to-mix-c-and-swift

I need to copy [String] to the char[][] in the C struct, which is used by some C code. I cannot change the C struct.

Handling fixed-sized array in a C-struct is one thing Swift is not good at.


You can write something like this in Swift:

func fillStringsToSomeStruct(_ ptr: UnsafeMutablePointer<someStruct>, _ strings: [String]) {
    let pathPtr = UnsafeMutableRawPointer(ptr)
        + MemoryLayout<someStruct>.offset(of: \someStruct.path)!
    assert(strings.count == 10, "strings must have 10 elements")
    for i in 0..<10 {
        let pathN = pathPtr + i * Int(MAXPATHLEN)
        strlcpy(pathN.assumingMemoryBound(to: CChar.self), strings[i], Int(MAXPATHLEN))
    }
}


Testing helper in Objective-C:

MyClass.h

#import <Foundation/Foundation.h>
#import "someStruct.h"

NS_ASSUME_NONNULL_BEGIN

@interface MyClass : NSObject
- (void)dumpSomeStruct:(struct someStruct *)someStructPtr;

@end

NS_ASSUME_NONNULL_END

MyClass.m

#import "MyClass.h"

@implementation MyClass

- (void)dumpSomeStruct:(struct someStruct *)someStructPtr {
    for( int i = 0; i < 10; ++i ) {
        NSLog(@"[%d] %s", i, someStructPtr->path[i]);
    }
}

@end


Testing code:

var someVar = someStruct()

let strings = ["This", "is", "a", "test", "string", "Array", "containing", "10", "elements", "."]

fillStringsToSomeStruct(&someVar, strings)

let myObj = MyClass()
myObj.dumpSomeStruct(&someVar)


Output:

2019-08-10 05:42:31.129755+0900 StrCpyArray[88658:7320007] [0] This

2019-08-10 05:42:31.129900+0900 StrCpyArray[88658:7320007] [1] is

2019-08-10 05:42:31.129909+0900 StrCpyArray[88658:7320007] [2] a

2019-08-10 05:42:31.129923+0900 StrCpyArray[88658:7320007] [3] test

2019-08-10 05:42:31.129931+0900 StrCpyArray[88658:7320007] [4] string

2019-08-10 05:42:31.129937+0900 StrCpyArray[88658:7320007] [5] Array

2019-08-10 05:42:31.129948+0900 StrCpyArray[88658:7320007] [6] containing

2019-08-10 05:42:31.129954+0900 StrCpyArray[88658:7320007] [7] 10

2019-08-10 05:42:31.129960+0900 StrCpyArray[88658:7320007] [8] elements

2019-08-10 05:42:31.129966+0900 StrCpyArray[88658:7320007] [9] .


I would recommend to write an Objective-C wrapper for such structs, than manipulating them directly in Swift.

This works like a charm. Thanks a million!