0% found this document useful (0 votes)
9 views10 pages

RCPP Modules

The document discusses Rcpp modules, which facilitate the exposure of C++ functions and classes to R, inspired by the Boost.Python library. It outlines the motivation behind using Rcpp, the process of exposing functions and classes, and the advantages of Rcpp modules over traditional methods. Additionally, it provides examples and guidelines for defining and using Rcpp modules in R programming.

Uploaded by

timothyzheng2000
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
9 views10 pages

RCPP Modules

The document discusses Rcpp modules, which facilitate the exposure of C++ functions and classes to R, inspired by the Boost.Python library. It outlines the motivation behind using Rcpp, the process of exposing functions and classes, and the advantages of Rcpp modules over traditional methods. Additionally, it provides examples and guidelines for defining and using Rcpp modules in R programming.

Uploaded by

timothyzheng2000
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 10

Exposing C++ functions and classes

with Rcpp modules


Dirk Eddelbuettela and Romain Françoisb
a
http://dirk.eddelbuettel.com; b https://romain.rbind.io/

This version was compiled on January 11, 2022

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);
}

https://cran.r-project.org/package=Rcpp Rcpp Vignette | January 11, 2022 | 1–10


using namespace Rcpp; # syntactic sugar to allow new( "Uniform", ... )
setMethod("initialize", "Uniform",
/// create external pointer to a Uniform object function(.Object, ...) {
RcppExport SEXP Uniform__new(SEXP min_, .Object@pointer <-
SEXP max_) { .Call(Uniform_method("new"), ...)
// convert inputs to appropriate C++ types .Object
double min = as<double>(min_), } )
max = as<double>(max_);
u <- new("Uniform", 0, 10)
// create pointer to an Uniform object and u$draw( 10L )
// wrap it as an external pointer
Rcpp considerably simplifies the code that would be involved
Rcpp::XPtr<Uniform>
for using external pointers with the traditional R API. Yet this still
ptr( new Uniform( min, max ), true );
involves a lot of mechanical code that quickly becomes hard to
maintain and error prone. Rcpp modules offer an elegant way to
// return the external pointer to the R side
expose the Uniform class in a way that makes both the internal
return ptr;
C++ code and the R code easier.
}
2. Rcpp modules
/// invoke the draw method
RcppExport SEXP Uniform__draw(SEXP xp, SEXP n_) { The design of Rcpp modules has been influenced by Python modules
// grab the object as a XPtr (smart pointer) which are generated by the Boost.Python library (Abrahams and
// to Uniform Grosse-Kunstleve, 2003). Rcpp modules provide a convenient and
Rcpp::XPtr<Uniform> ptr(xp); easy-to-use way to expose C++ functions and classes to R, grouped
together in a single entity.
// convert the parameter to int A Rcpp module is created in C++ source code using the
int n = as<int>(n_); RCPP_MODULE macro, which then provides declarative code of what
the module exposes to R.
// invoke the function This section provides an extensive description of how Rcpp
NumericVector res = ptr->draw( n ); modules are defined in standalone C++ code and loaded into R.
Note however that defining and using Rcpp modules as part of
// return the result to R other R packages simplifies the way modules are actually loaded,
return res; as detailed in Section 3 below.
}
2.1. Exposing C++ functions using Rcpp modules. Consider the
As it is generally a bad idea to expose external pointers ‘as is’, norm function from the previous section. We can expose it to R:
they usually get wrapped as a slot of an S4 class. using namespace Rcpp;
Using cxxfunction() from the inline package, we can build
this example on the fly. Suppose the previous example code as- double norm(double x, double y) {
signed to a text variable unifModCode, we could then do return sqrt(x*x + y*y);
}
f1 <- cxxfunction( , "", includes = unifModCode,
plugin = "Rcpp" )
RCPP_MODULE(mod) {
getDynLib(f1) ## will display info about 'f1'
function("norm", &norm);
}
The following listing shows some manual wrapping to access
the code, we will see later how this can be automated: The code creates an Rcpp module called mod that exposes the
norm function. Rcpp automatically deduces the conversions that
setClass("Uniform",
are needed for input and output. This alleviates the need for a
representation( pointer = "externalptr"))
wrapper function using either Rcpp or the R API.
On the R side, the module is retrieved by using the Module
# helper
function from Rcpp
Uniform_method <- function(name) {
paste("Uniform", name, sep = "__") inc <- '
} using namespace Rcpp;

# syntactic sugar to allow object$method( ... ) double norm( double x, double y ) {


setMethod("$", "Uniform", function(x, name) { return sqrt(x*x + y*y);
function(...) }
.Call(Uniform_method(name) ,
x@pointer, ...) RCPP_MODULE(mod) {
} ) function("norm", &norm);

2 | https://cran.r-project.org/package=Rcpp Eddelbuettel and François


} yada$bla2(2L, 5.0)
'
The requirements for a function to be exposed to R via Rcpp
fx <- cxxfunction(signature(), modules are:
plugin="Rcpp", include=inc) • The function takes between 0 and 65 parameters.
mod <- Module("mod", getDynLib(fx)) • The type of each input parameter must be manageable by the
Note that this example assumed that the previous code segment Rcpp::as template.
defining the module was returned by the cxxfunction() (from the • The return type of the function must be either void or any
inline package) as callable R function fx from which we can extract type that can be managed by the Rcpp::wrap template.
the relevant pointer using getDynLib() (again from inline). • The function name itself has to be unique in the module. In
Throughout the rest of the examples in this document, we al- other words, no two functions with the same name but differ-
ways assume that the C++ code defining a module is used to create ent signatures are allowed. C++ allows overloading functions.
an object fx via a similar call to cxxfunction. As an alternative, This might be added in future versions of modules.
one can also use sourceCpp as described in Section 2.3.
2.1.1. Documentation for exposed functions using Rcpp modules. In
ad-
A module can contain any number of calls to function to reg-
dition to the name of the function and the function pointer, it is
ister many internal functions to R. For example, these 6 functions:
possible to pass a short description of the function as the third
std::string hello() { parameter of function.
return "hello";
using namespace Rcpp;
}
double norm(double x, double y) {
int bar( int x) {
return sqrt(x*x + y*y);
return x*2;
}
}
RCPP_MODULE(mod) {
double foo( int x, double y) {
function("norm", &norm,
return x * y;
"Provides a simple vector norm");
}
}
void bla( ) {
The description is used when displaying the function to the R
Rprintf("hello\\n");
prompt:
}
mod <- Module("mod", getDynLib(fx))
void bla1( int x) { show(mod$norm)
Rprintf("hello (x = %d)\\n", x);
} 2.1.2. Formal arguments specification. function also gives the pos-
sibility to specify the formal arguments of the R function that
void bla2( int x, double y) { encapsulates the C++ function, by passing a Rcpp::List after the
Rprintf("hello (x = %d, y = %5.2f)\\n", x, y); function pointer.
}
using namespace Rcpp;
can be exposed with the following minimal code:
double norm(double x, double y) {
RCPP_MODULE(yada) {
return sqrt(x*x + y*y);
using namespace Rcpp;
}
function("hello" , &hello);
RCPP_MODULE(mod_formals) {
function("bar" , &bar );
function("norm",
function("foo" , &foo );
&norm,
function("bla" , &bla );
List::create(_["x"] = 0.0,
function("bla1" , &bla1 );
_["y"] = 0.0),
function("bla2" , &bla2 );
"Provides a simple vector norm");
}
}
which can then be used from R:
A simple usage example is provided below:
yada <- Module("yada", getDynLib(fx))
yada$bar(2L) mod <- Module("mod_formals", getDynLib(fx))
yada$foo(2L, 10.0) norm <- mod$norm
yada$hello() norm()
yada$bla() norm(x = 2, y = 3)
yada$bla1(2L)
To set formal arguments without default values, omit the rhs.

Eddelbuettel and François Rcpp Vignette | January 11, 2022 | 3


using namespace Rcpp; }

