在FFI中使用c_void

时间:2014-06-12 18:15:38

标签: ffi rust

我正在努力通过一个接受void的FFI传递一个结构并在另一端读回它。

有问题的库是libtsm,一个终端状态机。它允许您输入输入,然后找出输入后终端的状态。

它将其绘制函数声明为:

pub fn tsm_screen_draw(con: *tsm_screen, draw_cb: tsm_screen_draw_cb, data: *mut c_void) -> tsm_age_t;

其中tsm_screen_draw_cb是由库用户实现的回调,具有签名:

pub type tsm_screen_draw_cb = extern "C" fn(
  con: *tsm_screen,
  id: u32,
  ch: *const uint32_t,
  len: size_t,
  width: uint,
  posx: uint,
  posy: uint,
  attr: *tsm_screen_attr,
  age: tsm_age_t,
  data: *mut c_void
);

这里重要的部分是data参数。它允许用户通过指向自我实现状态的指针,对其进行操作并在绘制后使用它。给出一个简单的结构:

struct State {
  state: int
}

我该如何正确地做到这一点?我不确定如何正确地将指向结构的指针转换为void和back。

1 个答案:

答案 0 :(得分:17)

您无法将结构转换为c_void,但您可以将引用转换为结构*mut c_void并返回使用一些指针强制转换:

fn my_callback(con: *tsm_screen, ..., data: *mut c_void) {
    // unsafe is needed because we dereference a raw pointer here
    let data: &mut State = unsafe { &mut *(data as *mut State) };
    println!("state: {}", data.state);
    state.x = 10;
}

// ...

let mut state = State { state: 20 };
let state_ptr: *mut c_void = &mut state as *mut _ as *mut c_void;
tsm_screen_draw(con, my_callback, state_ptr);

也可以使用std::mem::transmute()函数在指针之间进行转换,但它是比这里真正需要的更强大的工具,应尽可能避免使用。

请注意,您必须格外小心地将不安全的指针转换回引用。如果tsm_screen_draw在另一个线程中调用它的回调或将其存储在一个全局变量中,然后另一个函数调用它,那么调用回调时state局部变量可能会超出范围,这会使你的崩溃程序