R当中调用C 8

之前写了一小篇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)

8 thoughts on “R当中调用C

  1. Reply ZiSu 7月 29,2014 3:32 上午

    欧老师你好,
    我又来问问题了。
    博文中提到的 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在哪里
    您能解答我的问题吗,谢谢

  2. Reply ZiSu 7月 29,2014 7:10 下午

    昨天太粗心了,没看见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进来的做法真的行得通吗?

  3. Reply ZiSu 8月 3,2014 3:48 上午

    已经把R目录下的include文件夹里所有.h 复制到 VC目录下的include文件夹中,也在VC中把它们全部include进来,运行一下源码,还是出错啊,是不是还少了东西?

  4. Reply ZiSu 10月 14,2014 7:24 上午

    R调用C,在linux生成.so,在windows生成.dll吧。我发现很神奇的是,用R CMD SHLIB命令生成的dll既可以被R调用,也可以被Java调用。所以我想用利用这个原理,在别的语言里使用R的方法,但是可行吗

Leave a Reply

  

  

  

%d 博主赞过: