【问题标题】:What are the steps necessary to pass R objects to a Rust program?将 R 对象传递给 Rust 程序需要哪些步骤?
【发布时间】:2015-03-06 14:35:42
【问题描述】:

R 和 Rust 都可以与 C 代码交互,所以我认为这是很有可能的。但是,我有点不清楚如何进行。

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

  1. R-extensions System-and-foreign-language-interfaces
  2. The Rust foreign function interface guide

但是,虽然我精通R,但我不是系统程序员,并且对构建链的外观感到困惑。

使用Rinternals.h 是理想的,但我也会满足于更简单的.C 界面。

【问题讨论】:

  • 你想从 Rust 调用 R,还是从 R 调用 Rust?如果您是后者,请查看.Call() 而不是.C(),因为没有人应该再使用它了。 Rcpp 可以帮助你做一个中间层。
  • 我想从 R 中调用 Rust 代码,并在我的 R 会话中对对象使用 Rust 库。
  • 您应该能够(轻松地)通过 Rcpp 从 R 转到 C++。到那时,@swizard 的答案就可以按原样使用了。如果您有兴趣,我们可以在Rcpp Gallery 中制作一个小演示作为示例——例如embedding Python via Boost 之一。

标签: r rust


【解决方案1】:

我也为此苦苦挣扎了一段时间,但一旦你知道我实际上并不难。

首先按照以下说明创建一个 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) 的目录。在外壳中做:

$ 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++ 文件的。
  • rebuild 选项用于在您每次运行这行 R 代码时强制重建 C++ 文件。

如果一切顺利,则在交互式控制台中运行上面的 R 文件,当您到达最后一行时,它应该输出 80.33

如果有什么不清楚的地方,请在 cmets 中提问,我会努力改进我的答案。

希望它有所帮助:)


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

【讨论】:

    【解决方案2】:

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

    然后您可以轻松地使用您的库,因为它是用 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,
        }
    }
    

    然后我正在制作共享库:

    % 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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-01-15
      • 2016-01-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-21
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多