簡體   English   中英

如何通過 C-FFI 從 Rust 調用 Nim 函數?

[英]How to call a Nim function from Rust through C-FFI?

Nim 后端集成指南描述了如何從 C 調用 Nim 函數。

示例函數:

proc fib(a: cint): cint {.exportc.} =
  if a <= 2:
    result = 1
  else:
    result = fib(a - 1) + fib(a - 2)

該過程要求指示 Nim 編譯器不要創建main函數,避免從以下位置鏈接和創建頭文件到 FFI:

$ nim c --noMain --noLinking --header:fib.h fib.nim

為了能夠使用該函數,C main 必須調用一個名為NimMain()的函數,如下所示:

#include "fib.h"
#include <stdio.h>

int main(void)
{
  NimMain();
  for (int f = 0; f < 10; f++)
    printf("Fib of %d is %d\n", f, fib(f));
  return 0;
}

前面提到的生成的頭文件放在nimcache目錄下。 必須指示 C 編譯器編譯生成的nimcache子目錄nimbase.hmain.c下的所有文件:

$ gcc -om -I$HOME/.cache/nim/fib_d -Ipath/to/nim/lib $HOME/.cache/nim/fib_d/*.c maths.c

如何指示 rust 編譯器在nimcache下查找那些翻譯單元?

在 Rust 項目中,可以使用構建腳本來編譯和鏈接第三方非 Rust 代碼。 結合cc crate使調用 C/C++ 編譯器更容易,這很有趣。

項目布局:

├── build.rs
├── Cargo.toml
└── src
    ├── fib.nim
    └── main.rs

build.rs本身:

use std::io::{self, Write};
use std::process::Command;

fn main() {
    let output = Command::new("nim")
        .arg("c")
        .arg("--noMain")
        .arg("--noLinking")
        .arg("--nimcache:nimcache")
        .arg("src/fib.nim")
        .output()
        .expect("Failed to invoke nim compiler");
    if !output.status.success() {
        let msg = String::from_utf8_lossy(output.stderr.as_slice());
        let _ = writeln!(io::stderr(), "\nerror occurred: {}\n", msg);
        std::process::exit(1);
    }

    cc::Build::new()
        .include("/usr/lib/nim")
        .warnings(false)
        .file("nimcache/fib.nim.c")
        .file("nimcache/stdlib_system.nim.c")
        .compile("fib_nim");
}

注意這里有幾個依賴於平台的位,主要是 Nim 標頭位置。 並且 Nim 編譯器還被告知將中間文件放入項目根目錄中名為nimcache的目錄中,而不是用戶主目錄下的默認目錄。

Cargo.toml文件:

[package]
name = "nim-ffi"
version = "0.1.0"
authors = ["rustacean"]
edition = "2018"

[dependencies]
libc = "0.2"

[build-dependencies]
cc = "1.0"

最后是主要的 Rust 源文件:

use libc::c_int;

extern "C" {
    fn NimMain();
    fn fib(_: c_int) -> c_int;
}

fn main() {
    // initialize nim gc memory, types and stack
    unsafe {
        NimMain();
    }

    let res = unsafe { fib(20) };
    println!("Nim fib(20) is: {}", res);
}

它構建並成功運行:

$ cargo run
Nim fib(20) is: 6765

看起來這會將 nim 編譯為 C,然后編譯 C 程序,包括已編譯的 nim 內容。

對於其他語言的 FFI,您需要編譯為庫(動態或共享),然后通過 FFI 鏈接和訪問它。

我不知道它是否仍然有效,但https://github.com/oderwat/nim-haskell-ffi提供了一個創建靜態庫然后從 Haskell 鏈接和 FFI 調用它的示例。

暫無
暫無

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

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