7 个版本
0.4.3 | 2024 年 8 月 2 日 |
---|---|
0.4.3-pre0 | 2024 年 7 月 19 日 |
0.4.0-pre2 | 2024 年 6 月 9 日 |
0.3.1 | 2024 年 6 月 6 日 |
0.1.0 |
|
#1 in #writting
230 个月下载量
81KB
1K SLoC
rmin - 一个用于编写 R 扩展的最小化 Rust 库
这是一个非常早期的版本,仅支持向量类型,因此其开销最小化。
与广为人知的 rextendr
相比,这个包尽管有一些限制,但可以提供更快的实现、更小的代码大小和更快的编译时间。
由于它足够小,你可以很容易地将这个包集成到你的 CRAN 软件包中。
状态
最近可用的版本是 v0.4.3,不要使用 GIT 版本
请注意,我对切换分支不熟悉,直接提交到主分支是非常不可信的。GitHub 只是一个仓库
v0.4.3 版本中的破坏性变化
- 创建一个(不安全的)类型
OptionSexp
,因为 R 中的MISSING(一个缺失值)
返回非零(甚至不返回,因为指针可能无效)。这不是选择,因为有了宏支持,Sexp
不能缺失, - 在宏中统一两个入口与 lib*.so 和 *.so:创建两者。
- 你应该使用
use rmin::{*, println};
在 std 模式下导入,否则会生成警告。因为std::println!
不能输出到 Rgui.exe,需要显式导入来覆盖 std::println。
v0.4.0 版本即将到来的破坏性变化
character
重新绑定到 Sexp<char>
,它将 SEXPTYPE
绑定到 STRSXP
版本0.3.0的character
绑定移动到Rchar
,因为R告诉我返回的CHARSXP
类型名称为
在printf和rf_errorcall中添加额外的'%s\0',以防止格式化错误
特性
需要以下特性之一:cfg-if
(用于无_std环境)或std
(用于常规使用)。
详细信息
panic-info-message
启用Rust特性panic_info_message
,将Rust的panic消息返回到R,可能对调试很有用。默认启用。
std
大多数Rust包都依赖于std::*
,如果你想要使用其他包,应该启用此特性。不使用lto
编译整个包大约需要1秒,但如果为了更快的执行速度启用lto
,可能需要大约5秒来完成编译。
core
std
的对应特性,目前std
只是一个发出警告而未正确具体化的指示器。此特性控制异常处理语言项的链接,因此启用时不能忽略。
rmin-macros
将进程宏#[export] fn func_name(...)...{...}
和done!(crate_name)
导入到crate::prelude
,因此在crate
::*根目录下直接可用。
请注意,宏需要rmin::reg
路径才能工作(在rmin
包中选择宏时自动启用,如果你将rmin-macros作为独立的依赖项启用,你应该手动启用rmin::reg
)。
rmin-macros-camel-ass-wrapper
仅限内部使用,使用驼峰命名法(即iOS命名法)定义内部名称,以避免名称冲突。
rmin-macros-warning
对于具有错误(或空)签名的函数发出警告,例如。例如,fn()->Owned<character>
将产生一个警告warning, [[]] is omitted
,因为签名是空的。
fn(a:Sexp<f64>,)->Owned<f64>
同样会引发相同的警告(由于最后一个逗号)
它们可能会损害宏,从而引发警告(尽管上面两个例子是无害的,但像 (a:Sexp<f64>,,b:Sexp<f64>)
这样的写法将中断编译过程。)
rmin-macros-verbose
默认禁用,包含一些简单信息,例如导出的函数名称和终结器生成的内容。
public-all
最邪恶且危险的功能。最好不要启用它。大多数有用的函数都有一个名为 public-by-default-even-public-all-is-not-set
的标记功能,该功能是一个标记功能,不做任何事情,只是告诉你可以从 prelude
模块中获得哪些函数。
min-import
对于 prelude
模块。由于所有 RType
别名都可以从 crate::prelude::R
访问,此功能禁用将别名导入 prelude
模块。
register-routines
注册 R 例程,主要用于宏,因为手动编写此类事情很痛苦。
cfg-if
默认启用,因为编译 no_std
环境的异常处理函数需要 cfg-if
。如果你使用 std
功能,则可以禁用它。
public-by-default-even-public-all-is-not-set
虚拟功能。如果你使用 --no-default-feature
禁用它,则不会发生任何事情。
注意
请切换到 prelude
模块页面进行初步了解,因为我想要展示所有文档,大多数私有内容都使用 public-all
功能标志进行文档说明。请勿直接使用它们,因为大多数都有安全包装,直接使用它们是危险的。
用法
版本 0.1.0 提供了一种最快(但很丑)的方法,在函数上实现约 2 倍的速度提升。它们在 0.2.* 中被弃用,因为它们真的很不安全,可能会引起内存泄漏。
当前0.3.0版本与0.2.0版本略有不同,将SEXP<T>
重命名为Sexp<T>
,并(将)支持类似Sexp
<numeric_list
>(R::numeric_list)或甚至是一个任意列表Sexp<(T1,T2)>
。
注意:在即将到来的0.4.0版本中,所有decl_macro可能会被移动到一个单独的crate中,该crate提供宏和proc_macros。这可能只会影响具有默认no_std环境的用户。
0.3.0,将#[no_std]
恢复!
在0.3.0中,feature std
再次是可选的,这将给我们更快的代码生成速度。
变更
-
- 目前,新的方法和来自(Rust类型)的方法都进入SExt,您仍然可以编写
Owned<T>
::
new
()
,但返回Protected<T>
。
- 目前,新的方法和来自(Rust类型)的方法都进入SExt,您仍然可以编写
-
- 为
no_std
添加一个catch_unwind
。
- 为
-
- 将
SEXP<T>
移动到Sexp<T>
,因此SEXP和Sexp可以在相同的情况下出现。
- 将
-
- 使用宏2.0隐藏大部分结构和方法,但保留文档以供调试目的。
-
- 添加对列表的支持(部分完成。)
语法
#![no_std]
use rmin::{*, println};
/// Return a+b to R.
#[no_mangle]
pub extern "C" fn add_protect(a:Sexp<f64>,b:Sexp<f64>) -> Owned<f64> {
handle_panic(||{
let mut c=Owned::new(1);
c[0]=a[0]+b[0];
c.into()
})
}
#[no_mangle]
pub extern "C" fn add_noprotect(a:Sexp<f64>,b:Sexp<f64>) -> Owned<f64> {
handle_panic(||{
let mut c=Owned::new(1);
c[0]=a[0]+b[0];
c.into()
})
}
/// raise panic.
#[no_mangle]
pub extern "C" fn panic() -> Owned<f64> {
handle_panic(||{
panic!("error occurs")
})
}
/// with macro
/// macro will register this function, thus R will check whether all parameters are missing
#[export]
fn macro_will_expand_and_register_it(a:Sexp<f64>)->Owned<f64>{
let mut b=Owned::new(1);
b[0]=a.data().sum();
}
done!();// in case you're using macros, adding a done! is necessary, this done call generate the
// register routine, which will ensure the expanded code is checked.
fn main() {} // just makes compiler happy
上述程序可以使用测试命令进行测试
export LOAD="dyn.load('target/release/examples/libcompare_rmin.so');addnp=getNativeSymbolInfo('add_noprotect');addp=getNativeSymbolInfo('add_protect');panic=getNativeSymbolInfo('panic')" ; LC_ALL=C r -e "$LOAD;system.time(sapply(1:100000,function(x)tryCatch(.Call(wrap__panic),error=I)))" 2>/dev/null ; LC_ALL=C r -e "$LOAD;system.time(sapply(1:1000000,function(x).Call(addp,1.,2.)));system.time(sapply(1:1000000,function(x).Call(addp,1.,2.)))"