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
}
どうすればそれを適切に行うことができますか?構造体へのポインタを正しくキャストして無効にしたり戻したりする方法がわかりません。
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
ローカル変数がスコープ外になる可能性があり、クラッシュします。あなたのプログラム。