[英]How to use from C# a Rust function that has another function as a parameter?
下面的例子說明了我想要做什么。 操作function 接收一個值和一個 function 作為參數,然后返回 function 的執行。 到目前為止,一切都很完美。 現在我想在庫中使用 function操作以在 C# 中使用。
fn operation(number: i32, f: &dyn Fn(i32) -> i32) -> i32 {
f(number)
}
fn square(number: i32) -> i32 {
number * number
}
fn cube(number: i32) -> i32 {
number * number * number
}
fn test() {// passing a function as parameter, OK :)
println!("{}", operation(5, &square));
println!("{}", operation(7, &square));
println!("{}", operation(3, &cube));
}
// Tried without success :_(
/*
#[no_mangle]
pub extern "C" c_operation(number: i32, f: &dyn Fn(i32) -> i32) -> *mut i32 {
unsafe {
&mut f(number)
}
}
*/
function c_operation
的正確簽名應該是什么?
我認為當簽名在庫中定義良好時,以下 C# 代碼應該可以工作。
const string RSTLIB = "../../PathOfDLL";
public delegate int Fn(int number);
[DllImport(RSTLIB)] static extern int c_operation(int x, Fn fn);
int Square(int number) => number * number;
int Cube(int number) => number * number * number;
void Test()
{
Console.WriteLine("{0}", c_operation(5, Square));
Console.WriteLine("{0}", c_operation(7, Square));
Console.WriteLine("{0}", c_operation(3, Cube));
}
感謝您的關注
dyn
指針不是 FFI 安全的,因此它們不能通過extern "C"
屏障。 您實際上會收到關於此的警告:
警告:
extern
fn 使用類型dyn Fn(i32) -> i32
,這不是 FFI 安全的。
但是,您可以傳遞一個普通的 function 指針,只要它也是extern "C"
,當然):
#[no_mangle]
pub extern "C" fn c_operation(number: i32, f: extern "C" fn(i32) -> i32) -> *mut i32 {
unsafe {
&mut f(number)
}
}
(您正在返回一個指向本地臨時的懸空指針,但我想這只是因為您正在編寫虛擬代碼。)
然后,從 C 開始,你可以使用普通函數,但是沒有地方放置閉包數據。 That would be an issue for C or C++, but not for C#, because it does some magic that creates raw plain function pointers from any delegate, as long as you decorate the delegate type properly:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int Fn(int number)
然后,您的c_operation()
聲明應該可以正常工作,除了 C# int
中的返回類型,而 Rust 版本返回一個指針。 這對應於IntPtr
:
[DllImport(RSTLIB)] static extern IntPtr c_operation(int x, Fn fn);
另外,請注意,如果c_operation()
存儲指針fn
以便稍后調用它,那么在 C# 中,您必須保持委托 object 處於活動狀態,或者將其保存在變量中,或者為其分配GCHandle
。
Fn fSquare = Square;
//keep fSquare somewhere
c_operation(5, fSquare);
如果你不這樣做,垃圾收集器收集自動臨時委托(編譯器為你創建的),然后 Rust 代碼嘗試調用存儲的 function 指針......好吧,它不會很好。
對於 C 的使用,聲明c_operation
並根據operation
定義它的一種方法是:
#[no_mangle]
pub extern "C" fn c_operation(
number: i32,
f: unsafe extern "C" fn(i32) -> i32
) -> i32 {
operation(number, &|n| unsafe { f(n) })
}
User4815162342的回答正是我想要的。 I would like to add that it is surprising that you can pass a C# function as a parameter to a Rust function, and get a result. 同樣,我們可以從 C# 將 Rust function 傳遞給回調中的同一個 ZF5E265D607CB020058ZFC166 庫。 為了結束這個問題,我將展示結果。
// user4815162342 answer:
#[no_mangle]
pub extern "C" fn c_operation(number: i32, f: unsafe extern "C" fn(i32) -> i32) -> i32 {
operation(number, &|n| unsafe { f(n) })
}
// by example
#[no_mangle]
fn cube(number: i32) -> i32 {
number * number * number
}
C# 代碼:
const string RSTLIB = "...\release\rstlib.dll";
public delegate int Fn(int number);
[DllImport(RSTLIB)] static extern int c_operation(int number, Fn fn);
[DllImport(RSTLIB)] static extern int cube(int number);
int Square(int number) => number * number;
int Cube(int number) => number * number * number;
public void Test()
{
// passing a C# function as parameter of Rust function
Console.WriteLine("c_operation(5, Square) : {0}", c_operation(5, Square));
Console.WriteLine("c_operation(3, Cube) : {0}", c_operation(3, Cube));
// passing a Rust function as callback inside the same Rust function
Console.WriteLine("c_operation(5, cube) : {0}", c_operation(5, cube));
// Output
// c_operation(5, Square) : 25
// c_operation(3, Cube) : 27
// c_operation(5, cube) : 125
}
感謝您的關注。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.