Properly using `finalizer`, `ccall`, `cconvert` and `unsafe_convert`

This is not forbidden, the forbidden thing is returning address of anything you allocated in the function.

That’s not true, see below.

Yes, your function will take a void** that’s fine, but if you want to mutate a void* in a C struct, you don’t define that field as void**, you’ll just define it as void*. And you’ll use it as

struct mytype {
    double v;
    void *ptr;
};

// and some code in a function
mytype *obj;
// ...
somefunction(obj->v, &obj->ptr);

And you can write a exact equivalent in julia.

No this is not necessary.

Also

is fine but also not necessary. The validity of the ::Cdouble doesn’t rely on the liveness of the ::MyType. It’s certainly valid code though, just a little over-complicated.

The direct (and invalid!) way to implement the c++ code above in julia would simply be

ccall(somefunction, Cdouble, (Cdouble, Ptr{Ptr{Void}}), obj.v, pointer_to_objref(obj) + fieldoffset(typeof(obj), 2))

The only invalid part of the above code is that pointer_to_objref(obj) + fieldoffset(typeof(obj), 2) will not keep obj alive during the call. And the fix would be to somehow keep it alive. This can be acomplished either with @gc_preserve on 0.7 like

Base.@gc_preserve obj ccall(somefunction, Cdouble, (Cdouble, Ptr{Ptr{Void}}), obj.v, pointer_to_objref(obj) + fieldoffset(typeof(obj), 2))

which will be the preferred way if the convertion is callsite specific. On 0.6 where there’s no @gc_preserve or if this call convertion is used multiple times, it is preferred to use the unsafe_convert/cconvert and you can do this simply by putting that unsafe convert in, well, unsafe_convert, sth like.

Base.unsafe_convert(::Type{Ptr{Ptr{Void}}}, obj::MyType) = Ptr{Ptr{Void}}(pointer_to_objref(obj) + fieldoffset(typeof(obj), 2))

And simply do

ccall(somefunction, Cdouble, (Cdouble, Ptr{Ptr{Void}}), obj.v, obj)

The use of either Ref{Ptr{Void}} or Ptr{Ptr{Void}} is valid and doesn’t really matter. In fact, any Ref or Ptr type should work. I usually prefer Ptr though since Ref has special meaning and it is better to not mess with it.

And I’ll also add that I can see how this is a failed attempt at translating the C code I translated above. The important thing to note here about the difference between C and julia is that there’s no C reference in julia. Even though it is not called so in C, obj.field produces a lvalue reference which is why & on it gives you the address inside obj. OTOH, obj.field always produce a rvalue in julia, or in another word, local variable assignment is never significant, you can insert as many of them as you want in the code and they’ll never make any difference in semantics. This means that pointer_from_objref(m.handle) corresponds to

{
    void *handle = m->handle;
    return &handle;
}

and it should be easy to see why it doesn’t give you the result you want. That’s also why you need to explicitly take the address of m in order to get the address of handle field in m.

1 Like