将R对象传递给Rust程序需要哪些步骤?

时间:2015-03-06 14:35:43

标签: r rust

R和Rust都可以与C代码连接,所以我认为这很有可能。然而,我对如何继续有点不清楚。

我已阅读这些部分寻找答案:

  1. R-extensions System-and-foreign-language-interfaces
  2. The Rust foreign function interface guide
  3. 但是,虽然我精通R,但我不是系统程序员,并且对构建链的这种努力看起来很困惑。

    使用Rinternals.h会很理想,但我也会选择更简单的.C界面。

2 个答案:

答案 0 :(得分:7)

我一直在努力争取这段时间,但是一旦你知道我实际上并不那么困难。

首先按照以下说明创建一个Rust库:rust-inside-other-languages。 这是一个示例Rust库:

//src/lib.rs

#[no_mangle]
pub fn kelvin_to_fahrenheit(n: f64) -> f64 {
    n * 9.0/5.0 - 459.67
}

如果您按照rust-inside-other-languages中的说明操作,那么您应该能够生成*.so(或*.dll.dylib,具体取决于您的系统)。我们假设这个编译的文件叫做libtempr.so

现在创建一个C ++文件,它将把你需要的函数传递给R:

//embed.cpp

extern "C" {
    double kelvin_to_fahrenheit(double);
}

// [[Rcpp::export]]
double cpp_kelvin_to_fahrenheit(double k) {
  double f = kelvin_to_fahrenheit(k);
  return(f);
}

现在,在启动R之前,请确保环境变量LD_LIBRARY_PATH包含存储先前生成的共享对象(libtempr.so)的目录。在shell中执行:

$ export LD_LIBRARY_PATH=/home/sam/path/to/shared/object:$LD_LIBRARY_PATH
$ rstudio # I highly recommend using Rstudio for your R coding

最后在Rstudo中,写下这个文件:

library(Rcpp)

Sys.setenv("PKG_LIBS"="-L/home/sam/path/to/shared/object -ltempr")

sourceCpp("/home/sam/path/to/embed.cpp", verbose = T, rebuild = T)

cpp_kelvin_to_fahrenheit(300)
  • 请注意Sys.setenv -L选项指向包含Rust共享对象的目录。
  • 另外请注意,-l选项是没有lib前缀且没有.so(或系统中的任何内容)后缀的共享对象的名称。
  • 在R中使用Sys.setenv设置LD_LIBRARY_PATH变量不工作。在开始R。
  • 之前导出变量
  • verbose选项在那里,以便您可以看到Rcpp编译C ++文件的功能。请注意上面PKG_LIBS中的选项如何用于编译C ++文件。
  • 每次运行此R代码行时,rebuild选项都会强制重建C ++文件。

如果你做得很好,那么在交互式控制台中运行上面的R文件,当你到达最后一行时它应该输出80.33

如果有任何不清楚的地方,请在评论中提问,我会尽力改善我的答案。

希望它有所帮助:)

最后注意,基本函数dyn.load.C可用作替代方法。但这需要编写比这种方法更多的样板包装代码。

答案 1 :(得分:2)

如果R可以与C代码接口,那么从Rust代码中编译共享库就完全没有问题了。它暴露了C风格的函数。

然后,您可以轻松使用您的库,因为它是用C或C ++编写的。当然,你不能直接从R使用Rust对象和库,你必须使用适当的C接口来转换它们的函数。

以下是我如何为SBCL做到这一点,我想它对R来说非常相似:

在Rust侧

一些代码:

% cat experiment.rs

extern crate libc;

use libc::{c_int, c_char};
use std::{ffi, str};

#[no_mangle]
pub extern fn rust_code_string_to_int(s: *const c_char, r: *mut c_int) -> c_int { 
    let string = String::from_utf8_lossy(unsafe { ffi::CStr::from_ptr(s).to_bytes() });
    match <isize as str::FromStr>::from_str(&*string) {
        Ok(value) => { unsafe { *r = value as c_int }; 0 },
        Err(_) => -1,
    }
}

然后我正在制作共享的lib:

% rustc --crate-type dylib experiment.rs
% nm -a libexperiment.dylib | grep rust_code_string_to_int
0000000000001630 t __ZN23rust_code_string_to_int10__rust_abiE
00000000000015e0 T _rust_code_string_to_int

接着,在SBCL方面

现在我只是加载我的共享库,然后我可以访问我的rust_code_string_to_int函数:

RUST> (sb-alien:load-shared-object "libexperiment.dylib")
#P"libexperiment.dylib"
RUST> (sb-alien:with-alien ((result sb-alien:int 0))  
          (values (sb-alien:alien-funcall (sb-alien:extern-alien "rust_code_string_to_int" 
                                                                 (sb-alien:function sb-alien:int 
                                                                                    (sb-alien:c-string :external-format :utf-8)
                                                                                    (sb-alien:* sb-alien:int)))
                                          (sb-alien:make-alien-string "42")
                                          (sb-alien:addr result))
                  result))
0
42