我有一个从 Rust 返回到 C 代码的结构。我不知道这是否是一种好方法,但它确实可以重建结构并释放内存而不会泄漏。
#[repr(C)]
pub struct s {
// ...
}
#[repr(C)]
#[allow(clippy::box_vec)]
pub struct s_arr {
arr: *const s,
n: i8,
vec: Box<Vec<s>>,
}
/// Frees memory that was returned to C code
pub unsafe extern "C" fn free_s_arr(a: *mut s_arr) {
Box::from_raw(s_arr);
}
/// Generates an array for the C code
pub unsafe extern "C" fn gen_s_arr() -> *mut s_arr {
let many_s: Vec<s> = Vec::new();
// ... logic here
Box::into_raw(Box::new(s_arr {
arr: many_s.as_mut_ptr(),
n: many_s.len() as i8,
vec: many_s,
}))
}
Run Code Online (Sandbox Code Playgroud)
C 头文件目前是手工编写的,但我想尝试一下 cbindgen。手动 C 定义是s_arr:
struct s_arr {
struct s *arr;
int8_t n;
void *_;
};
Run Code Online (Sandbox Code Playgroud)
cbindgen 生成以下内容s_arr:
typedef struct Box_Vec_s Box_Vec_s;
typedef struct s_arr {
const s *arr;
int8_t n;
Box_Vec_s vec;
} s_arr;
Run Code Online (Sandbox Code Playgroud)
这不起作用,因为struct Box_Vec_s未定义。理想情况下,我只想重写生成的 cbindgen 类型来vec实现它void *,因为它不需要更改代码,因此不需要额外的测试,但我愿意接受其他建议。
我浏览了 cbindgen 文档,尽管没有查看示例,但找不到任何内容。
你的问题有点不清楚,但我认为如果我理解正确的话,你就会混淆两件事,并因此被带入一条黑暗的小巷。
在 C 中,您可能知道,动态大小的数组由两件事标识:
Rust 遵循相同的约定 - aVec<_>在底层共享相同的结构(好吧,几乎。它也有一个容量,但这不是重点)。
在指针之上传递装箱向量不仅是过分的,而且是极其不明智的。FFI 绑定可能很智能,但大多数时候它们还不够智能,无法处理盒装复杂类型。
为了解决这个问题,我们将简化您的绑定。我添加了一个元素来struct S向您展示它是如何工作的。我还清理了您的 FFI 边界:
#[repr(C)]
#[no_mangle]
pub struct S {
foo: u8
}
#[repr(C)]
pub struct s_arr {
arr: *mut S,
n: usize,
cap: usize
}
// Retrieve the vector back
pub unsafe extern "C" fn recombine_s_arr(ptr: *mut S, n: usize, cap: usize) -> Vec<S> {
Vec::from_raw_parts(ptr, n, cap)
}
#[no_mangle]
pub unsafe extern "C" fn gen_s_arr() -> s_arr {
let mut many_s: Vec<S> = Vec::new();
let output = s_arr {
arr: many_s.as_mut_ptr(),
n: many_s.len(),
cap: many_s.capacity()
};
std::mem::forget(many_s);
output
}
Run Code Online (Sandbox Code Playgroud)
这样,cbindgen 返回预期的标头定义:
#[repr(C)]
#[no_mangle]
pub struct S {
foo: u8
}
#[repr(C)]
pub struct s_arr {
arr: *mut S,
n: usize,
cap: usize
}
// Retrieve the vector back
pub unsafe extern "C" fn recombine_s_arr(ptr: *mut S, n: usize, cap: usize) -> Vec<S> {
Vec::from_raw_parts(ptr, n, cap)
}
#[no_mangle]
pub unsafe extern "C" fn gen_s_arr() -> s_arr {
let mut many_s: Vec<S> = Vec::new();
let output = s_arr {
arr: many_s.as_mut_ptr(),
n: many_s.len(),
cap: many_s.capacity()
};
std::mem::forget(many_s);
output
}
Run Code Online (Sandbox Code Playgroud)
这允许我们从 C 或 Rust 调用gen_s_arr()并检索可跨 FFI 边界 ( ) 的两个部分使用的结构so58311426s_arr。这个结构包含我们需要能够修改我们的数组的所有内容S(好吧,so58311426S根据 cbindgen)。
通过 FFI 时,您需要确定一些简单的事情:
std::mem::forget()您不想取消分配任何内容,并确保记住取消分配或在其他地方对其进行改革。我将在一小时内编辑这个问题;我有一架飞机要登机。如果有任何需要澄清的地方,请告诉我,我会在到达正确的国家/地区后进行说明:-)
| 归档时间: |
|
| 查看次数: |
1533 次 |
| 最近记录: |