Certainly this is possible. Letβs say you had the following C++ code, which is compiled into a shared library:
test_lib.cpp
#include <stdlib.h>
struct NodeData {
float position[3];
float orientation[4];
unsigned int connectedNodeIndices[5];
};
extern "C" {
NodeData* alloc_nodes(int N) {
// We use malloc rather than `operator new[]` here so that Julia
// can just use `free()` on the other side.
// If your application differs in how it allocates memory, you may
// need to write a `delete_nodes()` function for Julia to call.
NodeData* data = (NodeData*)malloc(sizeof(NodeData)*N);
for(int i = 0; i < N; ++i) {
data[i].position[0] = i;
data[i].position[1] = 0;
data[i].position[2] = -i;
// ... and fill in other fields
}
return data;
}
} // extern "C"
Iβll assume you want to call your C++ code from Julia (rather than embedding Julia in C++) as this is much easier to demonstrate.
To make this into a shared library which you can call from Julia, you can use gcc
, for example with the following flags (assuming youβre compiling on linux):
g++ -fPIC -shared test_lib.cpp -o test_lib.so
test_lib.jl
Now you can make a Julia struct
to mirror the exact layout of your C++ struct
, and call your C++ function alloc_nodes()
from Julia using @ccall
:
# StaticArrays is convenient here, but not required. You could use
# NTuple{3,Cfloat} to get the same memory layout as SVector{3,Cfloat}
using StaticArrays
struct NodeData
position::SVector{3,Cfloat}
orientation::SVector{4,Cfloat}
connectedNodeIndices::SVector{5,Cuint}
end
# Assume test_lib.so is in the current directory with the Julia script
const test_lib = "./test_lib.so"
function alloc_nodes(N)
node_data_ptr = @ccall test_lib.alloc_nodes(N::Cint)::Ptr{NodeData}
# Here we wrap the C++ data in a Julia Array for convenience.
# This isn't strictly necessary - you could use `unsafe_load`
# with node_data_ptr instead.
# Note `own=true` here β in this example, we assume Julia will take
# responsibility for calling `free(node_data_ptr)` for memory cleanup.
node_data = unsafe_wrap(Array, node_data_ptr, N; own=true)
end
function test_interop()
N = 4
nodes = alloc_nodes(N)
for i = 1:4
@info "Node $i" nodes[i].position
end
end
Calling this from the julia REPL, we can see the data is being correctly passed from C++ to Julia:
julia> test_interop()
β Info: Node 1
β (nodes[i]).position =
β 3-element SVector{3, Float32} with indices SOneTo(3):
β 0.0
β 0.0
β 0.0
β Info: Node 2
β (nodes[i]).position =
β 3-element SVector{3, Float32} with indices SOneTo(3):
β 1.0
β 0.0
β -1.0
β Info: Node 3
β (nodes[i]).position =
β 3-element SVector{3, Float32} with indices SOneTo(3):
β 2.0
β 0.0
β -2.0
β Info: Node 4
β (nodes[i]).position =
β 3-element SVector{3, Float32} with indices SOneTo(3):
β 3.0
β 0.0
β -3.0