在FFI中使用c_void [英] Working with c_void in an FFI

查看:99
本文介绍了在FFI中使用c_void的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我很努力地将结构传递给FFI,该结构接受void并在另一端重新读取它.

有问题的库是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并返回.

解决方案

您不能将 struct 强制转换为c_void,但是可以将 reference 强制转换为将结构转换为*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局部变量很可能在调用该回调时超出范围,这将使您的程序崩溃.

I am struggling with passing a struct through an FFI that accepts void and reading it back on the other end.

The library in question is libtsm, a terminal state machine. It allows you to feed input and then find out in which state a terminal would be after the input.

It declares its draw function as:

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

where tsm_screen_draw_cb is a callback to be implemented by the library user, with the signature:

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
);

The important part here is the data parameter. It allows the user to pass through a pointer to a self-implemented state, to manipulate it and use it after drawing. Given a simple struct:

struct State {
  state: int
}

how would I do that properly? I am unsure how to properly cast the pointer to the struct to void and back.

解决方案

You can't cast a struct to c_void, but you can cast a reference to the struct to *mut c_void and back using some pointer casts:

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);

It is also possible to use std::mem::transmute() function to cast between pointers, but it is much more powerful tool than is really needed here and should be avoided when possible.

Note that you have to be extra careful casting an unsafe pointer back to a reference. If tsm_screen_draw calls its callback in another thread or stores it in a global variable and then another function calls it, then state local variable may well go out of scope when the callback is called, which will crash your program.

这篇关于在FFI中使用c_void的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