Calling C++ function from R environment (Linux-64bit)

422 views
Skip to first unread message

Shekhar

unread,
Apr 19, 2011, 6:58:33 AM4/19/11
to Bangalore R Users - BRU
Hi,
This post is the sequel of the Rcpp installation (
http://groups.google.com/group/brumail/browse_thread/thread/aefbcab7a6f7804b
)

Assuming the Rcpp is properly installed on the system, this post is
meant for calling C++ function from R environment.
We will start with hello world program to gain confidence and then we
can move on to more sophisticated program.

The sample program is on linux ( Windows as usual somewhat
difficult.we need to download Rtools and lot of configuration, but for
sure we are going to break it)

For calling C/C++ functions from R following things should be taken
care of:

1. Linking the Rcpp header file. For this you have two options
OPTION 1: Give the directory path of the Rcpp in the
LD_LIBRARY_PATH
export LD_LIBRARY_PATH=/usr/lib64/R/
library/Rcpp/include
This option however was not working for
me, and i didn't dig more on this so i chose the second option.
OPTION 2: Copy the entire include directory in the /usr/
include/R directory
cp -r /usr/lib64/R/library/Rcpp/include /
usr/include/R

2.Writing C++ function:
(A) The C++ function which you will be calling from the R should be
enclosed in the extern "C" scope. Even though Rcpp is meant for
calling c++ function from R, but it compiles only C function. if you
dont enclose the functions within extern "C" scope, there wont be any
error but the function will not be loaded in the R symbol table.(More
on this later)

(B) While passing arguments to these C/C++ function, the arguments
should be pointer. While sending the "R" variables to these functions,
R will convert them to pointers and send them to the functions.

(C) The most intriguing part is that the function which you need to
call should take at least one parameter or else error will be thrown.

The following steps will give you the idea to how to call the
functions from R

Step1: Write your cpp function
Step2: Run the command R CMD SHLIB file.cpp. This command will create
the library and the object file
Step3: On the R side, load the library which is created in the step2
by using the function
dyn.load("path to the library name")
Step4: Call the function which you have declared in the cpp file by
the following conventions
(i) .C("FunctionName",variable that is needed by the
functions,...)
(ii) .Call("FunctionName",.....)
You need to send the variables in the same order in which the function
wants that or else you know what will happen?

Step4: The result you can verify

Now if the steps are clear, we can move on to our first sample
program:

------------------------------------------------
(1) Writing Cpp functions
------------------------------------------------
The following lines are there in my file hello_world.cpp
#include <Rcpp.h>
#include <R.h>
extern "C"
{
void Print(int* i)
{
Rprintf("hello world=%d",*i);
}
}

Description: As you can see that i have a function name Print(int* i),
even though that is not useful, but this is the restriction. I need to
do some research on that. The Rprintf function is C equivalent of
printf function which is defined in the header file R.h.
Another important thing to note is that i have enclosed the function
in the extern "C" scope. This is another restriction

---------------------------------------------------
(2) Running the command to create library and object file
-----------------------------------------------------
Run the following command to create the library:

R CMD SHLIB hello_world.cpp

This command if runs successfully will create the library
hello_world.so and hello_world.o in the same directory where the cpp
file is residing.

----------------------------------------------------
(3) Loading the libray
--------------------------------------------------
use the function dyn.load() to load the libray
dyn.load("/home/som/CPP/hello_world.so")

--------------------------------------------------------
(4)Check whether the function "Print" which you have written in
hello_world.cpp is present in the symbol table or not
-----------------------------------------------------------------------------------------------
is.loaded("Print")
if it returns true, then no problem, but if it written false, then
there could be following reasons:
(A) Library is not loaded
(B) Library is properly loaded since dyn.load is not throwing
error, then you have to check whether the Print function is under
extern "C" scope or not

----------------------------------------
(5) Calling the function
----------------------------------
If all the above steps are successful, then just declare some
arbitrary variable for ex: x<-10

and call the function as follows:

.C("Print",as.integer(x))

the output on the screen will be
hello world=10[[1]]
[1] 10
The list which you are seeing is nothing but the function returns
list back to the R.


---------------------------------
Sample R script hello.R
---------------------------------
myfirst=function(x)
{
if(!is.loaded('Print'))dyn.load('/home/drilldown/CPP/hello_world.so')
z=.C('Print',as.integer(x))
return(z)
}

z<-10;
ans<-myfirst(z)

if you run this script the output will be something like this:
>source("/home/som/CPP/hello.R")
>hello world=10

