web-dev-qa-db-ja.com

FFIでのc_voidの操作

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
}

どうすればそれを適切に行うことができますか?構造体へのポインタを正しくキャストして無効にしたり戻したりする方法がわかりません。

16
Skade

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ローカル変数がスコープ外になる可能性があり、クラッシュします。あなたのプログラム。

22