I’m trying to pass an array of structure (actually, a pointer to the chunk of memory containing the first structure) from Julia to C through ccall
.
An example of what I’m trying to do is shown below, using just C:
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
//Define a test structure
struct st_test {
int i;
double d;
char s[10];
double a[2];
int b[2];
};
//Print the contents of an array of structure, and modify their values
//upon return.
int printAndModify(struct st_test* pointer, int size) {
int i;
printf("\nin C:\n");
//Loop through elements in the array
for (i=0; i<size; i++) {
//Pointer to current element
struct st_test* cur = &(pointer[i]);
//Print structure contents
printf("index %d/%d, memory address %p\n", i+1, size, cur);
printf(" i=%d\n d=%f\n s=%10s\n a=%f %f\n b=%d %d\n",
cur->i, cur->d, cur->s,
cur->a[0], cur->a[1], cur->b[0], cur->b[1]);
//Modify structure contents
cur->i = 100 + i;
cur->d += 100;
strcpy(cur->s, "from C ");
cur->a[0] *= 3.;
cur->a[1] *= 4.;
cur->b[0] *= 5.;
cur->b[1] *= 6.;
}
return 1;
}
void test_c() {
//Allocate memory for the array of structures. NOTE: all
//structures are stored in CONTIGUOUS chunks!
const int size = 2;
struct st_test* array = malloc(sizeof(struct st_test) * size);
//Fill values
int i;
for (i=0; i<size; i++) {
//Pointer to current structure
struct st_test* cur = &(array[i]);
cur->i = 1;
cur->d = 2;
strcpy(cur->s, "from test_c");
cur->a[0] = 3;
cur->a[1] = 4;
cur->b[0] = 5;
cur->b[1] = 6;
}
printAndModify(array, size);
free(array);
}
After compiling the above code as a shared library, I wanted to implement the test_c
routine in Julia, as follows:
#Define a julia structure corresponding to the C one
mutable struct st_test
i::Cint
d::Cdouble
s::NTuple{10, Cchar}
a::NTuple{2, Cdouble}
b::NTuple{2, Cint}
end
#Simple constructor for the structure
st_test() = st_test(1, 2., NTuple{10,Cchar}("from Julia"), (3.,4.), (5, 6))
#Create an array of structures
array = [st_test() for i=1:2]
#Size of the array
size = Cint(length(array))
ret = ccall((:printAndModify, "./libarrayIssue.so"), Cint, (Ptr{st_test}, Cint),
array, size)
#Print structure contents
@printf("\nin Julia:\n");
for i in 1:size
cur = array[i]
@printf("index %d/%d\n", i, size);
@printf(" i=%d\n d=%f\n s=%10s\n a=%f %f\n b=%d %d\n",
cur.i, cur.d, join(Char.(cur.s), ""),
cur.a[1], cur.a[2], cur.b[1], cur.b[2]);
end
However, this Julia code ends up in a segfault since (as far as I understood) the structures passed through ccall
are not allocated in contiguous chunks of memory, while in C they were one next to the other.
The only way I found to solve the problem is to create a new version of the printAndModify
routine, called printAndModify_new
:
int printAndModify_new(struct st_test** pointer, int size) {
// CHANGED HERE ^
int i;
printf("\nin C:\n");
//Loop through elements in the array
for (i=0; i<size; i++) {
//Pointer to current element
struct st_test* cur = pointer[i]; // <-- CHANGED HERE
//Print structure contents
printf("index %d/%d, memory address %p\n", i+1, size, cur);
printf(" i=%d\n d=%f\n s=%10s\n a=%f %f\n b=%d %d\n",
cur->i, cur->d, cur->s,
cur->a[0], cur->a[1], cur->b[0], cur->b[1]);
//Modify structure contents
cur->i = 100 + i;
cur->d += 100;
strcpy(cur->s, "from C ");
cur->a[0] *= 3.;
cur->a[1] *= 4.;
cur->b[0] *= 5.;
cur->b[1] *= 6.;
}
return 1;
}
which I call from Julia with:
ret = ccall((:printAndModify_new, "./libarrayIssue.so"), Cint, (Ptr{st_test}, Cint),
array, size)
Notice the change in the input parameter: now it is a st_test**
.
This is a significantly simplified version of a problem I’m having in calling a C library, and I’m not going to change all the st_test*
to st_test**
in the code.
Is there anyway I can pass an Array{st_test,1}
through ccall
retaining the original function definition with st_test*
, i.e. by having the structures stored in continguous chunks of memory?