Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting an integer vector from C to R Lanugage using .Call

Tags:

c

r

I am using .Call for the first time. I wrote the simple code below, where I pass an integer and return an SEXP (to R):

#include <R.h>
#include <Rdefines.h>

SEXP setInt(int *a) {
   SEXP myint;
   int *p_myint;
   int len = 5;

   PROTECT(myint = NEW_INTEGER(len)); // Allocating storage space
   p_myint = INTEGER_POINTER(myint); // ponit to SEXP object

   p_myint[0] = *a;

   UNPROTECT(1);
   return myint;
}

Thus, I call R CMD SHLIB which creates the dll (no problem here).

When run the code below (in R), I get an answer different than c(100,0,0,0,0):

> dyn.load(file.path(path.dll,paste0("useC", .Platform$dynlib.ext)))
> a<-100
> out<- .Call("setInt",as.integer(a))
> out
[1] 536870925         0         0         0         0

P.S: Probably I am getting the value of the address of pointer variable "a", instead of its value. But I don't know what I am missing here.

EDIT:

Using the answer of @JoshuaUlrich I fixed the code and added more features. The fixed code is:

SEXP setInt(SEXP a,SEXP pos) {
   SEXP myint;
   int *p_a;
   int *p_myint;
   int len = 5;

   PROTECT(myint = NEW_INTEGER(len)); // Allocate storage space, with default 5 zeroes
   p_myint = INTEGER_POINTER(myint); // ponit to SEXP object

   p_a = INTEGER_POINTER(a);

   p_myint[0] = p_a[(asInteger(pos)-1)];  // get the element at pos

   UNPROTECT(1);
   return myint;
}

When calling from R you get:

a<-c(100,200,300)
pos<-1
out<- .Call("setInt",as.integer(a),as.integer(pos))

> out
[1] 100 0 0 0 0

pos<-2
out<- .Call("setInt",as.integer(a),as.integer(pos))

> out
[1] 200 0 0 0 0
like image 574
Marcelo Sardelich Avatar asked Oct 22 '22 06:10

Marcelo Sardelich


1 Answers

Your setInt function definition accepts a C int as its only argument. But in your R code, you're passing a INTSXP, not a C int. In general, C functions called via .Call should only accept SEXP as arguments.

So change your function definition to:

SEXP setInt(SEXP a) {
   SEXP myint;
   int *p_myint;
   int len = 5;

   PROTECT(myint = NEW_INTEGER(len)); // Allocating storage space
   p_myint = INTEGER_POINTER(myint); // ponit to SEXP object

   p_myint[0] = AS_INTEGER(a);

   UNPROTECT(1);
   return myint;
}

Also note that you should probably ensure a is INTSXP and length(a)==1. If length(a) > 1, you can do this:

int *p_a;
p_a = INTEGER_POINTER(a);
p_myint[0] = p_a[1];  // get second element from a
like image 120
Joshua Ulrich Avatar answered Oct 24 '22 10:10

Joshua Ulrich