张金龙的博客分享 http://blog.sciencenet.cn/u/zjlcas 物种适应性、分布与进化

博文

Rcpp简明指南: R调用C++

已有 14012 次阅读 2013-10-27 20:39 |个人分类:软件介绍|系统分类:科研笔记|关键词:学者| windows, 源代码, 电脑, 程序, 文件夹

Rcpp简明指南: R调用C++

张金龙

jinlongzhang01@gmail.com


新版本参见 http://blog.sciencenet.cn/home.php?mod=space&uid=255662&do=blog&id=835970 


注: 本文需要读者了解:

(1) R函数的基本知识

(2)编写R程序包

(3) C++的基础知识, 包括 类, 以及namespace等。



为什么要用R调用C++函数? 原因可能有以下几点:

(1) R开发虽然很快, 涉及到一些循环的时候, 执行效率却常常相当于C++的1/100,甚至更慢

(2) C++中有一些已经开发好的库, 人们不希望再重写一遍R的代码。 而是最好通过编译之后直接调用。


Rcpp程序包为用R调用C++代码提供了方便。

用户直接在R的源代码src文件夹下加入cpp和h文件, rtools即可对C++文件进行编译。 但是为了从R的console能够直接调用cpp写的函数, 还必须对cpp的函数进行修改。


修改需要遵循以下原则:

1 所有从R输入到cpp的对象, 都是SEXP

2 从cpp返回到R的对象也必须是SEXP

3 C++中, 返回值如为数值, 可以用wrap()帮忙, 直接返回为SEXP的对象, 实现R的调用


换句话说, 只有加了SEXP这样一个标签,C++函数在 R和C++中才都能识别。 Rcpp提供了SEXP处理的响应的类, 使得两者之间的数据传输变得容易。


以下是用R调用C++的一个简单例子:


如果在windows中要使用Rcpp, 则需要

1 安装Rcpp程序包,install.packages("Rcpp")

2 安装Rtools, 并配制好启动路径。电脑>属性>高级系统设置>高级>环境变量>系统变量>路径

3 在R console 中运行 library(Rcpp) , 之后调用 Rcpp.package.skeleton函数,

Rcpp.package.skeleton( "test" )

在生成的R包 skeleton中, 可以找到rcpp_hello_world.cpp文件, 可以尝试添加几个函数, 修改成如下样式 (注意以下代码为C++文件,扩展名为cpp):


////////////////////// C++文件开始//////////////////

#include "rcpp_hello_world.h"
#include <Rcpp.h>
using namespace Rcpp;
using namespace std;
// 注意 #include <Rcpp.h> 是调用Rcpp的必要条件



RcppExport SEXP intVec1a(SEXP vec) {
   Rcpp::NumericVector vec2(vec);
   int prod = 1;
   for (int i=0; i<vec2.size(); i++) {
       prod *= vec2[i];
   }
   return (wrap(prod));
}

// RcppExport 是为了在载入R包后, 在Namespace内内能够找到这个C++的函数

//第一个SEXP声明返回值的类型

//第二个SEXP, 声明vec变量为SEXP的类型,也就是说, 它可以是从R直接传入

// Rcpp::NumericVector vec2(vec); 意思 是将 vec转换为 vec2, 从SEXP转换为 NumericVector, NumericVector是Rcpp下的一个类, 在C++的函数中可以识别和调用。 后面的代码就和普通的C++没有什么分别了。

//return (wrap(prod)); 由于我们希望在R中直接可以得到该函数的运行结果, 因此, 返回值也需要转换为SEXP类型。 wrap函数提供了一种便捷的方式。 简单的类型, 如int, double等, 只需要直接调用wrap即可。


RcppExport SEXP Eccentricity(SEXP JD)
{
 double JD3  = Rcpp::as<double>(JD);
 double T = (JD3 - 2451545.0) / 36525.0;
 double Tsquared = T*T;
 return (wrap(1 - 0.002516*T - 0.0000074*Tsquared));
}
// Rcpp::as<double>(JD); 表示调用Rcpp内的方法, 将类型为SEXP的JD, 转换为double类型, 并赋值給double JD3



RcppExport SEXP expfun(SEXP x){
double x2 = Rcpp::as<double>(x);
double ans=1.0, term=1.0, eps=1e-16;
int n=0;
while (fabs(term)>eps){
   n++;
   term = term * x2 / n;
   ans = ans + term;
}
  return(wrap(ans));
}
////////////////////// C++文件结束//////////////////
对R程序包test的骨架进行修改后, 即可开始安装,

在windows下, 在 开始, cmd, 输入 Rcmd INSTALL test, 安装test程序包


之后转入 R console, library(test)

即可加载响应的动态链接库dll文件

只是此时调用dll中的文件, 还稍微有些不方便。 此时调用dll中函数的方法如下


.Call('Eccentricity', JD= 2314543)

.Call('intVec1a', vec= c(3, 4, 5))

.Call('expfun', x=3)

不过每次使用.Call函数, 仍然让人觉得不太满意。为了需要写一个R的wrapper function例如


expfunR <- function(x){

   return( .Call('expfun', x= x))

}


EccentricityR <- function(JD){

   return( .Call('Eccentricity', JD= JD))

}


intVecR <- function(x){
   return(.Call('intVec1a', vec= x))
}

调用方式同R函数,

expfunR(2)

EccentricityR(2314543)

intVecR(c(2,3,4))

这时候就和一般的R函数没有区别了。




https://m.sciencenet.cn/blog-255662-736600.html

上一篇:兩個可以自由調整的指北針
下一篇:新西兰Dunedin的杜鹃花海

0

该博文允许注册用户评论 请点击登录 评论 (1 个评论)

数据加载中...
扫一扫,分享此博文

Archiver|手机版|科学网 ( 京ICP备07017567号-12 )

GMT+8, 2024-3-29 13:13

Powered by ScienceNet.cn

Copyright © 2007- 中国科学报社

返回顶部