tl;dr: How to best create a pointer to an array of user-defined type of possibly mutating data when calling into a legacy C API.
I’m trying to Ccall a function of a legacy application in which I’ve embedded Julia to replace the original scripting language. The application wraps values into its own symbol type, a C struct. The C API functions expect an array to those symbols to hold the actual arguments. The contained values might be changed; the array is not changed in terms of memory location and size.
So, I have a value wrapper like the following plus a bunch of constructors
immutable MyType
data::UInt64 # a union of the value or a pointer to the value
datatype::Int64 # an enum of the contained type
end
The constructors from the relevant Julia types into that wrapped type work fine.
Then I have a function like the following that wraps any number of Julia values into a NTuple and performs the ccall:
function apicall(fn::Symbol, args...)
# Convert the arguments into a tuple
wrapped_args = map(MyType, args)
nargs = length(args)
fnname = string(fn)
rarg = Ref{MyType} # <---- (A)
# Alternative to the above via a zero-dimensional array to zero-initialize
#rarg = Array{MyType}(); rarg[] = MyType()
success = ccall(:apifn, Int64,
(CString, Int64, Ptr{Void}, Ptr{Void}), # <---- (B)
fnname, nargs,
pointer_from_objref(wrapped_args), pointer_from_objref(rarg)) # <---- (C)
success != 0 && error("Call failed.")
get(rarg) # Unwrap and return result
end
The above kind of works, but it seems to be prone to errors.
For clarification: The C API implements a signature of
# Variant 1
int apifn(const char*, int narg, MyType args[], MyType* rarg)
# Variant 2
int apifn(int narg, MyType args[], MyType* rarg)
with rarg
being the actual result; also wrapped.
The issue I’m having is with lines (A), (B) and (C):
(A): How to best create a reference to MyType
which should also be default initialized? Here Ref
appears to create unintialized memory.
(B,C): How can I get to the correctly GC managed Ref{MyType}
or similar when having such an NTuple of MyType
s?
Thus far, I figured I need to write a Base.cconvert
and Base.unsafe_convert
method, like it is e.g. done in StaticArrays
. But I fail at figuring out how to do that for an NTuple.
Or, is there a more canonical way to achieve something similar?
Many thanks in advance!