R调用C/CPP及R包编写

最后发布时间:2023-02-21 09:30:22 浏览量:

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="")

图片alt

图片alt

.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')

图片alt

图片alt

仅有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")

图片alt

图片alt

.
├── 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 

图片alt

图片alt

打包

R CMD build  TestRcpp

用RcppArmadillo帮助制作包含的R扩展包(支持并行)

创建骨架

library(RcppArmadillo)
RcppArmadillo.package.skeleto2021-04-02 21:04:30 星期五n("TestRcppArmadillo")

图片alt

图片alt

.
├── 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