R调用C/CPP及R包编写
roxygen
roxygen2::roxygenize('.', roclets = c('rd', 'collate', 'namespace'))
devtools::document()
Ctrl + Shift + D, if you’re using RStudio.
devtools
devtools::document(roclets = c('rd', 'collate', 'namespace'))
devtools::load_all(".")
devtools::install()
document()
updates generated documentation in man/, file collation and NAMESPACE.load_all()
模拟安装和重新加载包,在R/中加载R代码,在src/中加载编译的共享对象,在data/中加载数据文件。在开发过程中,您通常希望访问所有函数(甚至是未导出的内部函数),因此load_all()
的工作方式就像所有函数都在包NAMESPACE中导出一样。
https://github.com/wangyang1749/R_Create_Package_Example
R调用C
创建test.c文件
void hello(char **greeting) {
*greeting = "Hello World!";
}
编译so库文件
R CMD SHLIB hello.c
gcc -std=gnu99 -I"/usr/share/R/include" -DNDEBUG -fpic -g -O2 -fdebug-prefix-map=/build/r-base-V28x5H/r-base-3.6.3=. -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -g -c hello.c -o hello.o
gcc -std=gnu99 -shared -L/usr/lib/R/lib -Wl,-Bsymbolic-functions -Wl,-z,relro -o hello.so hello.o -L/usr/lib/R/lib -lR
得到 hello.so
加载so文件并调用
dyn.load("hello.so")
.C("hello",var_t="")
.C 接口是从 R 调用 C 的最简单但也是最有限的方法。在运行的 R 会话中,.C 接口允许在 R 会话的活动内存中,直接访问对象。因此,要编写兼容的 C 函数,所有参数都必须是指针。无论函数返回值的性质如何,它也必须使用指针来处理
# C code
void double_me(int* x) {
// Doubles the value at the memory location pointed to by x
*x = *x + *x;
}
# R code
> dyn.load("hello.so")
> b <- .C("double_me", a = as.integer(5))
> b
# $a
# [1] 1
.C 的输出是一个列表,其名称对应于参数
.Call 接口是 .C 接口功能更全面、更复杂的表亲。与 .C 不同,.Call 需要每个 R 安装都标配的头文件。这些头文件提供对新数据类型 SEXP 的访问
#include <R.h>
#include <Rdefines.h>
SEXP double_me2(SEXP x) {
// Doubles the value of the first integer element of the SEXP input
SEXP result;
PROTECT(result = NEW_INTEGER(1)); // i.e., a scalar quantity
INTEGER(result)[0] = INTEGER(x)[0] * 2;
UNPROTECT(1); // Release the one item that was protected
return result;
}
# R code
> dyn.load("hello2.so")
> .Call("double_me2", as.integer(5))
# [1] 10
与我们使用 .C 接口的经验不同,double_me2是一个函数并且确实返回一个值。虽然这很符合直觉,但无论原生输入和输出类型是什么,它们现在都必须存在于 SEXP 对象中。要编写double_me2代码,您必须知道输入x中有一个整数,并将其提取为 C 数组中的第一项。对于返回值,您必须以同样不自然的方式将整数结果添加到 SEXP 对象。必须使用 PROTECT 函数来防止 R 的自动垃圾收集销毁所有对象。
R调用C++
创建hello.cpp文件
#include <Rcpp.h>
using namespace Rcpp;
NumericVector timesTwo(NumericVector x) {
return x * 2;
}
/*** R
timesTwo(42)
*/
调用函数
Rcpp::sourceCpp('hello.cpp')
仅有R程序扩展包
package.skeleton("TestRcpp")
installing to library ‘/home/wy/R/x86_64-pc-linux-gnu-library/3.6
用Rcpp帮助制作包含的R扩展包
创建骨架
library(Rcpp)
Rcpp.package.skeleton("TestRcpp")
.
├── DESCRIPTION
├── man
│ ├── rcpp_hello_world.Rd
│ └── Test-package.Rd
├── NAMESPACE
├── R
│ └── RcppExports.R
├── Read-and-delete-me
└── src
├── RcppExports.cpp
└── rcpp_hello_world.cpp
编译运行
R CMD INSTALL TestRcpp
打包
R CMD build TestRcpp
用RcppArmadillo帮助制作包含的R扩展包(支持并行)
创建骨架
library(RcppArmadillo)
RcppArmadillo.package.skeleto2021-04-02 21:04:30 星期五n("TestRcppArmadillo")
.
├── DESCRIPTION
├── man
│ ├── rcpparma_hello_world.Rd
│ └── Test_Armadillo-package.Rd
├── NAMESPACE
├── R
│ └── RcppExports.R
├── Read-and-delete-me
└── src
├── Makevars
├── Makevars.win
├── rcpparma_hello_world.cpp
└── RcppExports.cpp
编译运行
R CMD INSTALL TestRcppArmadillo
打包
R CMD build TestRcppArmadillo
参考
- https://devtools.r-lib.org/
- https://r-pkgs.org/
- https://mirrors.tuna.tsinghua.edu.cn/CRAN/doc/manuals/r-release/R-exts.html
- https://adv-r.hadley.nz/rcpp.html
- http://mazamascience.com/WorkingWithData/?p=1067
- https://blog.csdn.net/weixin_41929524/article/details/81975484
- https://www.math.pku.edu.cn/teachers/lidf/docs/Rbook/html/_Rbook/rcpp-package.html
- https://blog.csdn.net/zyf_2014/article/details/106429644
- https://cpp11.r-lib.org/articles/cpp11.html