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