将 R 对象传递给 Rust 程序需要哪些步骤?
What are the steps necessary to pass R objects to a Rust program?
R和Rust都可以和C代码接口,所以我觉得很有可能。但是,我有点不清楚如何进行。
我已阅读这些部分以寻找答案:
但是,虽然我精通 R
,但我 不是 系统程序员,并且对构建链看起来像这样的努力感到困惑。
使用 Rinternals.h
是最理想的,但我也会选择更简单的 .C
界面。
如果 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
我也为此苦苦挣扎了一段时间,但一旦你知道我是怎么做到的,其实并不那么困难。
首先按照以下说明创建一个 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
可以用作替代方法。但这需要编写比这种方法更多的样板包装代码。
R和Rust都可以和C代码接口,所以我觉得很有可能。但是,我有点不清楚如何进行。
我已阅读这些部分以寻找答案:
但是,虽然我精通 R
,但我 不是 系统程序员,并且对构建链看起来像这样的努力感到困惑。
使用 Rinternals.h
是最理想的,但我也会选择更简单的 .C
界面。
如果 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
我也为此苦苦挣扎了一段时间,但一旦你知道我是怎么做到的,其实并不那么困难。
首先按照以下说明创建一个 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
可以用作替代方法。但这需要编写比这种方法更多的样板包装代码。