gpt4 book ai didi

c - 如何在 Rust 中正确包装 C 函数指针?

转载 作者:行者123 更新时间:2023-12-03 11:25:06 27 4
gpt4 key购买 nike

这个问题在这里已经有了答案:





Why does the address of an object change across methods?

(2 个回答)


2年前关闭。




我有一个 C 结构 Foo带有函数指针。在我的 Rust 绑定(bind)中,我想允许用户设置这个函数指针,但我想避免用户不得不处理 FFI 类型。

foo.h

struct Foo {
void* internal;
uint8_t a;
void (*cb_mutate_a)(void*);
};

struct Foo* foo_new();
void foo_free(struct Foo* foo);
void foo_call(struct Foo* foo);

foo.c

struct Foo* foo_new() {
return calloc(1, sizeof(struct Foo));
}

void foo_free(struct Foo* foo) {
free(foo);
}

void foo_call(struct Foo* foo) {
return foo->cb_mutate_a(foo->internal);
}

我目前的解决方案是创建一个 Rust 结构 Bar它有一个指向 bindgen 生成的 C 结构 foo_sys::Foo 的指针,其中我有一个特征对象( rust_cb ),这是我想在 Rust API 中公开的实际回调。我设置了 C cb指向 wrapped_cb并设置 internal指向 Bar的指针,这样我就可以调用 rust_cb从内部 wrapped_cb .

此代码有效,但提示访问未初始化的内存。当我使用 Valgrind 运行它时,我看到了 invalid reads在我访问 (*bar).rust_cb 的地方里面 wrapped_cb .我不确定我做错了什么。

extern crate libc;

use std::ffi;

#[repr(C)]
#[derive(Debug, Copy)]
pub struct Foo {
pub internal: *mut libc::c_void,
pub a: u8,
pub cb_mutate_a: ::core::option::Option<unsafe extern "C" fn(arg1: *mut libc::c_void)>,
}

impl Clone for Foo {
fn clone(&self) -> Self {
*self
}
}

extern "C" {
pub fn foo_new() -> *mut Foo;
}
extern "C" {
pub fn foo_free(foo: *mut Foo);
}
extern "C" {
pub fn foo_call(foo: *mut Foo);
}

struct Bar {
ptr: *mut Foo,
rust_cb: Option<Box<dyn FnMut(&mut u8)>>,
}

impl Bar {
fn new() -> Bar {
unsafe {
let mut bar = Bar {
ptr: foo_new(),
rust_cb: Some(Box::new(rust_cb)),
};
(*bar.ptr).cb_mutate_a = Some(cb);
let bar_ptr: *mut ffi::c_void = &mut bar as *mut _ as *mut ffi::c_void;
(*bar.ptr).internal = bar_ptr;
bar
}
}
}

impl Drop for Bar {
fn drop(&mut self) {
unsafe {
foo_free(self.ptr);
}
}
}

extern "C" fn cb(ptr: *mut libc::c_void) {
let bar = ptr as *mut _ as *mut Bar;
unsafe {
match &mut (*bar).rust_cb {
None => panic!("Missing callback!"),
Some(cb) => (*cb)(&mut (*(*bar).ptr).a),
}
}
}

fn rust_cb(a: &mut u8) {
*a += 2;
}

fn main() {
unsafe {
let bar = Bar::new();
let _ = foo_call(bar.ptr);
}
}

我查看了相关问题,这些问题似乎回答了我的问题,但解决了不同的问题:
  • Rust FFI passing trait object as context to call callbacks on

  • 这使用 dlsym从 C 调用 Rust 回调。
  • How do I convert a Rust closure to a C-style callback?
  • How do I pass a closure through raw pointers as an argument to a C function?

  • 这些描述了将闭包作为 C 函数指针传递的解决方案。

    我想要实现的是拥有一个具有成员变量 Bar 的 Rust 结构( ptr )指向一个 C 结构 ( Foo ),它本身有一个 void *internal指向 Rust 结构 Bar .

    这个想法是在 Rust 结构 Bar 中拥有一个特征对象和包装函数C 结构中的每个函数指针 Foo .当一个 Bar创建对象后,我们执行以下操作:
  • 创建 C Foo并在 Bar 中保留指向它的指针.
  • Foo->callback包装 Rust 函数。
  • Foo->internalBar .

  • 由于传递了包装函数 internal指针,我们可以得到一个指向 Bar 的指针并调用相应的闭包(来自 trait obj)。

    我可以指向 C void*到我的 Rust 结构,我还能够从 Rust 回调(或闭包)获得指向它的指针,这就是相关问题所要解决的问题。我面临的问题可能与生命周期有关,因为其中一个值的生命周期不够长,无法在回调中使用。

    最佳答案

    这是Bar::new() 中的一个错误(由@Shepmaster 标识)。函数,是由于我对 Rust 移动语义的根本误解而引起的。已通过 Bar::new() 修复返回 Box<Bar> -

    impl Bar {
    fn new() -> Box<Bar> {
    unsafe {
    let mut bar = Box::new(Bar { ptr: foo_sys::foo_new(), rust_cb: Some(Box::new(rust_cb)) });
    (*bar.ptr).cb_mutate_a = Some(cb);
    let bar_ptr: *mut ffi::c_void = &mut *bar as *mut _ as *mut ffi::c_void;
    (*bar.ptr).internal = bar_ptr;
    bar
    }
    }
    }

    关于c - 如何在 Rust 中正确包装 C 函数指针?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60969071/

    27 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com