Yes, except I think for your example the GC.@preserve is unnecessary - you should be able to just do
struct DenseMatrix # immutable!
rowCount::Cint
columnCount::Cint
data::Ptr{Cdouble}
end
Base.cconvert(::Type{DenseMatrix}, m::Matrix{Float64}) = m
function Base.unsafe_convert(::Type{DenseMatrix}, m::Matrix{Float64})
return DenseMatrix(size(m, 1), size(m, 2), pointer(m))
end
function CWrapper(arg1::Matrix{Float64})
@ccall lib.fname(arg1::DenseMatrix)::<rettype>
end
since the DenseMatrix will be passed by-value (i.e. as a copy) anyway (it’s isbits after all) and itself doesn’t need to be GC preserved, as I understand it. Just returning m from cconvert already @preserves the matrix itself:
julia> isbitstype(DenseMatrix)
true
shell> cat mwe.c
#include <math.h>
typedef struct {
int rowCount;
int columnCount;
double* data;
} DenseMatrix;
double foo(DenseMatrix foo) {
if (foo.columnCount <= 0)
return NAN;
if (foo.rowCount <= 0)
return INFINITY;
int idxa = foo.columnCount - 1;
int idxb = foo.rowCount - 1;
return foo.data[idxb*(foo.columnCount) + idxa];
}
julia> data = rand(Float64, 15, 37)
15×37 Matrix{Float64}:
0.599364 0.22981 0.96945 … 0.344901 0.416426 0.192333
0.0278234 0.0835467 0.460888 0.665467 0.825422 0.430733
0.365078 0.155395 0.0188155 0.968414 0.797662 0.628757
0.457193 0.256214 0.374096 0.0370763 0.261907 0.141956
0.181229 0.0202176 0.0417381 0.595971 0.425398 0.873739
0.166689 0.799661 0.30146 … 0.379971 0.0946295 0.709757
0.602595 0.0282483 0.327141 0.211264 0.93809 0.991113
0.446278 0.25726 0.617319 0.422344 0.473222 0.343918
0.331866 0.346359 0.464125 0.649524 0.568934 0.910309
0.00314365 0.0951955 0.523954 0.366409 0.239934 0.273698
0.625282 0.484357 0.646132 … 0.0714788 0.949366 0.181078
0.446451 0.203708 0.637352 0.57657 0.238058 0.800698
0.528139 0.915547 0.15079 0.561895 0.812107 0.940406
0.398491 0.664749 0.708104 0.979059 0.0340735 0.347522
0.965296 0.236917 0.781048 0.507677 0.866314 0.377035
julia> CWrapper(data)
0.3770354806819668
julia> CWrapper(data) === last(data)
true
julia> function foo(s1, s2)
data = rand(Float64, s1, s2)
CWrapper(data) == last(data)
end
foo (generic function with 1 method)
julia> @allocated foo(10, 15)
1296
julia> 10*15*8 # size of the matrix allocated in foo, plus some overhead for the `Vector` struct etc
1200
One thing you will have to be careful about is row- vs column-majorness, since Julia is column major and C-libraries usually do row-major.