This document provide brief instruction for calling C from R using Rcpp
. This chapter from Advanced R provides a wonderful and comprehensive description of Rcpp. Many materials in this lecture are borrowed from there.
There are two old-fashion R-to-C interfaces: .C
and .Call
, for those I have some description here, and some comparisons here.
gcc
, clang
, etc). If you want to have C code within R package, supports for make
, perl
are necessary.
cppFunction
We demonstrate this using a very simple Hello world
example. It just prints out a Hello world
message. There’s no input/output.
library(Rcpp)
cppFunction( 'void hello() {
printf("Hello world"); }' )
hello()
A few notes:
cppFunction
takes source C code as character
in R. So the whole chunk of C++ codes needs to be enclosed by single quotations marks.sourceCpp
In most cases you want to write and save your C/C++ codes in a seperate file. Below is my hello
function, which is saved in a seperate file hello.cpp.
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
void hello() {
printf("Hello world! \n");
}
The function can be compiled and load into R by
sourceCpp("hello.cpp")
Now the function can be accessed in R by hello()
.
A few notes:
.o
and .so
files) from compilation are not saved.To recompile the C++ code for every new R session is not ideal. To compile it once and use forever, there are two options:
.C
or .Call
way to include C/C++ code. I have some descriptions here.Now we will write a slightly more complicated function to compute the sum of a vector. Here is the C code named mySum.cpp
. It takes a vector from R, computes and returns the sum.
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
double mySum(NumericVector x) {
int i;
double result=0.0;
for(i=0; i<x.size(); i++)
result = result + x[i];
return(result);
}
Now we can compile and run it in R
sourceCpp("mySum.cpp")
mySum(1:10)
A few notes:
NumericVector
is the data type defined in Rcpp (it’s not a standard C++ data type)size()
function.The function below (myRowSum.cpp) computes the row-wise sums of a matrix.
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
NumericVector myRowSum( NumericMatrix x ) {
int nrow = x.nrow(), ncol = x.ncol();
int i, j;
double tmp;
NumericVector result(nrow);
for(i=0; i<nrow; i++) {
tmp = 0.0;
for(j=0; j<ncol; j++)
tmp = tmp + x(i,j);
result[i] = tmp;
}
return(result);
}
Now we can compile run the code in R
sourceCpp("myRowSum.cpp")
X = matrix(1:6, ncol=2)
myRowSum(X)
A few notes:
NumericMatrix
.nrow()
and ncol()
functions in C++.x(i,j)
. Pay attention: it’s ()
, not []
, different from accessing a NumericVector
.Variables passed from R to C++ need to be declared with correct data types. Basic data types in C++ are:
int, double, bool, String
IntegerVector, NumericVector, LogicalVector, CharacterVector
IntegerMatrix, NumericMatrix, LogicalMatrix, CharacterMatrix
For more complex data types and classes (list, data frame, function, etc.) please refer to the Rcpp manual.
Now I want to create a package pkgwithRcpp
with some C++ codes. Assume I have the cpp file (myRowSum.cpp
) in current directory, I can do the following:
Run
Rcpp.package.skeleton("pkgwithRcpp", cpp_files="myRowSum.cpp",
example_code=FALSE, attributes=FALSE)
This creates the package skeleton. Note that
Rcpp.package.skeleton
is a function in Rcpp
providing similar functionality of the standard package.skeleton
function. But it addes additional lines in DESCRIPTION
and NAMESPACE
files to ensure the C++ codes can be accessed.pkgwithRcpp
will appear in current directory with necessary files.pkgwithRcpp
, there is a src
subdirectory containing the C++ code.pkgwithRcpp
, the R
subdirectory is empty.Run Rcpp::compileAttributes("pkgwithRcpp")
. This step will scan the package and generate some necessary files. After this step, a new file RcppExports.R
will appear in the R
subdirectory, which defines the R interface function for the C function.
Now the package can be built and installed using typical procedure.
A few notes:
The messages for compiling C++ will appear when the package is being installed.
Anyone who needs to install this package from source need to have C++ compiler.
To avoid that, one can build a (platform-dependent) binary package, which precompiles the C++ code so that user without a compiler can install. Do following to build binary package:
R CMD INSTALL --build pkgwithRcpp
If I need to have multiple C++ files in the package, I can copy all cpp files into the src
directory. But after adding new cpp files, I need to rerun Rcpp::compileAttributes("pkgwithRcpp")
to update the R wrapper function.
For more details, view vignette("Rcpp-package")
.
Here I will introduce how to debug a standalone C code. Debugging C code within an R package is slightly different, which will be introduced later.
Debugging function is platform-dependent.
As an example below, I debug myRowSum
function. The typical steps are:
Run R within a GNU debugger.
R -d gdb
.R -d lldb
.Once we are in the debugger, start R by doing run
. Then we will be in the R console.
Debugging steps.
sourceCpp("myRowSum.cpp")
.Control-C
to switch back to lldb
environment.myRowSum.cpp:11
.continue
in lldb
. Now we come back to R. X = matrix(1:6, ncol=2)
myRowSum(X)
myRowSum
is hit, it will go to lldb
environment. We can then do all sorts of debugging, such as run line by line, inspect the value of variables, etc.For detailed commands for gdb
and lldb
, see https://lldb.llvm.org/use/map.html.
Debug C/C++ codes with an R package is a little troubler. Here is an excellent description: https://blog.davisvaughan.com/2019/04/05/debug-r-package-with-cpp/.
The first important thing is to make sure the C/C++ codes are compiled with a “debug-able” flag (usually a -g
). This can be achieved by modifying the .R/Makevars
files (Or you can run usethis::edit_r_makevars()
in R) by adding a few lines like
CXXFLAGS = -g -O0
CFLAGS = -g -O0
CPPFLAGS = -g -O0
Make sure you change it back (from -O0
to -O2
), or remove this file after debugging. Otherwise all future packages will be compiled with debug flag, which will make them slower.
The general steps of debuggin the C/C++ codes with an R package is:
R -d lldb
lldb
lldb
(Control-C
), set breakpoint in C code.