Access fields of pointer to struct returned from ccall

I am calling a C function that returns a pointer to a struct.
I have mapped that struct to a corresponding Julia type (see code below), and when I call the function I get a Ptr{T}.
How can access the fields of the returned pointer?

mutable struct XXH32_state_t
   total_len_32::UInt32
   large_len::UInt32
   v1::UInt32
   v2::UInt32
   v3::UInt32
   v4::UInt32
   mem32::NTuple{4,UInt32}
   memsize::UInt32
   reserved::UInt32
end

@inline function XXH32_createState()::Ref{XXH32_state_t}
   sr = ccall((:XXH32_createState, libxxhash), Ptr{XXH32_state_t}, ())
end

calling the function

sr=XXH32_createState()

returns:

Ptr{XXH32_state_t} @0x00000000014fc4f0

This is somewhat annoying. For read-only access, you can simply unsafe_load(sr) and obtain a new XXH32_state_t, the fields of which are copies of sr.

Generally, this is not what you want. The problem is that mutable struct implies that the object is managed by the julia garbage collector and preceded by a julia-specific object header. Your C library allocates the blob of memory, and it is therefore impossible to wrap it into a julia object of this type.

The proper type would be struct XXH32_state_t ... end. Then you could define a mutable struct XXH32_state_handle ptr::Ptr{XXH32_state_t} end that exposes all the relevant getproperty / setproperty!, and optionally has a finalizer that de-allocates the object (either by calling free if the object was simply malloced, or by calling something like XXH32_freeState).

The package blobs.jl is fantastic for exploring more ergonomic C interop. This PR is also relevant.

If you only need a single type, then Blobs.jl is probably not worth the dependency, though (one page of boilerplate code is easier to audit than a dependency on a rarely used package).

2 Likes

Thanks for your advice
You guessed correctly that the library has a XXH32_freeState function, and I would like to use your idea regarding a finalizer.
The functions are used to hash a data stream, thus I named the immutable struct:

mutable struct XXH32stream
   state_ptr::Ptr{XXH32_state_t}
   function XXH32stream()
      sp = XXH32_createState()
      new(sp)
      finalizer(state_ptr, XXH32_freeState)
   end
end

The finalizer statement raises the error that state_ptr is not defined.
Is there a way to reference the struct’s fields from within an inner constructor?
If not, what is the “proper” way to register a finalizer when creating an object?

...
     stream = new(sp)
     finalizer(stream.state_ptr, XXH32_freeState)
     return stream
...

perhaps?

But the function should be the first argument.

1 Like

Thanks
I had to make a slight modification, as the proposed syntax raised the error:

ERROR: objects of type Ptr{XXH32_state_t} cannot be finalized

Which “agrees” with the documentation: The type of x must be a mutable struct, otherwise the behavior of this function is unpredictable.
Thus, I ended up with the following, which seems to work:

mutable struct XXH32stream
   state_ptr::Ptr{XXH32_state_t}
   function XXH32stream()
      sp = XXH32_createState()
      stream = new(sp)
      finalizer(x->XXH32_freeState(x.state_ptr), stream)
      return stream
   end
end