Hello, is there a simple/built in way to pass array of C++ stucts from Julia to c++?
C++ code:
struct Foo
{
std::wstring name;
int dataLen;
double* data;
};
void useFooVec(const std::vector<Foo>& v) # how to deal with the vector<T> to be called from julia?
{
// ...
}
struct CFoo
name::Ptr{Cwchar_t}
len::Cint
data::Ptr{Float64}
end
struct JFoo
name::String
data::Vector{Float64}
end
function Base.cconvert(x::JFoo)
# Might work a bit simpler, but for all I know this is the official way that makes sure we preserve gc roots!
nptr = Base.unsafe_convert(Ptr{Cwchar_t}, Base.cconvert(Cwstring, x.name))
dptr = Base.unsafe_convert(Ptr{Float64}, Base.cconvert(Ptr{Float64}, x.data))
CFoo(nptr, Cint(length(x.data)), dptr)
end
I think that’s what you need to do… Sorry for the awkwardness, I think cstruct compatibility needs a revamp edit
you might also want to see the ccall signature
If you can modify the type on the C++ side to add a constructor, and if you don’t need a “mirror” struct on the Julia side, this would be the current way to do it in CxxWrap:
using CxxWrap
wrap_modules(raw"PATH_TO_MODULE\foo.dll")
using FooMod: Foo, name, data, print_foo_array
foovec = Any[Foo("a", [1.0, 2.0, 3.0]), Foo("b", [11.0, 12.0, 13.0])] # Must be Any because of the boxing
@show name(foovec[1])
@show data(foovec[1])
print_foo_array(foovec)
This approach is not ideal:
The structs are boxed, so you have an array of pointers. To pack the data, you have to use @sdanisch 's approach with an intermediate isbits struct
Currently std::vector is not wrapped, so if you need that in your API it will need extra work
ArrayRef doesn’t support boxed values directly, so you need the jl_value_t* and unbox_wrapped_ptr ugliness.
barche, thanks a lot
I’ve been approaching your solution slowly… I’ve modified Foo on the C++ side, but the approach with the data copying looks much better.
And the trick with print_foo_array...
Despite the not wrapped std::vector and the boxed ugliness, the solution looks great.
Could you please explain, what work is needed to wrap the std::vector?
Thanks to everyone who is supporting C++ usage with Julia, great work guys!
OK, great! Note that ArrayRef does not protect the referenced array from garbage collection, so the array should be referenced explicitly on the Julia side if you keep an ArrayRef stored somewhere.
Regarding std::vector, I think that something like what I’m doing for the Trilinos Kokkos View should work. The idea is that you get a pointer to the C++ data, which is wrapped in a Julia type that implements the Array interface. To be completely general, for element types that are non-bits the actual vector should be wrapped and accessed using std::vector access functions, instead of using the raw data pointer. The C++ code: https://github.com/barche/Trilinos.jl/blob/master/deps/src/trilinoswrap/kokkos.cpp
One more question: Is there a way to call a callback defined in Julia where input parameter is Vector?
function testf_arf(v::Vector{Float64}, s::String)
r = sum(v)
print_with_color(:green, "callback in Julia: $(s) = $(r)\n")
return r
end
@show c_func_arf = safe_cfunction(testf_arf, Float64, (CxxWrap.CppArray{Float64}, String))
I’m using Julia 0.6 and the Base.cconvert generates an error, and that was the reason I was preferring the CxxWrap solution.
julia> Base.cconvert(CFoo, jFoo)
ERROR: MethodError: Cannot `convert` an object of type JFoo to an object of type CFoo
This may have arisen from a call to the constructor CFoo(...),
since type constructors fall back to convert methods.
Stacktrace:
[1] cconvert(::Type{T} where T, ::JFoo) at .\essentials.jl:149
the problem can be overcome by writing a constructor for CFoo, which works fine:
struct CFoo
name::Ptr{Cwchar_t}
len::Cint
data::Ptr{Float64}
function CFoo(a::JFoo)
dptr = Base.unsafe_convert(Ptr{Float64}, Base.cconvert(Ptr{Float64}, a.data))
nptr = Base.unsafe_convert(Ptr{Cwchar_t}, Base.cconvert(Cwstring, a.name))
new(nptr, Cint(length(a.data)), dptr)
end
end
jFooVec = [JFoo("a", [1., 2., 3.]), JFoo("b", [11., 12., 13.])]
cFooVec = [CFoo(a) for a in jFooVec]