Rcpp is an R package that facilitates seamless integration of R and C++ code. By providing tools and classes within this R package, users can easily write high-performance C++ functions and use them directly in R, which allows users to combine R’s statistical capabilities with the speed and flexibility of C++.
Rcpp acts as an Application Programming Interface (API) between R and C++. As an API, Rcpp defines a set of tools and conventions that enable R and C++ code to communicate and work together seamlessly, making it easy to call C++ functions from R and exchange data between the two languages.
There are other options out there besides Rcpp, e.g., cpp11 and cpp4r. We choose to use Rcpp because
Using {Rcpp} you can compile C++ code using R and never have to interact with an external file or compiler using one of the two following functions:
Rcpp::cppFunction().Rcpp::evalCpp().library(Rcpp)
# Compile inline C++ using R
Rcpp::cppFunction("int add(int x, int y, int z) {
int sum = x + y + z;
return sum;
}")
# after compiling, add works like a regular R function
add## function (x, y, z)
## .Call(<pointer: 0x7fbbd4c7f150>, x, y, z)
## [1] 6
## [1] 4
## [1] 1.797693e+308
You can save your C++ code in a separate file with the .cpp extension
and call this file from R using Rcpp::sourceCpp(). For
example, the code below stores meanC, a C++ function that
calculates the mean of a numeric vector. This code can be saved in a
.cpp file, e.g., mean.cpp and compiled within your R
session with Rcpp::sourceCpp("mean.cpp"). Again, if you do
not want to use external cpp files, you can wrap the C++ code in quotes
and store it as an R object like the example above. Either way, the C++
is then made available within the R session using
Rcpp::sourceCpp(). Both methods are illustrated below.
/*
The include statement allows you to use the Rcpp library in Rcpp.h, which
includes NumericVector.
*/
#include <Rcpp.h>
/*
You can use Rcpp functions like NumericVector without specifying their
namespace on each instance if you include the entire Rcpp namespace using the
line below, e.g., you can use `NumericVector` rather than
`Rcpp::NumericVector`.
*/
using namespace Rcpp;
/*
Use Rcpp::export to export the following function to R
*/
// [[Rcpp::export]]
double meanC(NumericVector x) {
/*
.size() is a member function of Rcpp::NumericVector class and can be accessed
with the dot operator from any Rcpp::NumericVector object
*/
int n = x.size();
double total = 0;
for (int i = 0; i < n; ++i) {
/*
+= is the same as x = x + y, and is known as an overload operator
*/
total += x[i];
}
return total / n;
}# code can be saved in .cpp file and compiled
# Rcpp::sourceCpp("mean.cpp")
# meanC(1:10)
# The same code that is stored in mean.cpp can be sourced directly
src <-
"#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
double meanC(NumericVector x) {
int n = x.size();
double total = 0;
for (int i = 0; i < n; ++i) {
total += x[i];
}
return total / n;
}
"
# compile the text
Rcpp::sourceCpp(code = src)
meanC(1:10)## [1] 5.5
Compiled C++ is often much faster than R. Below we compare our
compiled meanC function against the R function
mean using {microbenchmark}.
## Unit: microseconds
## expr min lq mean median uq max neval
## mean(x) 468.845 492.3350 496.3064 495.069 503.326 528.644 100
## meanC(x) 419.422 420.1125 434.5882 420.914 430.528 1397.671 100
In FIMS, most of the C++ code is organized into several header files
that are stored in the inst/include/
directory. These files include the definitions of Rcpp modules and
interfaces (in inst/include/interface/rcpp/*) that make C++ classes and
functions accessible from R.
A single C++ source file (i.e., src/FIMS.cpp)
lists all of the header files and serves as their main entry point for
compilation. The build process is managed by the src/Makevars
file (and src/Makevars.win
for Windows machines), which sets the necessary compiler and linker
flags for R to compile the C++ code into a shared library (DLL or SO).
This shared library is automatically loaded by R when users call
library(FIMS), allowing R users to call high-performance
C++ code without needing to use Rcpp::sourceCpp()
manually.
Rcpp types map standard C++ data types to R objects enabling seamless
data exchange and operations between R and C++ for efficiency. Example
Rcpp types include classes like NumericVector and
IntegerVector. Rcpp types handle everything from basic data
to complex structures like matrices, lists, and functions. Additionally,
each type handles memory management for you automatically.
intdoubleboolStringIntegerVectorNumericVectorLogicalVectorCharacterVectorUnder the hood, every R object—whether it’s a number, vector, list,
or function—is represented in C code as a SEXP
(S-expression). A SEXP is essentially a pointer (see the section
on pointers in the C++ vignette) to a data structure called a
SEXPREC. The SEXPREC structure contains
information about the type of the object (such as numeric, integer,
logical, or character), as well as the actual data and other metadata.
This design allows R to handle all its objects in a uniform way,
regardless of their specific type. See the SEXP Structures
in C for more information.
When you use Rcpp types like NumericVector, Rcpp
automatically manages the conversion between these internal R
representations and C++ types, so you usually don’t need to interact
with SEXP directly. However, when writing more advanced C++
code or working with the R API at a low level, you may encounter
SEXP types explicitly.
When working with C++ code that doesn’t use Rcpp types directly, you
can use wrap() and as<>() to convert
between R objects (SEXP) and C++ types.
as<>(): Converts R objects (SEXP) to C++
typeswrap(): Converts C++ types to R objects (SEXP)These functions are particularly useful when you want to use standard
C++ types (like std::vector<double>) in your C++ code
while still maintaining compatibility with R. See get_report()
in rcpp_models.hpp for an example of as<>().
#include <Rcpp.h>
using namespace Rcpp;
template <typename T>
T meanC(std::vector<T> x) {
int n = x.size();
T total = 0;
for(int i = 0; i < n; ++i) {
total += x[i];
}
return total / n;
}
// [[Rcpp::export]]
SEXP mean_wrap(SEXP input){
// Convert R object to C++ std::vector
std::vector<double> x = as<std::vector<double>>(input);
// Perform calculation
double mean = meanC(x);
// Convert C++ result back to R object
return wrap(mean);
}Rcpp methods are functions that belong to Rcpp classes and allow you to interact with C++ objects from R in an object-oriented way.
:: on a class. For
example, NumericVector::create() creates a numeric vector
without needing an existing object.$ operator in R and the dot operator in C++. For
example, the .size() member function of the
NumericVector class returns the number of elements in the
Rcpp::NumericVector class. If you have any experience with
Python, it’s somewhat similar to the way you use dot notation
there.src <-
"#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
NumericVector fun() {
//Call static methods on NumericVector
//Create new vector of size 3 using numbers 1, 2, and 3
NumericVector v = NumericVector::create(1, 2, 3);
Rcout << NumericVector::get_na() << std::endl;
//Call member methods on object v of class NumericVector
//Print the length of v
Rcout << v.size() << std::endl;
//append the number 4 to the vector using push_back()
v.push_back(4);
return v;
}
"
# Compile the code and call it
sourceCpp(code = src)
fun()## nan
## 3
## [1] 1 2 3 4
Rcpp
modules are a feature of the Rcpp package that allow you to expose
C++ classes, methods, and functions to R in a structured and
object-oriented way. This makes it possible to work with C++ objects
directly from R, enabling more advanced and efficient workflows that
combine the strengths of both languages. A module is defined in C++
using the RCPP_MODULE macro.
By using modules, you avoid the need to write low-level interface code for each function or class you want to expose. Instead, you describe the interface in a concise and readable way, and Rcpp handles the details of data conversion and method dispatch.
Rcpp modules can be used to expose C++ functions and classes using
the Rcpp macro RCPP_MODULE. Without
RCPP_MODULE the C++ that you want to call within R would
have to be extremely complex to work within the R environment. Much of
the complexity of changing R input (SEXP) into C++ types is handled by
including #include <Rcpp.h> but
RCPP_MODULE handles some additional complexity.
Within a module, you can register the following components:
Thus, RCPP_MODULEs can include .field,
.constructor, .method, and
.property arguments, where .field can be used
with two or three arguments. For example, the class that we create below
called Uniform is complex with a constructor; two inputs,
min and max; and one method,
draw. Additionally, classes can include
.field_readonly, which prevents it from being modified
within R.
First, we define a C++ class for generating uniform random numbers:
#include <Rcpp.h>
using namespace Rcpp;
class Uniform {
public:
Uniform(double min_, double max_) :
min(min_), max(max_) {}
NumericVector draw(int n) {
RNGScope scope;
return runif(n, min, max);
}
double min, max;
};Then we expose this class to R using RCPP_MODULE:
RCPP_MODULE(unif_module) {
class_<Uniform>("Uniform")
.constructor<double,double>()
.field("min", &Uniform::min, "minimum value")
.field("max", &Uniform::max, "maximum value")
.method("draw", &Uniform::draw);
}After compiling with Rcpp::sourceCpp(), you can use this
class from R:
Rcpp provides several built-in types, such as
Rcpp::NumericVector, but when you define your own C++
class, you need to make Rcpp aware of it. This is done using the RCPP_EXPOSED_CLASS
macro. For example, if you create a new class called
FancyVector, you need add
RCPP_EXPOSED_CLASS(FancyVector) to your code. This macro
instructs Rcpp to generate the necessary type information, allowing your
new class to be used within Rcpp modules. As a result, you can pass
objects of your class between R and C++, store them in Rcpp containers,
and use them as arguments or return values in Rcpp-exposed
functions.
Within FIMS, we first use RCPP_EXPOSED_CLASS() to expose
all of our new type classes, e.g.,
RCPP_EXPOSED_CLASS(Parameter) in src/fims_modules.hpp.
Once all of the type classes are exposed, we then use a single instance
of RCPP_MODULE to expose the C++ to R in that same
file.