之前写了一小篇R调用C程序,只是一个很小的引子,示例一下如何使用R调用c程序。
文中提到R与C之间数据传递的方式只能通过形参的方式返回,是真的吗?当然不是。真正的R可调用的C程序的变量类型一般都是SEXP。那么SEXP是什么呢?SEXP其实就是一个指针。它指向的是一个数据结构SEXPREC,它被分为了三个部分,sxpinfo,data以及union。sxpinfo是一个32-bit的结构体。
struct sxpinfo_struct { SEXPTYPE type : 5; /* discussed above */ unsigned int obj : 1; /* is this an object with a class attribute? */ unsigned int named : 2; /* used to control copying */ unsigned int gp : 16; /* general purpose, see below */ unsigned int mark : 1; /* mark object as `in use' in GC */ unsigned int debug : 1; unsigned int trace : 1; unsigned int spare : 1; /* unused */ unsigned int gcgen : 1; /* generation for GC */ unsigned int gccls : 3; /* class of node for GC */ }; /* Tot: 32 */ |
union { struct primsxp_struct primsxp; struct symsxp_struct symsxp; struct listsxp_struct listsxp; struct envsxp_struct envsxp; struct closxp_struct closxp; struct promsxp_struct promsxp; } u; |
其中,SEXPTYPE type值的定义为
no SEXPTYPE Description 0 NILSXP NULL 1 SYMSXP symbols 2 LISTSXP pairlists 3 CLOSXP closures 4 ENVSXP environments 5 PROMSXP promises 6 LANGSXP language objects 7 SPECIALSXP special functions 8 BUILTINSXP builtin functions 9 CHARSXP internal character strings 10 LGLSXP logical vectors 13 INTSXP integer vectors 14 REALSXP numeric vectors 15 CPLXSXP complex vectors 16 STRSXP character vectors 17 DOTSXP dot-dot-dot object 18 ANYSXP make “any” args work 19 VECSXP list (generic vector) 20 EXPRSXP expression vector 21 BCODESXP byte code 22 EXTPTRSXP external pointer 23 WEAKREFSXP weak reference 24 RAWSXP raw vector 25 S4SXP S4 classes not of simple type |
有了这些知识,我们就可以写一个程序了。
1 2 3 4 5 6 7 8 9 10 11 12 | #include <R.h> #include <Rinternals.h> #include <Rmath.h> SEXP vecSum(SEXP Rvec){ int i, n; double *vec, value = 0; vec = REAL(Rvec); n = length(Rvec); for (i = 0; i < n; i++) value += vec[i]; printf("The value is: %4.6f \n", value); return R_NilValue; } |
我们可以看到,程序中的数据传递的类型都定义成为SEXP。那么下一个问题就是如何进行数据类型转换呢?我们将R中的基本数据类型分为factor, character, numeric, logical, complex, raw, list, expression,等等。这里,我只涉汲几种最基本的类型转换,
R中类型 -->C中类型 character -->char* integer -->int double -->double logical -->bool matrix -->array[][] |
编辑一段C代码并保存为main.c
#include <R.h> #include <Rinternals.h> SEXP convert(SEXP Rchar, SEXP Rint, SEXP Rdoub, SEXP Rlogi, SEXP Rmat){ const char* ch = CHAR(asChar(Rchar)); //character -->char int i = asInteger(Rint); //integer -->int double d = asReal(Rdoub); //double -->double int b = asLogical(Rlogi); //logical -->bool Rprintf("Rchar=%s\tRint=%d\tRdoub=%f\tRlogi=%d\n",ch,i,d,b); //matrix --> array[][] SEXP dimnames = getAttrib(Rmat, R_DimNamesSymbol); SEXP rownames = VECTOR_ELT(dimnames, 0); SEXP colnames = VECTOR_ELT(dimnames, 1); const int ncol = length(colnames); const char* cnames [ncol]; for(int j=0; j<ncol; j++) cnames[j] = CHAR(STRING_ELT(colnames,j)); const int nrow = length(rownames); const char* rnames [nrow]; for(int j=0; j<nrow; j++) rnames[j] = CHAR(STRING_ELT(rownames,j)); if (b) { //假设matrix为double型 //注意不能是data.frame double mat [nrow][ncol]; //isInteger matrix for(int m=0; m<nrow; m++){ for(int n=0; n<ncol; n++){ mat[m][n]=INTEGER(Rmat)[m+n*(ncol-1)]; Rprintf("%f\t",mat[m][n]); } Rprintf("\n"); } } else { //假设matrix为character型。 const char* mat [nrow][ncol]; for(int m=0; m<nrow; m++){ for(int n=0; n<ncol; n++){ mat[m][n]=CHAR(STRING_ELT(Rmat,m+n*(ncol-1))); Rprintf("%s\t",mat[m][n]); } Rprintf("\n"); } } //from c type back to R type // SEXP num; // PROTECT(num = allocVector(REALSXP, 1)); // REAL(num)[0] = d; // UNPROTECT(1);//<-1,or 2... is determined by how many PROTECT you are using // return num; //create a matrix for R SEXP ans; PROTECT(ans = allocMatrix(REALSXP, nrow, ncol)); for(int m=0; m<nrow; m++) for(int n=0; n<ncol; n++) REAL(ans)[m + n*nrow] = m + n; setAttrib(ans, R_DimNamesSymbol, dimnames); UNPROTECT(1); return(ans); } |
在terminal中当前目录下运行
R CMD SHLIB main.c |
会得到一个名为main.so的文件。然后就可以在R中测试它了
mat<-matrix(1:20, 4, 5) colnames(mat)<-LETTERS[1:5] rownames(mat)<-1:4 lmat<-matrix(letters[1:20],4,5) colnames(lmat)<-LETTERS[1:5] rownames(lmat)<-1:4 dyn.load("/path/to/your/code/main.so") .Call("convert","qiuworld.com", 100, 9.99, TRUE, mat) .Call("convert","糗世界", 100, 9.99, FALSE, lmat) |
欧老师你好,
我又来问问题了。
博文中提到的 struct sxpinfo_struct 和 union 都可以在 Rinternals.h 可以找得到,SEXP指针也在此定义。类似 REAL,PROTECT,asReal 等等也都是在 Rinternals.h 声明。于是我想直接在 VC 中用REAL,PROTECT,asReal 这些方法,就去R目录下的include文件夹里把 Rinternals.h 复制到 VC目录下的include文件夹,但是发现还需要R_ext/Arith.h等等,我找了一下找不到R_ext/Arith.h在哪里
您能解答我的问题吗,谢谢
昨天太粗心了,没看见R目录下的include文件夹下还有一个R_ext子文件夹,Arith.h 就在里面。我已经把R目录下的 include 中的所有.h文件(包括R_ext子文件夹)都复制到 VC 的 include 目录下,结果使用 SEXP 还是会出现各种问题,比如:
SEXP BinDist(SEXP sx, SEXP sw)
{
PROTECT(sx = coerceVector(sx, REALSXP));
PROTECT(sw = coerceVector(sw, REALSXP));
UNPROTECT(2);
return 0;
}
会显示:Linking…
6.obj : error LNK2001: unresolved external symbol _Rf_unprotect
6.obj : error LNK2001: unresolved external symbol _Rf_protect
6.obj : error LNK2001: unresolved external symbol _Rf_coerceVector
其实我把R的头文件都include进来的做法真的行得通吗?
它们的目录关系还是得保持吧。其实你可以把path都include进来就可以了。
已经把R目录下的include文件夹里所有.h 复制到 VC目录下的include文件夹中,也在VC中把它们全部include进来,运行一下源码,还是出错啊,是不是还少了东西?
这个需要研究一下。回头我整个教程吧。
我最近看到了RInside库,你可以试一试。
R调用C,在linux生成.so,在windows生成.dll吧。我发现很神奇的是,用R CMD SHLIB命令生成的dll既可以被R调用,也可以被Java调用。所以我想用利用这个原理,在别的语言里使用R的方法,但是可行吗
我觉得是可行的。