RAII and wrapping a C library

ccall
memory-allocation

#1

There is an example in the Julia doc of constructing, destructing a GSL permutation object using ccall:
https://docs.julialang.org/en/stable/manual/calling-c-and-fortran-code/#Some-Examples-of-C-Wrappers-1

In this example, (1) it is possible to create a Julia permutation object which is not valid in a GSL call and (2) if the Julia object is garbage collected, the underlying object managed by the C library will never be freed.

For my wrapper of the CALCEPH lib, I have come up with the following RAII design using inner constructor/finalizers.

type CalcephEphem
   data :: Ptr{Void}
   function CalcephEphem(files::Vector{<:AbstractString})
      ptr = ccall((:calceph_open_array, libcalceph), Ptr{Void}, (Int, Ptr{Ptr{UInt8}}), length(files), files)
      if (ptr == C_NULL)
         error("Unable to open ephemeris file(s)!")
      end
      obj = new(ptr)
      finalizer(obj,CalcephEphemDestructor) # register object destructor
      return obj
   end
end

# to be called by gc when cleaning up
# not in the exposed interface but can be called with finalize(e)
function CalcephEphemDestructor(e::CalcephEphem)
   if (e.data == C_NULL) 
      return
   end
   ccall((:calceph_close, libcalceph), Void, (Ptr{Void},), e.data) 
   e.data = C_NULL
   return  
end

CalcephEphem(file::AbstractString) = CalcephEphem([file])

function CalcephPrefetch(e::CalcephEphem) 
    if (e.data == C_NULL)
       error("Ephemeris object is not propely initialized.")
    end
    stat = ccall((:calceph_prefetch, libcalceph), Int, (Ptr{Void},), e.data)
    if (stat == 0)
       error("Unable to prefetch ephemeris!")
    end
    return
end

export CalcephEphem, CalcephPrefetch

Is this a proper way of doing this sort of thing in Julia or should I prefer the GSL example way in the docs?

Thank you.


#2

I think both approaches are fine. Your approach is commonly used e.g. in HDF5.jl which wraps the hdf5 C library. If its just memory then the approach is the way to go. For other resources one might want to have more control over the destruction. In that case I usually implement an explicit destructor and document that it is mandatory calling it.


#3

Do note that there is no guarantee about finalization order or when the finalizer will run like there is with RAII in C++. So it is discouraged to rely on those properties for program behavior (even if you can make something work the way you want with current behavior).