R和Rust都可以与C代码连接,所以我认为这很有可能。然而,我对如何继续有点不清楚。
我已阅读这些部分寻找答案:
但是,虽然我精通R
,但我不是系统程序员,并且对构建链的这种努力看起来很困惑。
使用Rinternals.h
会很理想,但我也会选择更简单的.C
界面。
答案 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
(或系统中的任何内容)后缀的共享对象的名称。Sys.setenv
设置LD_LIBRARY_PATH
变量不工作。在开始R。verbose
选项在那里,以便您可以看到Rcpp
编译C ++文件的功能。请注意上面PKG_LIBS
中的选项如何用于编译C ++文件。rebuild
选项都会强制重建C ++文件。如果你做得很好,那么在交互式控制台中运行上面的R文件,当你到达最后一行时它应该输出80.33
。
如果有任何不清楚的地方,请在评论中提问,我会尽力改善我的答案。
希望它有所帮助:)
最后注意,基本函数dyn.load
和.C
可用作替代方法。但这需要编写比这种方法更多的样板包装代码。
答案 1 :(得分:2)
如果R可以与C代码接口,那么从Rust代码中编译共享库就完全没有问题了。它暴露了C风格的函数。
然后,您可以轻松使用您的库,因为它是用C或C ++编写的。当然,你不能直接从R使用Rust对象和库,你必须使用适当的C接口来转换它们的函数。
以下是我如何为SBCL做到这一点,我想它对R来说非常相似:
一些代码:
% 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
现在我只是加载我的共享库,然后我可以访问我的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