How to use ccall, cconvert, and unsafe_convert in a convenient and memory-safe way?

sloede
Tue, 23 Jun 2020 12:34:12 +0000
General Usage

I am writing Julia code to conveniently interface a C library, which in turn is just an interface to a C++ library. Therefore, many times I have to store pointers to void as the C interface does not know anything about the actual types either.

For this purpose, I have several structs like this

mutable struct CxxObjectWrapper
  handle::Ptr{Cvoid}
end

which have no other purpose than to hold a Ptr to the actual memory (which is managed through the C library), and to be able to use it conveniently with multiple dispatch.

My questions are regarding the proper use of CxxObjectWrapper when used in ccall. Many C functions look like

long foo(void *cxx_object_wrapper);

to which I write the following Julia function:

function foo(obj::CxxObjectWrapper)
  ccall((:call_foo, libname), Clong, (Ptr{CVoid},), obj.handle)
end

Now my questions are:

1. Is this a problem in terms of garbage collection (specifically, the obj.handle part)? E.g., should I wrap the ccall in something like GC.@preserve obj ccall(...)?

2. What would be the safe and canonical way to do this conversion automatically? Should I define something like

cconvert(::Type{Ptr{Cvoid}}, x::CxxObjectWrapper) = x.handle

or do I need to use unsafe_convert? I am a little at a loss here, since the docstrings imply that cconvert should not return a Ptr but I am not sure if this applies in this case (or if it just means it should not return a Ptr to the object itself).

19 posts - 4 participants