我如何使用cbindgen返回并释放Box >?

问题描述 投票:1回答:1

我有一个从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,
    }))
}

C标头目前是手工编写的,但我想尝试cbindgen。 s_arr的手动C定义是:

struct s_arr {
    struct s *arr;
    int8_t n;
    void *_;
};

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;

由于未定义struct Box_Vec_s,因此无法使用。理想情况下,我只想覆盖为vec生成的cbindgen类型,使其变为void *,因为它不需要更改代码,因此也不需要任何其他测试,但是我愿意接受其他建议。

我浏览了cbindgen文档,虽然不是示例,但找不到任何东西。

rust ffi
1个回答
0
投票

您的问题尚不清楚,但我认为,如果我理解正确,您就会混淆两件事,结果被引向黑暗的小巷。

在C中,您可能知道,动态大小的数组由两件事标识:

  1. 其起始位置,作为指针
  2. 其长度

铁锈遵循相同的约定-引擎盖下方的Vec<_>具有相同的结构(很好,几乎。它也具有容量,但这不重要)。

传递指针的装箱矢量在顶部不仅过分,而且极其不明智。 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
}

使用此,cbindgen返回预期的标头定义:

typedef struct {
  uint8_t foo;
} so58311426S;

typedef struct {
  so58311426S *arr;
  uintptr_t n;
  uintptr_t cap;
} so58311426s_arr;

so58311426s_arr gen_s_arr(void);

这允许我们从C或Rust调用gen_s_arr()并检索可在FFI边界的两个部分(so58311426s_arr)上使用的结构。该结构包含修改S数组(根据cbindgen的so58311426S)所需的全部内容。

通过FFI时,您需要确保一些简单的事情:

  • 您不能传递原始框或非原始类型;您几乎都需要转换为一组指针或将定义更改为适应(如我在这里所做的那样)]
  • 您最多确定地不要传递原始向量。最多可以传递一个切片,因为它是原始类型(请参见上面的要点)。
  • 您确保std::mem::forget()您不想取消分配的任何内容,并确保记住要对其进行取消分配或在其他地方重新设置它。

我将在一小时内编辑此问题;我要坐飞机去。让我知道其中是否需要澄清,一旦我在正确的国家/地区就可以解决:-)

© www.soinside.com 2019 - 2024. All rights reserved.