如何在Rust中正确包装C函数指针? [英] How to properly wrap a C function pointer in Rust?

查看:409
本文介绍了如何在Rust中正确包装C函数指针?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个带有函数指针的C结构Foo.在我的Rust绑定中,我希望允许用户设置此函数指针,但我希望避免用户不得不处理FFI类型.

I have a C struct Foo with a function pointer. In my Rust bindings, I would like to allow users to set this function pointer, but I would like to avoid users having to deal with FFI types.

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,这样我就可以从wrapped_cb内部调用rust_cb.

My current solution is to create a Rust struct Bar which has a pointer to the bindgen-generated C struct foo_sys::Foo, and in it I have a trait object (rust_cb) that is the actual callback I would like to expose in the Rust API. I set the C cb to point to a wrapped_cb and set the internal pointer to point to Bar, this way I am able to call rust_cb from inside wrapped_cb.

此代码有效,但抱怨访问未初始化的内存.当我使用Valgrind运行它时,在访问wrapped_cb内部的(*bar).rust_cb的位置看到了invalid reads.我不确定自己在做什么错.

This code works but complains about access to uninitialised memory. When I run it with Valgrind, I see invalid reads at the point where I access the (*bar).rust_cb inside wrapped_cb. I am not sure what I am doing wrong.

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);
    }
}

我看了一些相关问题,这些问题似乎可以回答我的问题,但可以解决不同的问题:

I looked at related questions which seem to answer my question but solve different problems:

这使用dlsym从C调用Rust回调.

This uses dlsym to call a Rust callback from C.

  • 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函数指针传递的解决方案.

These describe solutions for passing closures as C function pointers.

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

What I am trying to achieve is to have a Rust struct (Bar) which has a member variable ptr that points to a C struct (Foo), which itself has a void *internal that points back to the Rust struct Bar.

这个想法是在C结构Foo中每个函数指针在Rust结构Bar中具有一个特征对象和包装函数.创建Bar对象后,我们将执行以下操作:

The idea is to have one trait object and wrapper function in Rust struct Bar per function pointer in C struct Foo. When a Bar object is created, we do the following:

  • 创建C Foo,并在Bar中保留指向它的指针.
  • Foo->callback指向包装Rust函数.
  • Foo->internal指向Bar.
  • create C Foo and keep a pointer to it in Bar.
  • Point Foo->callback to wrapper Rust function.
  • Point Foo->internal to Bar.

由于包装器函数传递了internal指针,因此我们能够获得指向Bar的指针并调用相应的闭包(来自trait obj).

Since the wrapper function is passed the internal pointer, we are able to get a pointer to Bar and call the respective closure (from trait obj).

我可以将C void*指向我的Rust结构,也可以从Rust回调(或闭包)中获取指向它的指针,这是相关问题所解决的.我 am 面临的问题可能与生命周期有关,因为其中一个值的寿命不足以在回调中可用.

I am able to point the C void* to my Rust struct and I am also able to get a pointer to it from a Rust callback (or closure), which is what the related questions address. The problem I am facing is that probably related to lifetimes because one of the values isn't living long enough to be available in the callback.

推荐答案

这是Bar::new()函数中的错误(由@Shepmaster标识),这是由于我对Rust移动语义的基本误解造成的.通过使Bar::new()返回Box<Bar>-

This is a bug (identified by @Shepmaster) in the Bar::new() function, caused due to my fundamental misunderstanding of Rust move semantics. Fixed by having Bar::new() return a 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
        }
    }
}

这篇关于如何在Rust中正确包装C函数指针?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