簡體   English   中英

可以安全地將沒有repr(C)的struct傳遞給C API嗎?

[英]Safe to pass struct without repr(C) to C API?

根據Shepmaster的評論建議提出新問題

假設一個庫定義了一個沒有#[repr(C)] 如果需要將結構傳遞給C API,有沒有一種安全的方法呢? 我是否應該擔心Rust編譯器會以C API不期望的方式更改結構的內存布局?

我見過一些圖書館這樣做。 根據我的經驗, 他們在將結構傳遞給C函數時使用mem::transmute transmute是否會以某種方式消除#[repr(C)]

如果我是定義結構的人,我不會問。 我只想添加#[repr(C)] 問題是我想使用我無法控制的庫中的結構。

老問題

我一直在使用cgmath-rs ,如果cgmath 被放棄 ,我可能會切換到nalgebra

在這兩個庫的源代碼中,我沒有在向量和矩陣結構上看到#[repr(C)] 那么這些庫如何與OpenGL API兼容? 當您將指針傳遞給向量和矩陣時,OpenGL需要一定的內存布局。 我的理解是,沒有#[repr(C)] ,Rust結構的內存布局是未定義的。

至少在一個例子中 ,我看到mem::transmute在將它們交給OpenGL之前應用於這些結構。 我可能會誤解transmute ,但該函數似乎保留了struct的內存布局。 所以,如果布局是錯誤的,首先,它仍然是錯誤的之后transmute ,是否正確?

我也考慮過vecmath 但看起來,根據設計,vecmath缺乏輔助函數來生成旋轉矩陣等。 沒錯,我可以實現這些,但不必這樣做會很好。 無論如何,vecmath的設計是否通過使用數組而不是結構來避免內存布局的問題?

我見過一些圖書館這樣做。 根據我的經驗,他們在將結構傳遞給C函數時使用mem :: transmute。 轉化是否會以某種方式消除#[repr(C)]的需要?

沒有。

......但它很復雜。

如果你有一個&Foo,並且你將它傳遞給C並且根本不編輯它 ,那么將你的&Foo轉換為* const c_void並將其傳遞給ac調用是完全有效的,例如:

let fp = &foo as *const Foo as *const c_void

你可能會看到人們這樣做是一步,使用transmute;

unsafe { ffi_call(transmute(&foo), ...) }

...但重要的是要理解轉化調用不會修改內存布局; 但它確實消耗了價值

例如,此代碼可能會導致以后出現段錯誤:

{
  let foo = Foo { ... }
  unsafe { ffi_call(&foo as _ as *const c_void); }
}

這是因為指針&foo指向foo; 但是在范圍結束后foo停止存在; 因此,如果稍后使用(例如,ffi調用保持引用),將導致段錯誤。

你可能會認為拳擊(即移動到堆)修復了這個問題:

let foo = Box::new(Foo { ... })
unsafe { ffi_call(&*foo as *const c_void); }

......但它沒有; 因為當Box離開范圍時,它會被丟棄。 但是,因為轉化會改變價值; 此代碼安全地將foo實例移動到ffi調用中以供以后使用; 請注意,如果以后不恢復該值,則會出現內存泄漏:

let foo = Box::new(Foo { ... })
unsafe { ffi_call(transmute(foo)); }

...但是沒有使用transmute()可以解決repr(C)的缺失,是的,你可以期待生銹與你的結構布局混亂; 這通常與drop標志有關,並且可以作為https://github.com/rust-lang/rfcs/pull/320的一部分解決,因為drop flags目前是內存布局到結構的唯一有形的不同; 但由於RFC沒有明確涵蓋,我不會屏住呼吸。

即。 tldr; 如果你需要傳遞一個結構並在C中修改它,它需要repr(C); 如果沒有,它將無法工作**。

轉化被用於其他原因,完全與此無關。

** - >好吧,它可能有效,但你真正打的是未定義的行為。 如果您正在使用的庫工作,可能是因為它恰好起作用。 生銹很多東西都是如此; 例如。 在某些情況下可變別名...但它確實意味着破壞了代碼。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM