RCPP Modules
RCPP Modules
This note discusses Rcpp modules. Rcpp modules allow programmers to Here we use the (templated) Rcpp converter as() which can
expose C++ functions and classes to R with relative ease. Rcpp modules transform from a SEXP to a number of different C++ and Rcpp
are inspired from the Boost.Python C++ library (Abrahams and Grosse- types. The Rcpp function wrap() offers the opposite functionality
Kunstleve, 2003) which provides similar features for Python. and converts many known types to a SEXP.
This process is simple enough, and is used by a number of
Rcpp | modules | R | C++
CRAN packages. However, it requires direct involvement from the
programmer, which quickly becomes tiresome when many functions
1. Motivation are involved. Rcpp modules provides a much more elegant and
Exposing C++ functionality to R is greatly facilitated by the Rcpp unintrusive way to expose C++ functions such as the norm function
package and its underlying C++ library (Eddelbuettel et al., 2022; shown above to R.
Eddelbuettel and François, 2011). Rcpp smoothes many of the We should note that Rcpp now has Rcpp attributes which ex-
rough edges in R and C++ integration by replacing the traditional tends certain aspect of Rcpp modules and makes binding to simple
R Application Programming Interface (API) described in ‘Writing functions such as this one even easier. With Rcpp attributes we can
R Extensions’ (R Core Team, 2021) with a consistent set of C++ just write
classes. The ‘Rcpp-jss-2011’ vignette (Eddelbuettel et al., 2022; #include <Rcpp.h>
Eddelbuettel and François, 2011) describes the API and provides
an introduction to using Rcpp. // [[Rcpp::export]]
These Rcpp facilities offer a lot of assistance to the programmer double norm(double x, double y) {
wishing to interface R and C++. At the same time, these facilities return sqrt(x*x + y*y);
are limited as they operate on a function-by-function basis. The }
programmer has to implement a .Call compatible function (to
conform to the R API) using classes of the Rcpp API as described See the corresponding vignette (Allaire et al., 2022) for details,
in the next section. but read on for Rcpp modules which provide features not covered
by Rcpp attributes, particularly when it comes to binding entire
1.1. Exposing functions using Rcpp. Exposing existing C++ func-
C++ classes and more.
tions to R through Rcpp usually involves several steps. One ap-
proach is to write an additional wrapper function that is responsible 1.2. Exposing classes using Rcpp. Exposing C++ classes or structs
for converting input objects to the appropriate types, calling the is even more of a challenge because it requires writing glue code
actual worker function and converting the results back to a suitable for each member function that is to be exposed.
type that can be returned to R (SEXP). Consider the norm function Consider the simple Uniform class below:
below:
class Uniform {
double norm( double x, double y ) {
public:
return sqrt( x*x + y*y );
Uniform(double min_, double max_) :
}
min(min_), max(max_) {}
This simple function does not meet the requirements set by the NumericVector draw(int n) {
.Call convention, so it cannot be called directly by R. Exposing RNGScope scope;
the function involves writing a simple wrapper function that does return runif(n, min, max);
match the .Call requirements. Rcpp makes this easy. }
using namespace Rcpp;
RcppExport SEXP norm_wrapper(SEXP x_, SEXP y_) { private:
// step 0: convert input to C++ types double min, max;
double x = as<double>(x_), y = as<double>(y_); };
// step 1: call the underlying C++ function To use this class from R, we at least need to expose the construc-
double res = norm(x, y); tor and the draw method. External pointers (R Core Team, 2021)
are the perfect vessel for this, and using the Rcpp:::XPtr template
// step 2: return the result as a SEXP from Rcpp we can expose the class with these two functions:
return wrap(res);
}
RCPP_MODULE(mod_formals2) { .constructor<double,double>()
function("norm", &norm,
List::create(_["x"], _["y"] = 0.0), .field("min", &Uniform::min)
"Provides a simple vector norm"); .field("max", &Uniform::max)
}
.method("draw", &Uniform::draw)
This can be used as follows: .method("range", &uniformRange)
mod <- Module("mod_formals2", getDynLib(fx)) ;
norm <- mod$norm
args(norm) }
The ellipsis (...) can be used to denote that additional argu- unif_module <- Module("unif_module",
ments are optional; it does not take a default value. getDynLib(fx))
using namespace Rcpp; Uniform <- unif_module$Uniform
u <- new(Uniform, 0, 10)
double norm(double x, double y) { u$draw(10L)
return sqrt(x*x + y*y); u$range()
} u$max <- 1
u$range()
RCPP_MODULE(mod_formals3) { u$draw(10)
function("norm", &norm,
List::create(_["x"], _["..."]), class_ is templated by the C++ class or struct that is to be
"documentation for norm"); exposed to R. The parameter of the class_<Uniform> constructor
} is the name we will use on the R side. It usually makes sense to
use the same name as the class name. While this is not enforced, it
This works similarly from the R side where the ellipsis is also might be useful when exposing a class generated from a template.
understood: Then constructors, fields and methods are exposed.
mod <- Module("mod_formals3", getDynLib(fx)) 2.2.2. Exposing constructors using Rcpp modules. Public
constructors
norm <- mod$norm that take from 0 and 6 parameters can be exposed to the R level
args(norm) using the .constructor template method of class_.
Optionally, .constructor can take a description as the first
2.2. Exposing C++ classes using Rcpp modules. Rcpp modules argument.
also provide a mechanism for exposing C++ classes, based on the
.constructor<double,double>("sets the min and "
reference classes introduced in R 2.12.0.
"max value of the distribution")
2.2.1. Initial example. A class is exposed using the class_ keyword.
The Uniform class may be exposed to R as follows: Also, the second argument can be a function pointer (called
validator) matching the following type:
using namespace Rcpp;
class Uniform { typedef bool (*ValidConstructor)(SEXP*,int);
public:
Uniform(double min_, double max_) : The validator can be used to implement dispatch to the appro-
min(min_), max(max_) {} priate constructor, when multiple constructors taking the same
number of arguments are exposed. The default validator always
NumericVector draw(int n) const { accepts the constructor as valid if it is passed the appropriate num-
RNGScope scope; ber of arguments. For example, with the call above, the default
return runif(n, min, max); validator accepts any call from R with two double arguments (or
} arguments that can be cast to double).
TODO: include validator example here
double min, max;
}; 2.2.3. Exposing fields and properties. class_ has three ways to expose
fields and properties, as illustrated in the example below:
double uniformRange(Uniform* w) {
return w->max - w->min;
The .property method allows indirect access to fields through .property( "x", &Bar::get_x, &Bar::set_x )
a getter and a setter. The setter is optional, and the property is .method( "stats", &Bar::stats )
considered read-only if the setter is not supplied. A description of ;
the property is also allowed: }
double z_get(Foo* foo) { return foo->get_z(); } • A public member function of the class, either const or non-
const, that returns void or any type that can be handled by
TODO: mention overloading, need good example. Base *newBase( const std::string &name ) {
if (name == "d1"){
2.2.6. Const and non-const member functions. .method is able to ex- return new Derived1;
pose both const and non-const member functions of a class. There } else if (name == "d2"){
are however situations where a class defines two versions of the return new Derived2;
same method, differing only in their signature by the const-ness. } else {
It is for example the case of the member functions back of the return 0;
std::vector template from the STL. }
reference back ( ); }
const_reference back ( ) const;
RCPP_MODULE(mod) {
To resolve the ambiguity, it is possible to use .const_method Rcpp::class_< Base >("Base")
or .nonconst_method instead of .method in order to restrict the .factory<const std::string&>(newBase)
candidate methods. .method("name", &Base::name);
}
2.2.7. Special methods. Rcpp considers the methods [[ and [[<-
The newBase method returns a pointer to a Base object. Since
special, and promotes them to indexing methods on the R side.
that class is an abstract class, the objects are actually instances of
2.2.8. Object finalizers. The .finalizer member function of class_ Derived1 or Derived2. The same behavior is now available in R:
can be used to register a finalizer. A finalizer is a free function that mod <- Module("mod", getDynLib(fx))
takes a pointer to the target class and return void. The finalizer is Base <- mod$Base
called before the destructor and so operates on a valid object of dv1 <- new(Base, "d1")
the target class. dv1$name() # returns "Derived1"
It can be used to perform operations, releasing resources, etc dv2 <- new(Base, "d2")
... dv2$name() # returns "Derived2"
The finalizer is called automatically when the R object that
encapsulates the C++ object is garbage collected.
2.2.10. S4 dispatch. When a C++ class is exposed by the class_
2.2.9. Object factories. The
.factory member function of class_ template, a new S4 class is registered as well. The name of the S4
can be used to register a factory that can be used as alternative to class is obfuscated in order to avoid name clashes (i.e. two modules
a constructor. A factory can be a static member function or a free exposing the same class). This allows implementation of R-level
function that returns a pointer to the target class. Typical use-cases (S4) dispatch.
include creating objects in a hierarchy: For example, consider the C++ class World exposed in module
yada:
#include <Rcpp.h>
using namespace Rcpp; class World {
public:
// abstract class World() : msg("hello") {}
class Base { void set(std::string msg) { this->msg = msg; }
public: std::string greet() { return msg; }
virtual ~Base() {}
virtual std::string name() const = 0; private:
}; std::string msg;
};
// first derived class
class Derived1: public Base { RCPP_MODULE(yada){
public: using namespace Rcpp;
Derived1() : Base() {}
The loadModule function has an argument what to control 3.5. Module documentation. Rcpp defines a prompt method for
which objects are exposed in the package namespace. The special the Module class, allowing generation of a skeleton of an Rd file
value TRUE means that all objects are exposed. containing some information about the module.
This will look in the package’s DESCRIPTION file for the 4. Future extensions
RcppModules field, load each declared module and populate their Boost.Python has many more features that we would like to port
contents into the package’s namespace. For example, a package to Rcpp modules: class inheritance, default arguments, enum types,
defining modules yada, stdVector, NumEx would have this decla- ...
ration: