I have spent considerable time finding a bug in my code related to memory layout of types and immutables when interfacing with structs in c. I want to summarize here to check my understanding and as it may be useful to others.
When to use immutable
Firstly, I have been calling a code that requests and array of structs (memory owned by Julia). The C code is like
typedef struct {
int size;
void * data;
} bob_t;
int fill_bob(bob_t * bobs, int nbobs);
which I would call from Julia using
immutable bob_t
size::Cint
data::Ptr{Void}
end
function fill_bob(size)
bobs = Array{bob_t,1}(size)
err = ccall((:fill_bob, library), Cint, (Ref{bob_t}, Cint), bobs, size)
bobs
end
In this case one must use an immutable
, because C expects the structs to be stored inline inside the array. If I use a type
for bob_t
then AFAICT the array bobs
only holds references to the types, and so the above code doesn’t work. Is that correct? Is there a data structure that stores types
inline?
When to use type
On the other hand sometimes one needs a longer term reference to a specific struct
that is shared between Julia and C. e.g.
typedef struct {
int size;
void * data;
} frank_t;
int do_something(frank_t * frank);
In Julia I often want to store such a struct inside another type.
type frank_t
size::Cint
data::Ptr{Void}
end
type Frank
f::frank_t
data::Array{Uint8,1}
end
function Frank(size)
data = Array{Uint8,1)(size)
Frank(frank_t(size, pointer(data, 1)), data)
end
function do_something(frank::Frank)
err = ccall((:do_something, library), Cint, (Ref{frank_t},), frank.f)
end
In this case I keep a Julia reference to the array in Frank
, which corresponds to the pointer inside of frank_t
. Then I can call the c functions as necessary to work on frank_t, but also access the data directly in Julia using the data array field of Frank
.
In this case it is essential that frank_t
and Frank
are type
and not immutable
, because otherwise the actual address of frank_t
changes from call to call. If the C library stores references to frank_t
internally (as the one I’m using does) then this can lead to subtle bugs.
Does that make sense? Did I miss something obvious?
Thanks!