How in swift loop through array of structs returning from C function?

Having the following pseudo code. How in swift , i could obtain all array elements and subalterns of data?.

struct data {
int a;
int b;
}

//this C function returns array of data struct
data* CFunc(void)
{
//allocate dynamic array of data. loop and fills the elements.
}

You can enclose code fragments into triple back tick marks (`) to make the code formatted.

C header:

typedef struct {
    int a;
    int b;
} CData;

const CData* __nonnull cfunc(void);

Swift source:

    let datas = cfunc() // UnsafePointer<CData>
    for i in 0 ..< 100 { // number of elements
        let data = datas[i]
    }

Without const that would translate into UnsafeMutablePointer in which case the data elements could be modified. And without __nonnull the pointer returned could be nil, in which case you'll need to check and unwrap it first.

As usual with C API's you'd either need to know the number of elements or provide some sentinel element (designating a certain value of CData as a "list end").

1 Like
data* CFunc(void)
{
//allocate dynamic array of data. loop and fills the elements.
}

How about if data* and size is passed and returned as arguments of the CFunc(data, size) instead of returning data* like that?. After CFunc returns size would indicate number of data structures to loop through. I tried that , but got errors. In pure C i would use pointers.

Can you post a simple example of what your C function looks like and how you would call it in C?

typedef struct {
    int a;
    int b;
} CData;

it returns an int, I pass a pointer to CData, it expands the memory spaceinside the C function

int CFunc(CData** array, int* size)
{
pseudo:
allocate dynamic array using malloc for for n number of Cdata. Loop n times and and fills the elements.
*size = n;
return 0 or error.
}

call it call

CData **d;
int *s;
int ret = CFunc(d, *s)

I'm slightly confused by your example, because there's no way to get the allocated CData array in the caller if the buffer is malloced or reallocated. Did you intend to take a CData **array so that the caller has access to a possibly-reallocated buffer?

updated it.

Ok, so in that case you would either need to allocate the original buffer with malloc, or you would need a guarantee that memory allocated to back Swift's UnsafeBufferPointer is compatible with malloc/free/realloc (@Andrew_Trick, do we make that promise? I believe that it's true of all existing implementations, but I don't know if we guarantee it), or you would need to pass a pointer-to-null as a sigil to just allocate in C, as in general there's no guarantee that memory from a source you don't control can be safely reallocated in C.

In C I would do, like if it is NULL, use malloc, if not null free it and use malloc. How about in this case? Can I see a working example for this case?.

So a use site for the API you described would generally look something like:

var data: UnsafeMutablePointer<CData>? = nil
var size: Int32 = 0
guard CFunc(&data, &size) == 0 else { /* handle error */ }
// Wrap the pointer + size into a buffer
let buffer = UnsafeBufferPointer(start: data, count: Int(size))
// Use normal Swift collection APIs on buffer:
for element in buffer { ... }
// or
buffer.map { ... }

If you can refactor somewhat so that you move allocation out of your C function and into the caller, and the C function only provides the data, then you can use the sometimes-more-ergonomic approach:

// int cfunc(CData *data, int len);
let array = [CData](unsafeUninitializedCapacity: length) { p, n in
  if foo(p.baseAddress!, Int32(length)) == 0 {
    n = length
  }
}
1 Like

That should be :slight_smile:

CData *d;
int s;
int ret = CFunc (&d, &s)
CData *d;
int s;
int ret = CFunc (&d, &s);

actually, but I think we all figured out what they meant.

To simplify things I would recommend to return the pointer as a return value as in your original example. Non nil value means no error and nil value means some error.

const CData* __nullable cfunc(int* __nonnull itemCount);
1 Like

Sadly, Windows requires _aligned_malloc and _aligned_free to work with Swift-allocated buffers. Those aren't malloc/free compatible. But if you're willing to use the correct version of malloc/free for the platform, then yes you can malloc in one language and free in the other.

1 Like

This was also discussed in

3 Likes

This raises a question of whether we might provide a header with C bindings for manipulating swift allocations.

2 Likes

right.