if you print "ans" then it will print the list
> ans
[[1]]
[1] 10


Hope you got this. The next post we will deal with some advanced
examples and usage of R variables in c++ functions

Regards,
Som Shekhar

Shekhar

unread,
Apr 19, 2011, 8:02:29 AM4/19/11
to Bangalore R Users - BRU
A quick update on the loading and unloading the library.
Once you have load the library using dyn.load and lets say you have
not unloaded it. And if you make some changes in the cpp file and
created a new library and again you loaded the library. You might be
expecting the new results, but you won't get it until and unless you
unload the library. (dyn.unload("name.so").


Regards,
Som Shekhar

Shekhar

unread,
Apr 26, 2011, 2:44:41 AM4/26/11
to Bangalore R Users - BRU
Hi,
Let us do some more hands-on Rcpp.
Here the problem statement is, we have two arrays and we will create
this arrays in R, pass it to the C/C++ function where they will get
multiplied and the result will be sent back to R.

In the previous post as we seen that there are two mechanisms by which
we can C/C++ function from R.They are

(I) .C convention: In this convention, there is slight restriction
that function should receive everything as pointer.

(II) .Call convention: In this convention, we can directly send the R
object and inside the C++ function we can manipulate these R objects.

-------------------------------------------------------
Using .C convention
----------------------------------------------------
(A) C++ side code:

Filename: array_mult.cpp

Compile as R CMD SHLIB array_mult.cpp

# include <iostream>
#include <Rcpp.h>
#include <R.h>
using namespace std;
extern "C"
{
void mult(int* length_arr,int* arr1, int* arr2, int* result)
{
cout<<"Multiplying two array and storing the result in
result array"<<endl;
for(int i=0;i<*length_arr;i++)
{
result[i] = arr1[i] * arr2[i];
}
}
}

(B) R side code:
# Here we are initialize the two vectors to some values ( vec1 and
vec2) and we also initialize the result vector(vec3).

vec1<-c(1,2,3)
vec2<-c(1,2,3)
vec3<-vector(mode="integer",length=length(vec1))
if(!is.loaded("mult")) dyn.load("array_mult.so")

z<-.C("mult",as.integer(length(vec1)),as.integer(vec1),as.integer(vec2),result=as.integer(vec3))

dyn.unload("array_mult.so")

if you print z, then it will show all the arguments which are passed
to the function including the result:
> z
[[1]]
[1] 3

[[2]]
[1] 1 2 3

[[3]]
[1] 1 2 3

$result
[1] 1 4 9

> z$result
[1] 1 4 9


--------------------------------------------------------
Using .Call convention
--------------------------------------------------------
In this convention you can pass the R objects directly to the C/C++
function. But they need to be received as the R objects (SEXP) which
later on can be converted to desired data pointers.

For example: If you are sending a vector from R, then at the function
you need to receive this vector as SEXP. Now to access the data, you
can use the R macros like REAL, DOUBLE etc.

Let's say we are sending vec<-c(1,2,4) and in the function we are
receiving as SEXP arr;
To get access to the data we can use int* data = REAL(arr)

(A) C++ side code:

File name: array_mult.cpp
Compile as R CMD SHLIB array_mult.cpp

#include <iostream>
#include <R.h>
#include <Rinternals.h>
using namespace std;
extern "C"
{
SEXP mult(SEXP arr1, SEXP arr2)
{
double *i1, *i2, *ans;
SEXP result,length;
// int* vec_length;
cout<<"Som"<<endl;
i1 = REAL(arr1);
i2 = REAL(arr2);
cout<<"Finding the length"<<endl;
length = getAttrib(arr1,R_DimSymbol);
// Rprintf("Length=%d",length[1]);
// vec_length = INTEGER(length);
// cout<<"The length of the vector is="<<*vec_length<<endl;
PROTECT(result = allocVector(REALSXP,3));
ans = REAL(result);
for(int i=0;i<3;i++)
{
ans[i] = i1[i] * i2[i];
}
UNPROTECT(1);
return(result);
}
}


Shekhar

unread,
Apr 26, 2011, 2:49:14 AM4/26/11
to Bangalore R Users - BRU

Hi,
For the above post the R side code will be:

vec1<-c(1,2,3)
vec2<-c(1,2,3)
dyn.unload("array_mult.so")
if(!is.loaded("mult")) dyn.load("array_mult.so")

answer<-.Call("mult",vec1,vec2)
dyn.unload("array_mult.so")

Regards,
Som Shekhar
Reply all
Reply to author
Forward
0 new messages