如何在Rust中正确包装C函数指针? [英] How to properly wrap a C function pointer in Rust?
问题描述
我有一个带有函数指针的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 inBar
. - Point
Foo->callback
to wrapper Rust function. - Point
Foo->internal
toBar
.
由于包装器函数传递了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屋!