除非C API允许传递用户提供的回调参数,否则你无法这样做。如果不允许,你只能使用静态函数。
原因是闭包不仅仅是函数。正如它们的名字所示,闭包从它们的词法作用域中“关闭”变量。每个闭包都有一个关联的数据片段,其中包含捕获变量的值(如果使用了move
关键字)或对它们的引用。这个数据可以想象成一个未命名的、匿名的struct
。
编译器会自动为这些匿名结构体添加相应的Fn*
trait实现。正如您所看到的,这些trait上的方法除了闭包参数之外还接受self
。在这个上下文中,self
是实现该trait的struct
。这意味着与闭包对应的每个函数也有一个额外的参数,其中包含闭包环境。
如果你的C API只允许你传递没有任何用户定义参数的函数,则无法编写一个包装器,使你能够使用闭包。我猜可能可以编写一些全局的闭包环境持有器,但我怀疑它是否容易和安全。
如果你的C API允许传递用户定义的参数,则可以使用特质对象来实现你想要的功能:
extern crate libc;
use std::mem;
use libc::{c_int, c_void};
extern "C" {
fn do_something(f: Option<extern "C" fn(x: c_int, arg: *mut c_void) -> c_int>, arg: *mut c_void) -> c_int;
}
extern "C" fn do_something_handler(x: c_int, arg: *mut c_void) -> c_int {
let closure: &mut &mut dyn FnMut(i32) -> bool = unsafe { mem::transmute(arg) };
closure(x as i32) as c_int
}
pub fn do_with_callback<F>(x: i32, mut callback: F) -> bool
where F: FnMut(i32) -> bool
{
let mut cb: &mut dyn FnMut(i32) -> bool = &mut callback;
let cb = &mut cb;
unsafe { do_something(Some(do_something_handler), cb as *mut _ as *mut c_void) > 0 }
}
如果do_something
没有将回调函数的指针存储在其他地方,那么这个方法才能起作用。如果它这样做了,你需要使用一个Box<Fn(..) -> ..>
特征对象,并在将其传递给函数后泄漏它。然后,如果可能的话,应该从你的C库中获取并处理掉它。代码示例如下:
extern crate libc;
use std::mem;
use libc::{c_int, c_void};
extern "C" {
fn set_handler(f: Option<extern "C" fn(x: c_int, arg: *mut c_void) -> c_int>, arg: *mut c_void);
fn invoke_handler(x: c_int) -> c_int;
fn unset_handler() -> *mut c_void;
}
extern "C" fn do_something_handler(x: c_int, arg: *mut c_void) -> c_int {
let closure: &mut Box<dyn FnMut(i32) -> bool> = unsafe { mem::transmute(arg) };
closure(x as i32) as c_int
}
pub fn set_callback<F>(callback: F)
where F: FnMut(i32) -> bool,
F: 'static
{
let cb: Box<Box<dyn FnMut(i32) -> bool>> = Box::new(Box::new(callback));
unsafe {
set_handler(Some(do_something_handler), Box::into_raw(cb) as *mut _);
}
}
pub fn invoke_callback(x: i32) -> bool {
unsafe { invoke_handler(x as c_int) > 0 }
}
pub fn unset_callback() {
let ptr = unsafe { unset_handler() };
let _: Box<Box<dyn FnMut(i32) -> bool>> = unsafe { Box::from_raw(ptr as *mut _) };
}
fn main() {
let mut y = 0;
set_callback(move |x| {
y += 1;
x > y
});
println!("First: {}", invoke_callback(2));
println!("Second: {}", invoke_callback(2));
unset_callback();
}
双重间接性(即 Box<Box<...>>
)是必需的,因为 Box<Fn(..) -> ..>
是一个 trait 对象,因此是一个 fat 指针,与 *mut c_void
大小不同而不兼容。
(x, y)
做出所有需要的决策。 - Shepmasterstatic
和全局状态。而且它甚至没有尝试做到线程安全。 - Tomo