Dereferencing an Array of Struct Pointer

I’m having trouble dereferencing a pointer to an array of structs. See the following code for an example:

mutable struct Test
    x::Float64
end

tests = [Test(1.2), Test(3.4)]

tests_ptr = Base.unsafe_convert(Ptr{Test}, tests)

test1 = unsafe_load(tests_ptr)
test2 = unsafe_load(tests_ptr + sizeof(Test))

println("test1.x = $(test1.x)")
println("test2.x = $(test2.x)")

The result I get is junk:

test1.x = 1.719804727e-315
test2.x = 1.719804806e-315

My goal in the end is to pass data using structures back and forth from c#, but first I need to figure out how to get the correct pointer to each element of the struct array.

There are the unsafe function

pointer(tests, i)

and the safe function

Ref(tests, i)

to get respectively a pointer and a reference to the i-th element of tests.

This still gives back junk…

julia> pointer(tests, 1)
Ptr{Test} @0x0000000015d32270

julia> tests_ptr
Ptr{Test} @0x0000000015d32270

julia> unsafe_load(pointer(tests,1))
Test(1.795226654e-315)

Uhm… You are right. The reason is that in the array tests there are not stored the objects Test themselves, but just pointers to them, so there is a double indirection (because they are mutable, which I didn’t notice).

The following works, but seem extremely fragile, so there must be a better solution:

p = Base.unsafe_convert(Ptr{Ptr{Test}}, pointer(tests, 1))
unsafe_load(unsafe_load(p))

Edit

This works as well and is a bit cleaner:

p = Base.unsafe_convert(Ptr{Test}, Ref(tests, 1))
unsafe_load(p)
2 Likes

Thanks so much! That works!

Yes it does, but I’m not so sure it is the most Julianic way to achieve it, as I’m not too familiar with all the low level tools that the language provides to interface with external C code.

Maybe someone else can provide a cleaner solution that avoids unsafe_convert, I don’t know.

1 Like

Just for further info, here is the working code to get both elements:

mutable struct Test
    x::Float64
end

tests = [Test(1.2), Test(3.4)]

# ccall method
tests_ptr_c = @ccall jl_array_ptr(tests::Array{Test})::Ptr{Ptr{Test}}

# julia method
tests_ptr_j = Base.unsafe_convert(Ptr{Ptr{Test}}, tests)

test1_ptr = unsafe_load(tests_ptr_j)
test2_ptr = unsafe_load(tests_ptr_j+sizeof(Test))

test1 = unsafe_load(test1_ptr)
test2 = unsafe_load(test2_ptr)

println("test1.x = $(test1.x)")
println("test2.x = $(test2.x)")

Which gives the correct result:

test1.x = 1.2
test2.x = 3.4
2 Likes