如何全局存储特征对象以使其可以被C API回调访问?

时间:2015-02-15 19:17:18

标签: rust

假设一个C API在返回之前调用回调。不幸的是,除了全局变量之外,没有办法将数据发送到回调。顺便说一下,只有一个线程。

为了使这个例子编译,我在Rust中添加了一个虚拟实现,真正的东西是extern“C”

unsafe fn c_api(c_api_callback:extern fn()){
  c_api_callback();
}

我想为这个API封装一些状态

pub trait State {
    fn called(&mut self); //c_api_callback should call this on self
}

以通用方式。可以存在多个独立的State实现

struct MyState {
    value:i32
}

impl State for MyState{
    fn called(&mut self){
        println!("I hope this prints 123:{}", self.value);
    }
}

pub fn main(){
    let mut mystate = MyState { value: 123 };
    do_call(&mut mystate);
}

基本问题:我如何实施以下内容?

//rustc says: error: explicit lifetime bound required [E0228]
 static static_state:* mut State=0 as *mut State;

//This doesn't work 
//static static_state:*'static mut State=0 as *mut State;
//error: bare raw pointers are no longer allowed, you should likely use `*mut T`, but otherwise `*T` is now known as `*const T`

extern fn my_callback_impl(){
    static_state.called();
}

pub fn do_call(state:&mut State){
    static_state=state;
    unsafe{
        c_api(my_callback_impl);
    }
    static_state=0 as *mut State;
}

我尝试了各种可怕的解决方法,包括在结构中包含特征并使用transmute将其转换为*u8,并且我收集了很多奇怪的错误消息和编译器崩溃的结果。

由于这是第二次因为生锈中的静电而感到困惑,如果有人对博客提出了一些建议或者说明了这里发生了什么的良好示例代码,我也会感激不尽。

1 个答案:

答案 0 :(得分:2)

'static生命周期实际上并不复杂 - 它只是表示某些东西可以保证在程序的整个生命周期中生存。在这种情况下,根据定义,全局值需要长时间可用。

经常会出现问题,因为人们希望在程序运行期间初始化该全局值,这意味着整个程序

现在,问题的关键。解决方案几乎没有保证它的安全性。

首先,我认为你遇到了阻止你直接存储特征对象的bug。为了解决这个问题,我们将trait对象包装在一个小的虚拟结构(Holder)中,该结构将特征对象提供给生存的地方。

然后,我们将对持有者的引用粘贴到全局,可变,可怕,位置。打电话给回叫,然后,它就在那里,它就在那里!

use std::mem;

struct Holder<'a>(&'a mut (State + 'a)); //'

// You'd truly better never use this in multiple threads!
static mut static_state: *mut Holder<'static> = 0 as *mut _; //'

pub trait State {
    fn called(&mut self);
}

struct MyState {
    value: i32
}

impl State for MyState{
    fn called(&mut self) {
        println!("I hope this prints 123:{}", self.value);
    }
}

unsafe fn c_api(c_api_callback: extern fn()) {
    c_api_callback();
}

extern fn my_callback_impl() {
    // really should check that it's not 0 here...
    let h = unsafe { &mut *static_state };
    h.0.called();
}

pub fn do_call(state: &mut State){
    let h = Holder(state);
    unsafe {
        // Straight-up lie to the compiler: "yeah, this is static"
        static_state = mem::transmute(&h);
        c_api(my_callback_impl);
        static_state = 0 as *mut _;
    }
}

pub fn main(){
    let mut mystate = MyState { value: 123 };
    do_call(&mut mystate);
}