double norm(double x, double y) { RCPP_MODULE(unif_module) {


return sqrt(x*x + y*y);
} class_<Uniform>("Uniform")

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;

4 | https://cran.r-project.org/package=Rcpp Eddelbuettel and François


using namespace Rcpp; Setters can be either a member function taking a T and returning
class Foo { void, such as set_z above, or a free function taking a pointer to
public: the target class and a T:
Foo(double x_, double y_, double z_):
void z_set(Foo* foo, double z) { foo->set_z(z); }
x(x_), y(y_), z(z_) {}
Using properties gives more flexibility in case field access has
double x;
to be tracked or has impact on other fields. For example, this class
double y;
keeps track of how many times the x field is read and written.
double get_z() { return z; } class Bar {
void set_z(double z_) { z = z_; } public:

private: Bar(double x_) : x(x_), nread(0), nwrite(0) {}


double z;
}; double get_x() {
nread++;
RCPP_MODULE(mod_foo) { return x;
class_<Foo>( "Foo" ) }

.constructor<double,double,double>() void set_x(double x_) {


nwrite++;
.field("x", &Foo::x) x = x_;
.field_readonly("y", &Foo::y) }

.property("z", &Foo::get_z, &Foo::set_z) IntegerVector stats() const {


; return
} IntegerVector::create(_["read"] = nread,
_["write"] = nwrite);
The .field method exposes a public field with read/write ac- }
cess from R. It accepts an extra parameter to give a short description
of the field: private:
double x;
.field("x", &Foo::x, "documentation for x")
int nread, nwrite;
};
The .field_readonly exposes a public field with read-only
access from R. It also accepts the description of the field. RCPP_MODULE(mod_bar) {
class_<Bar>( "Bar" )
.field_readonly("y", &Foo::y,
"documentation for y") .constructor<double>()

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: }

// with getter and setter Here is a simple usage example:


.property("z", &Foo::get_z,
&Foo::set_z, "Documentation for z") mod_bar <- Module("mod_bar", getDynLib(fx))
Bar <- mod_bar$Bar
// with only getter b <- new(Bar, 10)
.property("z", b$x + b$x
&Foo::get_z, "Documentation for z") b$stats()
b$x <- 10
The type of the field (T) is deduced from the return type of the b$stats()
getter, and if a setter is given its unique parameter should be of the
same type. 2.2.4. Exposing methods using Rcpp modules. class_ has several over-
Getters can be member functions taking no parameter and re- loaded and templated .method functions allowing the programmer
turning a T (for example get_z above), or a free function taking a to expose a method associated with the class.
pointer to the exposed class and returning a T, for example: A legitimate method to be exposed by .method can be:

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

Eddelbuettel and François Rcpp Vignette | January 11, 2022 | 5


Rcpp::wrap, and that takes between 0 and 65 parameters virtual std::string name() const {
whose types can be handled by Rcpp::as. return "Derived1";
• A free function that takes a pointer to the target class as its }
first parameter, followed by 0 or more (up to 65) parameters };
that can be handled by Rcpp::as and returning a type that
can be handled by Rcpp::wrap or void. // second derived class
class Derived2: public Base {
can also include a short doc-
2.2.5. Documenting methods. .method
public:
umentation of the method, after the method (or free function)
Derived2() : Base() {}
pointer.
virtual std::string name() const {
.method("stats", &Bar::stats, return "Derived2";
"vector indicating the number of " }
"times x has been read and written") };

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() {}

6 | https://cran.r-project.org/package=Rcpp Eddelbuettel and François


