I'm writing a Rust wrapper for a C++ library using cxx.
One frequently encountered data type in this library are vectors of variable size, where each element is a vector of fixed size - e.g. an N-length vector containing N three-dimensional points, each made up of 3 doubles.
Looking at the cxx supported bindings, it seems like the obvious way is something like the below, binding Vec<[u64; 3]> to rust::Vec<std::array<uint64_t, 3>>:
// main.rs
include!("include/foo.h");
#[cxx::bridge]
pub mod ffi {
unsafe extern "C++" {
fn foo(x: Vec<[u64; 3]>);
}
}
// foo.h
void foo(rust::Vec<std::array<uint64_t, 3>> x);
However, this produces the following error:
error[cxxbridge]: unsupported element type of Vec
┌─ src/main.rs:17:19
│
17 │ fn foo(x: Vec<[u64; 3]>);
│ ^^^^^^^^^^^^^ unsupported element type of Vec
Solutions that do work, but aren't ideal:
Creating a shared struct, like Point3D, and using Vec<Point3D>. Annoying because I'd need a new struct for every variation. It's also not clean, because (in the case of 3D points) I have data types in Rust and C++ already for representing points, so this type is really only used in the bridge.
Flattening into a Vec<u64> and then unflattening to some Rust type. Fine if it's the only way, but feels a little messy.
I was hoping there would be a cleaner way, before I start repeating this pattern all over for all of the various methods I need to cover in my wrapper.
Unfortunately, C++ doesn't directly support vectors of arrays (Vec<[u64; 3]>). You can work around this limitation by defining a new wrapper type that encapsulates the fixed-size array.
#[cxx::bridge]
mod ffi {
struct Point3D {
data: [u64; 3],
}
extern "C++" {
include!("include/foo.h");
fn foo(x: Vec<Point3D>);
}
}
impl From<[u64; 3]> for ffi::Point3D {
fn from(arr: [u64; 3]) -> Self {
ffi::Point3D { data: arr }
}
}
impl From<ffi::Point3D> for [u64; 3] {
fn from(point: ffi::Point3D) -> Self {
point.data
}
}
Then define the wrapper type in C++:
// foo.h
#include "rust/cxx.h"
#include <array>
struct Point3D {
std::array<uint64_t, 3> data;
};
void foo(rust::Vec<Point3D> x);
Now implement the C++ function:
// foo.cpp
#include "foo.h"
#include <iostream>
void foo(rust::Vec<Point3D> x) {
for (const auto& point : x) {
std::cout << "Point: [" << point.data[0] << ", " << point.data[1] << ", " << point.data[2] << "]" << std::endl;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With