<- r"(
code #include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
void hello_world() {
Rcout << "Hello, World!" << std::endl;
}
)"
sourceCpp(code=code)
hello_world()
Hello, World!
Paolo Bosetti
Thanks to the Rcpp
library it is particularly easy to interface R with C/C++ code. This is called wrapping, i.e. making a C++ function available to R as if it were an R function. This is particularly useful when performance is an issue, as C++ code is generally faster than R code.
The Rcpp
library is designed to make it easy to interface with C++ functions and classes. Being a valid subset of C++, C can be used as well, although if you use external libraries compiled in C, you ought to remember to declare all functions as extern "C"
in the C headers.
I am linking here useful documentation and resources for Rcpp
:
C++ code can be embedded in R code as strings or read from source files:
code <- r"(
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
void hello_world() {
Rcout << "Hello, World!" << std::endl;
}
)"
sourceCpp(code=code)
hello_world()
Hello, World!
Note the special comment // [[Rcpp::export]]
that tells the sourceCpp
function to make the function available to R. Without that comment, the function is compiled but not exposed to R.
Vectors, lists and matrices can be passed to and returned from C++ functions:
code <- r"(
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
NumericVector add_one(NumericVector x) {
return x + 1;
}
// [[Rcpp::export]]
NumericVector scale(NumericVector x, double factor) {
return x * factor;
}
// [[Rcpp::export]]
NumericVector add(NumericVector x, NumericVector y) {
return x + y;
}
// [[Rcpp::export]]
NumericVector operate(NumericVector x, NumericVector y) {
if (x.size() != y.size()) {
stop("Vectors must have the same length");
}
NumericVector res(x.size());
for (int i = 0, j = x.size()-1; i < x.size(); i++, j--) {
res[i] = x[i] + y[j];
}
return res;
}
// [[Rcpp::export]]
NumericMatrix outer_product(NumericVector x, NumericVector y) {
NumericMatrix res(x.size(), y.size());
for (int i = 0; i < x.size(); i++) {
for (int j = 0; j < y.size(); j++) {
res(i, j) = x[i] * y[j];
}
}
return res;
}
// [[Rcpp::export]]
DataFrame data_frame(NumericVector x, NumericVector y) {
if (x.size() != y.size()) {
stop("Vectors must have the same length");
}
return DataFrame::create(
_["x"] = x,
_["y"] = y
);
}
)"
sourceCpp(code=code)
add_one(1:10)
[1] 2 3 4 5 6 7 8 9 10 11
[1] 2 4 6 8 10 12 14 16 18 20
[1] 12 14 16 18 20 22 24 26 28 30
[1] 21 21 21 21 21 21 21 21 21 21
[,1] [,2] [,3] [,4]
[1,] 1 2 3 4
[2,] 2 4 6 8
[3,] 3 6 9 12
x y
1 1 1
2 2 4
3 3 9
Look in Rcpp for everyone for the documentation of the Rcpp data classes and their methods.
R functions can be passed to and executed by C++ code, enabling functional programming:
code <- r"(
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
NumericVector apply(NumericVector x, Function f) {
NumericVector res(x.size());
for (int i = 0; i < x.size(); i++) {
res[i] = as<double>(f(x[i]));
}
return res;
}
)"
sourceCpp(code=code)
apply(1:10, \(x){ x + 1 })
[1] 2 3 4 5 6 7 8 9 10 11
If the passed function takes named arguments, it can be called in C++ as f(_["x"] = 10, _["y"] = 20)
. Look in Hadley Wickham’s Advanced R for more information.
Eigen (or Armadillo) can be used to perform linear algebra operations:
code <- r"(
#include <RcppEigen.h>
using namespace Rcpp;
using namespace Eigen;
// [[Rcpp::depends(RcppEigen)]]
// [[Rcpp::export]]
NumericVector eigen_example(NumericMatrix x) {
Map<MatrixXd> m(as<Map<MatrixXd> >(x));
SelfAdjointEigenSolver<MatrixXd> es(m);
return wrap(es.eigenvalues());
}
)"
sourceCpp(code=code)
eigen_example(matrix(1:4, 2, 2))
[1] 0 5
C++ classes can be wrapped as modules and used in R as S4 objects. In this case, there is no need to mark the functions with // [[Rcpp::export]]
, you rather define a class mapping with the RCPP_MODULE
macro:
code <- r"(
#include <Rcpp.h>
using namespace Rcpp;
class Counter {
public:
Counter() : count(0) {}
void increment() { count++; }
int get() { return count; }
private:
int count;
};
RCPP_MODULE(counter_module) {
class_<Counter>("Counter")
.constructor()
.method("increment", &Counter::increment)
.method("get", &Counter::get);
}
)"
sourceCpp(code=code)
counter <- new(Counter)
counter$increment()
counter$increment()
counter$get()
[1] 2