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
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).
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?
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