如何通过C-FFI从Rust调用Nim函数?

5
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主函数必须调用名为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 -o m -I$HOME/.cache/nim/fib_d -Ipath/to/nim/lib $HOME/.cache/nim/fib_d/*.c maths.c 我该如何指示 Rust 编译器去查找那些位于 nimcache 下的翻译单元呢?
2个回答

7
在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编译器将中间文件放在项目根目录下名为的目录中,而不是默认放在用户主目录下的目录中。

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

https://nim-lang.github.io/Nim/nimc.html#cross-compilation-for-android 强调在主函数顶部调用NimMain()函数以加载其垃圾回收机制的重要性。如果您的示例也包括该调用,那将是很好的。 - noconst

0

看起来这个编译器将Nim编译成C,然后编译包括已编译的Nim内容的C程序。

对于从其他语言进行FFI,您需要编译成库(动态或共享),然后通过FFI链接和访问该库。

我不知道它是否仍然有效,但https://github.com/oderwat/nim-haskell-ffi提供了一个创建静态库的示例,然后从Haskell链接和FFI调用它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接