Confusion about Ptr, Ref and dereferencing

I’m using ccall to access a C library that has its own memory management. For the constructor and destructor calls, it expects a void**. I thought it would be most natural to create a Ptr{Cvoid} in Julia, then pass a Ref of that to the ccall. However, I get this unexpected behavior:

julia> ptr = Ptr{Cvoid}(C_NULL)
Ptr{Nothing} @0x0000000000000000

julia> Ref(ptr)[] == ptr

julia> ptrptr = Ref(ptr)
Base.RefValue{Ptr{Nothing}}(Ptr{Nothing} @0x0000000000000000)

julia> ptrptr[] = 1

julia> ptrptr
Base.RefValue{Ptr{Nothing}}(Ptr{Nothing} @0x0000000000000001)

julia> ptrptr[]
Ptr{Nothing} @0x0000000000000001

julia> ptr
Ptr{Nothing} @0x0000000000000000

What I want to do in Julia should be equivalent to the follow C code:

void* ptr = NULL;
assert(ptr != NULL);

Is Ref not appropriate here? What should I do instead?

What you want to do is impossible. If ptr is C_NULL, the only way to change that is by assigning to ptr, i.e. ptr = .... There’s absolutely no exception to this rule, you cannot implicitly modify what a variable is bound to ever. Applying to immutable types (like Ptr) this means that without reassignment it’ll always be the same object witht he same content in that variable.

You already have the closest you can get in julia with your C code, which is to just use ptrptr[] instead.

1 Like

Thanks for your explanation.

So you suggest I never use my ptr variable in the Julia code at all and just do the following?

ptrptr = Ref(Ptr{Cvoid}(C_NULL))
my_constructor(ptrptr)  # ptrptr[] = 1

I felt like there should be way of more directly translating the C code, but it looks like there isn’t.

I first read the section When to use T, Ptr{T} and Ref{T} and did not fully understand. Now that I revisted it, I found another section further down: Passing Pointers for Modifying Inputs. In the example there, the Julia local variables are also of Ref type already.

Well, you can do ptr = ptrptr[] but otherwise basically yes.