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!

Answered by OOPer in 376369022

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.

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.

Accepted Answer

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!

Copying swift string to fixed size char[][]
 
 
Q