我正在使用 cxx 为 C++ 库编写一个 Rust 包装器。
此库中经常遇到的一种数据类型是可变大小的向量,其中每个元素都是固定大小的向量 - 例如包含 N 个三维点的 N 长度向量,每个点由 3 个双精度数组成。
查看 cxx 支持的绑定,似乎最明显的方法如下所示,将
Vec<[u64; 3]>
绑定到 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);
但是,这会产生以下错误:
error[cxxbridge]: unsupported element type of Vec
┌─ src/main.rs:17:19
│
17 │ fn foo(x: Vec<[u64; 3]>);
│ ^^^^^^^^^^^^^ unsupported element type of Vec
解决方案确实有效,但并不理想:
创建共享结构,如
Point3D
,并使用Vec<Point3D>
。很烦人,因为我需要为每个变体提供一个新的结构。它也不干净,因为(在 3D 点的情况下)我已经有 Rust 和 C++ 中的数据类型用于表示点,所以这种类型实际上只在桥中使用。
展平为
Vec<u64>
,然后展平为某种 Rust 类型。如果这是唯一的方法就好了,但感觉有点混乱。
在我开始为我需要在包装器中涵盖的所有各种方法重复此模式之前,我希望有一种更干净的方法。
不幸的是,C++ 不直接支持数组向量 (Vec<[u64; 3]>)。您可以通过定义封装固定大小数组的新包装类型来解决此限制。
#[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
}
}
然后在C++中定义包装类型:
// foo.h
#include "rust/cxx.h"
#include <array>
struct Point3D {
std::array<uint64_t, 3> data;
};
void foo(rust::Vec<Point3D> x);
现在实现C++函数:
// 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;
}
}