class_<World>("World") .constructor()
.method("handleFoo", &Bar::handleFoo);
// expose the default constructor }
.constructor()
Foo <- Module("Foo", getDynLib(fx))$Foo
.method("greet", &World::greet) Bar <- Module("Barl", getDynLib(fx))$Bar
.method("set", &World::set) foo <- new(Foo)
; bar <- new(Bar)
bar$handleFoo(foo)
} #> Got a Foo!
The show method for World objects is then implemented as:
2.2.12. Full example. The
following example illustrates how to use
yada <- Module("yada", getDynLib(fx)) Rcpp modules to expose the class std::vector<double> from the
setMethod("show", yada$World , function(object) { STL.
msg <- paste("World object with message : ", typedef std::vector<double> vec;
object$greet()) void vec_assign(vec* obj,
writeLines(msg) Rcpp::NumericVector data) {
} ) obj->assign(data.begin(), data.end());
yada$World$new() # implictly calls show }
void vec_insert(vec* obj, int position,
TODO: mention R inheritance (John ?) Rcpp::NumericVector data) {
vec::iterator it = obj->begin() + position;
2.2.11. Extending Rcpp::as and Rcpp::wrap. Sometimes it is neces-
obj->insert(it, data.begin(), data.end());
sary to extend Rcpp::as or Rcpp::wrap for classes that are
}
also exposed using Rcpp modules. Instead of using the general
Rcpp::NumericVector vec_asR( vec* obj ) {
methods described in the Rcpp Extending vignette, one can use
return Rcpp::wrap( *obj );
the RCPP_EXPOSED_AS or RCPP_EXPOSED_WRAP macros. Alterna-
}
tively the RCPP_EXPOSED_CLASS macro defines both Rcpp::as
void vec_set(vec* obj, int i, double value) {
and Rcpp::wrap specializations. Do not use these macros together
obj->at( i ) = value;
with the generic extension mechanisms. Note that opposed to
}
the generic methods, these macros can be used after Rcpp.h has
// Fix for C++11, where we cannot directly expose
been loaded. Here an example of a pair of Rcpp modules exposed
// member functions vec::resize and vec::push_back
classes where one of them has a method taking an instance of
void vec_resize (vec* obj, int n) {
the other class as argument. In this case it is sufficient to use
obj->resize(n);
RCPP_EXPOSED_AS to enable the transparent conversion from R to
}
C++:
void vec_push_back (vec* obj, double value) {
#include <Rcpp.h> obj->push_back(value);
}
class Foo {
public: RCPP_MODULE(mod_vec) {
Foo() = default; using namespace Rcpp;
};
// we expose class std::vector<double>
class Bar { // as "vec" on the R side
public: class_<vec>("vec")
Bar() = default;
void handleFoo(Foo foo) { // exposing constructors
Rcpp::Rcout << "Got a Foo!" << std::endl; .constructor()
}; .constructor<int>()
};
// exposing member functions
RCPP_EXPOSED_AS(Foo) .method("size", &vec::size)
.method("max_size", &vec::max_size)
RCPP_MODULE(Foo){ .method("capacity", &vec::capacity)
Rcpp::class_<Foo>("Foo") .method("empty", &vec::empty)
.constructor(); .method("reserve", &vec::reserve)
} .method("pop_back", &vec::pop_back)
.method("clear", &vec::clear)
RCPP_MODULE(Barl){
Rcpp::class_<Bar>("Bar") // exposing const member functions

Eddelbuettel and François Rcpp Vignette | January 11, 2022 | 7


.const_method("back", &vec::back) public:
.const_method("front", &vec::front) World() : msg("hello") {}
.const_method("at", &vec::at ) void set(std::string msg) { this->msg = msg; }
std::string greet() { return msg; }
// exposing free functions taking a
// std::vector<double>* as their first private:
// argument std::string msg;
.method("assign", &vec_assign) };
.method("insert", &vec_insert)
.method("resize", &vec_resize) RCPP_MODULE(yada){
.method("push_back", &vec_push_back) using namespace Rcpp;
.method("as.vector", &vec_asR)
function("hello" , &hello);
// special methods for indexing function("bla" , &bla);
.const_method("[[", &vec::at) function("bla2" , &bla2);
.method("[[<-", &vec_set)
; class_<World>("World")
} .constructor()
.method("greet", &World::greet)
mod_vec <- Module("mod_vec", getDynLib(fx)) .method("set", &World::set)
vec <- mod_vec$vec ;
v <- new(vec) }
v$reserve(50L)
v$assign(1:10) sourceCpp('yada.cpp')
v$push_back(10)
v$size() C++ functions hello, bla, bla2 and class World will be readily
v$resize(30L) available in R:
v$capacity()
v[[ 0L ]] hello()
v$as.vector() bla()
bla2(42, 0.42)
w <- new(World)
2.3. Loading modules via sourceCpp. As an alternative to the ex-
w$greet()
plicit creation of a Module object using the inline package via
w$set("hohoho")
cxxfunction and getDynLib, it is possible to use the sourceCpp
w$greet()
function, accepting C++ source code as either a .cpp file or a char-
acter string and described in the Rcpp attributes vignette (Allaire
et al., 2022). 3. Using modules in other packages
The main differences with this approach are:
3.1. Namespace import. When using Rcpp modules in a packages,
• The Rcpp.h header file must be explicitly included. the client package needs to import Rcpp’s namespace. This is
• The content of the module (C++ functions and classes) is im- achieved by adding the following line to the NAMESPACE file.
plicitly exposed and made available to R as individual objects,
as opposed to being accessed from a Module object with the import(Rcpp)
$ extractor.
In some case we have found that explicitly naming a symbol
Note that this is similar to exposing modules in R packages
can be preferable:
using loadModule, described in Section 3.2.1 below.
As an example, consider a file called yada.cpp containing the import(Rcpp, evalCpp)
following C++ code:
#include <Rcpp.h> 3.2. Load the module in the namespace.
std::string hello() {
return "hello"; 3.2.1. Load the module content via loadModule. Startingwith release
} 0.9.11, the preferred way for loading a module directly into a pack-
void bla() { age namespace is by calling the loadModule() function, which
Rprintf("hello\\n"); takes the module name as an argument and exposes the content
} of the module (C++ functions and classes) as individual objects in
void bla2( int x, double y) { the namespace. It can be placed in any .R file in the package. This
Rprintf("hello (x = %d, y = %5.2f)\\n", x, y); is useful as it allows to load the module from the same file as some
} auxiliary R functions using the module.
Consider a package testmod defining a module yada in the
class World { source file src/yada.cpp, with the same content as defined above
in Section 2.3 above

8 | https://cran.r-project.org/package=Rcpp Eddelbuettel and François


Then, loadModule is called in the package’s R code to expose 3.3. Namespace exports. The content of modules or the modules as
all C++ functions and classes as objects hello, bla, bla2, World a whole, exposed as objects in the package namespace, must be ex-
into the package namespace: ported to be visible to users of the package. As for any other object,
this is achieved by the appropriate export() or exportPattern()
loadModule("yada", TRUE) statements in the NAMESPACE file. For instance, the functions and
classes in the yada module considered above can be exported as:
Provided the objects are also exported (see Section 3.3 below),
this makes them readily available in R: export(hello, bla, bla2, World)

library(testmod) 3.4. Support for modules in skeleton generator. Creating a


hello() new package using Rcpp modules is easiest via the call to
bla() Rcpp.package.skeleton() with argument module=TRUE.
bla2(42, 0.42)
w <- new(World) Rcpp.package.skeleton("testmod", module = TRUE)
w$greet()
w$set("hohoho") This will install code providing three example modules, exposed
w$greet() using LoadModule.

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.

3.2.2. Deprecated legacy method using loadRcppModules. Prior


to re- yada <- Module("yada")
lease 0.9.11, where loadModule was introduced, loading all func- prompt(yada, "yada-module.Rd")
tions and classes from a module into a package namespace was
achieved using the loadRcppModules function within the .onLoad We strongly recommend using a package when working with
body. Modules. But in case a manually compiled shared library has to
loaded, the return argument of the getDynLib() function can be
.onLoad <- function(libname, pkgname) {
supplied as the PACKAGE argument to the Module() function as
loadRcppModules()
well.
}

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:

RcppModules: yada, stdVector, NumEx 5. Known shortcomings


There are some things Rcpp modules is not good at:
The loadRcppModules function has a single argument direct
with a default value of TRUE. With this default value, all content • serialization and deserialization of objects: modules are im-
from the module is exposed directly in the package namespace. If plemented via an external pointer using a memory loca-
set to FALSE, all content is exposed as components of the module. tion, which is non-constant and varies between session. Ob-
Note: This approach is deprecated as of Rcpp 0.12.5, and jects have to be re-created, which is different from the (de-
now triggers a warning message. Eventually this function will be )serialization that R offers. So these objects cannot be saved
withdrawn. from session to session.
• multiple inheritance: currently, only simple class structures
3.2.3. Just expose the module. Alternatively to exposing a module’s
are representable via Rcpp modules.
content via loadModule, it is possible to just expose the module
object to the users of the package, and let them extract the functions
and classes as needed. This uses lazy loading so that the module is 6. Summary
only loaded the first time the user attempts to extract a function or This note introduced Rcpp modules and illustrated how to expose
a class with the dollar extractor. C++ function and classes more easily to R. We hope that R and C++
yada <- Module( "yada" ) programmers find Rcpp modules useful.

.onLoad <- function(libname, pkgname) { References


# placeholder
Abrahams D, Grosse-Kunstleve RW (2003). Building Hybrid Systems with
} Boost.Python. Boost Consulting. URL https://www.boostpro.com/writing/
bpl.pdf.
Provided yada is properly exported, the functions and classes Allaire JJ, Eddelbuettel D, François R (2022). Rcpp Attributes. Vignette included
are accessed as e.g. yada$hello, yada$World. in R package Rcpp, URL https://CRAN.R-Project.org/package=Rcpp.

Eddelbuettel and François Rcpp Vignette | January 11, 2022 | 9


Eddelbuettel D, François R (2011). “Rcpp: Seamless R and C++
Integration.” Journal of Statistical Software, 40(8), 1–18. doi:
10.18637/jss.v040.i08. URL https://doi.org/10.18637/jss.v040.i08.
Eddelbuettel D, François R, Allaire J, Ushey K, Kou Q, Russel N, Chambers J,
Bates D (2022). Rcpp: Seamless R and C++ Integration. R package version
1.0.8, URL https://CRAN.R-Project.org/package=Rcpp.
R Core Team (2021). Writing R extensions. R Foundation for Statistical Comput-
ing, Vienna, Austria. URL https://CRAN.R-Project.org/doc/manuals/R-exts.
html.

10 | https://cran.r-project.org/package=Rcpp Eddelbuettel and François

You might also like