gpt4 book ai didi

rust - 使用 C 回调用户数据存储盒装 Rust 闭包时出现段错误

转载 作者:行者123 更新时间:2023-11-29 08:18:25 24 4
gpt4 key购买 nike

我正在围绕 C API 创建一个 Rust 包装器。此 C API 中的一个函数设置回调并接受将传递给回调的空指针。它存储了对回调和用户数据的引用供以后使用,所以我使用了 this answer 中的最后一个代码部分。 .

这是我的代码。 Test::trigger_callback(...) 函数旨在模拟调用回调的 C 库。

extern crate libc;

use libc::c_void;
use std::mem::transmute;

struct Test {
callback: extern "C" fn(data: i32, user: *mut c_void) -> (),
userdata: *mut c_void,
}

extern "C" fn c_callback(data: i32, user: *mut libc::c_void) {
unsafe {
println!("Line {}. Ptr: {}", line!(), user as u64);
let func: &mut Box<FnMut(i32) -> ()> = transmute(user);
println!("Line {}. Data: {:?}", line!(), data);
(*func)(data);
println!("Line {}", line!());
}
}

impl Test {
fn new<F>(func: F) -> Test
where
F: FnMut(i32) -> (),
F: 'static,
{
let func = Box::into_raw(Box::new(Box::new(func)));
println!("Line: {}, Ptr: {}", line!(), func as u64);

Test {
callback: c_callback,
userdata: func as *mut c_void,
}
}

fn trigger_callback(&self, data: i32) {
(self.callback)(data, self.userdata);
}
}

fn main() {
let test = Test::new(|data: i32| {
println!("Inside callback! Data: {}", data);
});

test.trigger_callback(12345);
}

如链接答案中所述,Box闭包意味着将其存储在堆上,以便指向它的指针在任意长的时间内都有效,然后 Box Box 是因为它是一个胖指针,但需要转换为常规指针,以便可以将其转换为 void 指针。

运行时,这段代码打印出来:

Line: 29, Ptr: 140589704282120
Line 13. Ptr: 140589704282120
Line 15. Data: 12345
Segmentation fault (core dumped)

尝试在 extern "C" 函数中调用闭包时会出现段错误。

为什么?据我了解,将闭包放在 Box 中然后使用 Box::into_raw(...) 应该将其存储在堆上并“泄漏”内存,因此只要程序运行,指针就应该有效。这哪一部分是错误的?

最佳答案

Box::into_raw(Box::new(Box::new(func)));

这不会产生您认为的类型:

   = note: expected type `()`
found type `*mut std::boxed::Box<F>`

你假设它是一个特征对象:

let func: &mut Box<FnMut(i32) -> ()> = transmute(user);

相反,在装箱时将输入值放入特征对象。我提倡用注释解释每一步的明确行:

// Trait object with a stable address
let func = Box::new(func) as Box<FnMut(i32)>;
// Thin pointer
let func = Box::new(func);
// Raw pointer
let func = Box::into_raw(func);

Box<FnMut(i32) -> ()>

() 的返回类型是多余的;使用 Box<FnMut(i32)>

let func: &mut Box<FnMut(i32) -> ()> = transmute(user);

努力避免transmute .通常可以使用较小的工具:

extern "C" fn c_callback(data: i32, user: *mut libc::c_void) {
let user = user as *mut Box<FnMut(i32)>;
unsafe {
(*user)(data);
}
}

避免重复相同的类型。引入一个类型别名:

type CallbackFn = Box<FnMut(i32)>;
let user = user as *mut CallbackFn;
let func = Box::new(func) as CallbackFn;

另见:

关于rust - 使用 C 回调用户数据存储盒装 Rust 闭包时出现段错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50955611/

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