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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With