簡體   English   中英

如何使用從 C# 到 Rust function 有另一個 ZC1C425268E68385D1AB5074C 作為參數?

[英]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.

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