Как вызвать функцию Nim из Rust через C -FFI? - PullRequest
2 голосов
/ 23 января 2020

Руководство по интеграции с Nim описывает, как вызывать функцию Nim из C.

Пример функции:

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.h и main.c:

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

Как я могу поручить компилятору ржавчины искать эти единицы перевода в nimcache?

Ответы [ 2 ]

4 голосов
/ 23 января 2020

В проекте 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 внутри проекта root вместо каталога по умолчанию в домашнем каталоге пользователя.

Файл 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
0 голосов
/ 23 января 2020

Похоже, что это компилирует nim в C, а затем компилирует программу C, включая материал скомпилированного nim.

Для FFI из другого языка вам нужно скомпилировать в библиотеку (Dynami c или общий доступ), затем связать и получить доступ к нему через FFI.

Я не знаю, работает ли он по-прежнему, но https://github.com/oderwat/nim-haskell-ffi предоставляет пример создания библиотеки stati c затем связывание и FFI-вызов от Haskell.

...