A followup question. In the above example I'd like to add method `getindex` for `XMatrix`. Previously, I could do it like this:
type XMatrix rows::Cint cols::Cint data::Ptr{Float64} end getindex(mat::XMatrix, i::Integer) = unsafe_load(mat.data, i) Now after Yichao's suggestion my type looks like: type XMatrix ptr::Ptr{Void} end So I don't have direct access to the `data` field. What is the proper way to get it? So far my best idea is to use something like this: type XMatrixStruct rows::Cint cols::Cint data::Ptr{Float64} end type XMatrix ptr::Ptr{XMatrixStruct} end function getindex(mat::XMatrix, i::Integer) struct = unsafe_load(mat.ptr) # load copy of XMatrixStruct type value = unsafe_load(struct.data, i) # load value from its data field end But it would be nice to avoid intermediate `XMatrixStruct` type. On Sunday, April 17, 2016 at 3:49:51 AM UTC+3, Andrei Zh wrote: > > This is exactly the answer I hoped to see! Thanks a lot! > > On Sunday, April 17, 2016 at 2:46:15 AM UTC+3, Yichao Yu wrote: >> >> On Sat, Apr 16, 2016 at 6:55 PM, Andrei Zh <faithle...@gmail.com> wrote: >> > I have a question regarding Struct Type correspondences section of the >> > documentation. Let's say, in a C library we have a struct like this: >> > >> > typedef struct { >> > int rows; >> > int cols; >> > double* data; >> > } xmatrix; >> > >> > In C code it can be created using function: >> > >> > int xmatrix_copy(void* src, xmatrix** mat, int rows, int cols); >> > >> > Note pointer-to-pointer in second argument. This C function allocates >> new >> > region of memory for `xmatrix` and fills in the values (rows, cols and >> > pointer to data). I call this function from Julia like this: >> > >> > type XMatrix >> > rows::Cint >> > cols::Cint >> > data::Ptr{Float64} >> > end >> > >> > >> > data = Float64[1, 2, 3, 4, 5, 6] >> > matptr = Array(Ptr{XMatrix}, 1) >> > >> > >> > ccall((:xmatrix_copy), Cint, >> > (Ptr{Void}, Ptr{Ptr{XMatrix}}, Cint, Cint), >> > pointer(data), matptr, Cint(3), Cint(2)) >> > >> > mat = unsafe_load(matptr[1]) >> >> This is wrong, it leaks memory. See below. >> >> > >> > Details to notice in `ccall`: >> > >> > 1. In type tuple I set type of xmatrix argument to be a >> pointer-to-pointer >> > to XMatrix. >> > 2. As an argument, I pass 1-element array of pointers to XMatrix and >> let C >> > code to fill it in. >> > >> > This approach works and I get initialized `XMatrix`. Yet, I don't >> really >> > understand all details of memory management in this case, In >> particular: >> > >> > 1. Who owns memory of an instance of `XMatrix`? Is `unsafe_load` >> copying >> > fields or just assigns Julia type tag to C-allocated region of memory? >> >> The owner of the memory is determined by the c library. In this case >> it seems that the C library owns the memory (which is generally the >> case if the library provide new and free functions) >> Julia never (and can never) assign tag to C memory. Unsafe load does >> exactly what the name suggests and is equivalent to `*ptr` in C. It >> copies the content of the struct and the return value >> has no aliasing with the original pointer. >> >> I assume the C library want the original pointer so you need to keep >> the pointer in the wrapper type and define conversion functions to >> pass it to C. See this[1] for an example for the proper way to do it >> (mostly the ptr field, the cconvert and unsafe_convert definitions and >> the finalizer(explained below)). >> >> > 2. What would be the proper way to free allocated memory given that C >> > library provides function `xmatrix_free(xmatrix* mat)`? >> >> ccall the free function. You usually also want a finalizer on the >> wrapper type mentioned above so that the GC can free the memory for >> you when the julia object is collected. Note that you should use >> finalizer to finalize object only for types that you don't care too >> much about their lifetime since the time when a finalizer is called is >> undefined. >> >> > 3. Aforementioned section (and the next one) suggest to use >> `Ref{XMatrix}` >> > instead. Can somebody provide an example of corresponding code using >> refs >> > instead of wrapping into an array? >> >> Ref on a bits type is basically a 0-dim array >> >> matout = Ref{Ptr{XMatrix}}() >> >> ccall((:xmatrix_copy), Cint, >> (Ptr{Void}, Ptr{Ptr{XMatrix}}, Cint, Cint), >> data, matout, Cint(3), Cint(2)) # DO NOT USE >> `pointer(data)`!!!! This is the single most common mistake when using >> ccall. It is fine in the global scope but if this is in a function, >> julia is free to collect the data array when you are in/before >> entering the ccall. See the manual section about cconvert and >> unsafe_convert >> >> matptr = matout[] # This is the C managed/owned pointer that you >> should embed in your julia type. >> >> > >> > Thanks, >> > Andrei >> >> >> [1] >> https://github.com/yuyichao/LibArchive.jl/blob/4112772e8d1d7124c896bf98c5baee13149d3f8d/src/writer.jl#L28-L58 >> >> >