1

I am not familiar with a way to convert a two dimensional array from C to an array of Strings which I can use in Swift. As far as I know there is no quick and easy way to do this in Swift.

This is the header of my C function:

char **getAllFilePaths(const char path []);

I tried the following:

//Get the pointer of the String array
if var ptr = getAllFilePaths(a) {

     //Check if the current String is null
     while let s = ptr.pointee {

        //Copy the String into an array
        a_paths.append(String(cString: s)) //<- Crash: Thread 1: EXC_BAD_ACCESS (code=EXC_I386_GPFLT)

        //Go to the next adress
        ptr += 1
     }

    print(a_paths)
}

(I have this code from Martin R: https://stackoverflow.com/a/38422783/10269733. Unfortunately it doesn't work anymore in Swift 4.2)

I am searching for a solution for this problem all day, therefore I am open for any kinds of creative ideas!

EDIT:

This code works perfectly

char paths [FILES_MAX][PATH_MAX];
static size_t i = 0;

char **getAllFilePaths(const char path []){
    PrintFile(path);

    size_t j;

    for(j = 0; j < i; j++){
        printf("%s\n", paths[j]);
    }

    return paths;
}
0

2 Answers 2

0

The problem is not in the Swift code (or related to any Swift 4.2 changes). The real problem is that the C function returns the “2-dimensional array” variable

char paths[FILES_MAX][PATH_MAX];

as a char ** to the caller – but that are incompatible types: one is an array of arrays (with all characters being in contiguous memory), the other is a pointer to a pointer. There should be a compiler warning like

incompatible pointer types returning 'char [...][...]' from a function with result type 'char **'

You could return paths from the function if you declare something like

typedef char PathName[PATH_MAX];

PathName *getAllFilePaths(const char path []) { ... }

But that would be imported to Swift as

typealias PathName = (Int8, Int8, ... , Int8)

public func getAllFilePaths(_ path: UnsafePointer<Int8>!) -> UnsafeMutablePointer<PathName>!

where PathName is a tuple of PATH_MAX characters – quite cumbersome to use from Swift! Another problem is that the caller does not know how many arrays elements have been filled.

Better define paths as an array of char pointers:

static char *paths[FILES_MAX] = { NULL };

and fill it with a NULL-terminated list of char pointers. Here is a over-simplified example:

char **getAllFilePaths(const char path []) {
    paths[0] = "foo";
    paths[1] = "bar";
    paths[2] = NULL;
    return paths;
}

In your real application you do not add string literals, so you'll probably have to duplicate the strings

    paths[j] = strdup(someString);

which means that at some point the strings must be free()d again, to avoid memory leaks.

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

Comments

0

credits to "https://oleb.net/blog/2016/10/swift-array-of-c-strings/"

C:

int myCFunc(char *const argv[], size_t howmany )
{
    int i;
    for(i=0; i < howmany; i++){
        printf("\n%s", argv[i]);
    }
    return 1;
}

Apart from headers/bridging:

// Swift:

import Foundation

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)
}


func goLowlevel(arguments: [String]) -> Int {

    let count = arguments.count
    return withArrayOfCStrings(arguments) {
        argv in

        myCFunc(argv, count)
        return 10
    }
}


let arr = ["AA", "BBB", "CCCC"]
goLowlevel(arguments: arr)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.