Using ccall to modify fields of Julia struct

I am totally new to Julia in the last week, and I have decided to dive in and try to write a wrapper for a library I frequently use in my research. This library has structs which get initialized by passing them to a function which sets the values appropriately. Here is some simplified C code which reflects this (not from the actual library as that would be too long to include a MWE here):

#include <string.h>
typedef char Msg[2048];

struct myStruct1{
	double field1;
	double * field2;
};

struct myStruct2{
	double field3;
};

int init_myStruct(struct myStruct1 * s1, struct myStruct2 * s2, Msg m){
	s1->field1 = 5.;
	s1->field2 = &(s1->field1);
	s2->field3 = *(s1->field2);
	const char* tmp = "Initialized";
	strcpy(m, tmp);
	return 1;
}

I am trying to wrap this in Julia. The goal here is that I can compute what I need using this library, and once that is done I can do the rest of my analysis in Julia. To do so, I have create analogues of these structs in Julia, and have I think initialized them to be empty and filled later. Here is my Julia code:

struct JStruct1
	field1::Cdouble
	field2::Ptr{Cdouble}
	JStruct1() = new()
end

struct JStruct2
	field3::Cdouble
	JStruct2() = new()
end

struct MyJuliaStruct
	s1::JStruct1
	s2::JStruct2
	m::NTuple{2048, Cchar}	
	function MyJuliaStruct()
		s1 = JStruct1()
		s2 = JStruct2()
		message=ntuple(x->convert(Cchar, 0*x), 2048)
		new(s1, s2, message)
	end
end

function main()
	s = MyJuliaStruct()
	ccall((:init_myStruct, "libtest.so"), Cint, (Ref{JStruct1}, Ref{JStruct2}, NTuple{2048, Cchar}), Ref(s.s1), Ref(s.s2), s.m)
	println(s.s1.field1)
	println(s.m)
end

main()

When I run this however, I get as output:

0.0
(0, 0, 0, 0, [2048 zeroes here that I won't repeat], 0)

The ccall seems to have worked fine but my values are not appropriately modified. I thought that by passing the Julia structs as Ref(s.s1), the memory could be managed by both C and Julia and that this would work. Can anyone point me in the right direction? I have looked at some similar topics but haven’t been able to figure it out. Thanks for reading!

Have you tried mutable struct?

This is not right. It’s basically wrong if you simply translate &x to Ref(x) in ccall. It should be something like:

x_ref = Ref(x)
ret = ccall(..., x_ref)
x = x_ref[]
ret 

NTuple{2048, Cchar}Ptr{Cchar}: In C, array types in the function proto should decay to corresponding pointer types.

struct MyJuliaStructmutable struct MyJuliaStruct: This should be mutable if you’re gonna change the fields. if you’d like to keep it immutable, you could use it in this way:

s = MyJuliaStruct()
s1_ref = Ref(s.s1)
s2_ref = Ref(s.s2)
m_ref = Ref(s.m)
ccall((:init_myStruct, "libtest.so"), Cint, (Ptr{JStruct1}, Ptr{JStruct2}, Ptr{Cchar}), s1_ref, s2_ref, m_ref)
s = MyJuliaStruct(s1_ref[], s2_ref[], m_ref[])

Or you could simply do:

s1_ref = Ref{JStruct1}()
s2_ref = Ref{JStruct2}()
m_ref = Ref{NTuple{2048, Cchar}}()
ccall((:init_myStruct, "libtest.so"), Cint, (Ptr{JStruct1}, Ptr{JStruct2}, Ptr{Cchar}), s1_ref, s2_ref, m_ref)
s = MyJuliaStruct(s1_ref[], s2_ref[], m_ref[])

I somehow deleted my previous post, but that is OK as I want to modify it anyway. Thank you very much for your suggestions, they have been very helpful. I have implemented them as you suggest, so that my main() function is now:

function main()
	s1_ref = Ref{JStruct1}()
	s2_ref = Ref{JStruct2}()
	m_ref = Ref{NTuple{2048, Cchar}}()
	ccall((:init_myStruct, "libtest.so"), Cint, (Ptr{JStruct1}, Ptr{JStruct2}, Ptr{Cchar}), s1_ref, s2_ref, m_ref)
	s = MyJuliaStruct(s1_ref[], s2_ref[], m_ref[])
	println(s.s1.field1)
	println(s.m)
end

main()

Running this gives the following error:

LoadError: UndefRefError: access to undefined reference
Stacktrace:
 [1] getproperty
   @ ./Base.jl:42 [inlined]
 [2] unsafe_convert
   @ ./refvalue.jl:42 [inlined]
 [3] main()
   @ Main ~/projects/misc/wrapping/cwrap.jl:28
 [4] top-level scope
   @ ~/projects/misc/wrapping/cwrap.jl:34

Do you have any idea why this might be happening? It is coming from the ccall itself because I get the same error even when commenting out lines after that.

You don’t need to define JStruct2() = new() for isbitstypes, but you could also use Ref(JStruct1()) instead, if you’d like to keep it.