RCPP Attributes
RCPP Attributes
Rcpp attributes provide a high-level syntax for declaring C++ functions #include <Rcpp.h>
as callable from R and automatically generating the code required to in- using namespace Rcpp;
voke them. Attributes are intended to facilitate both interactive use of C++
within R sessions as well as to support R package development. The im- // [[Rcpp::export]]
plementation of attributes is based on previous work in the inline package NumericVector convolveCpp(NumericVector a,
(Sklyar et al., 2020). NumericVector b) {
Rcpp | attributes | R | C++
int na = [Link](), nb = [Link]();
int nab = na + nb - 1;
Attributes are a new feature of Rcpp version 0.10.0 (Eddel-
NumericVector xab(nab);
buettel et al., 2021; Eddelbuettel and François, 2011) that provide
infrastructure for seamless language bindings between R and C++.
for (int i = 0; i < na; i++)
The motivation for attributes is several-fold:
for (int j = 0; j < nb; j++)
1. Reduce the learning curve associated with using C++ and R xab[i + j] += a[i] * b[j];
together
2. Eliminate boilerplate conversion and marshaling code wher- return xab;
ever possible }
3. Seamless use of C++ within interactive R sessions
4. Unified syntax for interactive work and package development The addition of the export attribute allows us to do this from
the R prompt:
The core concept is to add annotations to C++ source files that
sourceCpp("[Link]")
provide the context required to automatically generate R bindings
convolveCpp(x, y)
to C++ functions. Attributes and their supporting functions include:
• Rcpp::export attribute to export a C++ function to R We can now write C++ functions using built-in C++ types and
• sourceCpp function to source exported functions from a file Rcpp wrapper types and then source them just as we would an R
• cppFunction and evalCpp functions for inline declarations script.
and execution The sourceCpp function performs caching based on the last
• Rcpp::depends attribute for specifying additional build de- modified date of the source file and it’s local dependencies so as
pendencies for sourceCpp long as the source does not change the compilation will occur only
once per R session.
Attributes can also be used for package development via the
compileAttributes function, which automatically generates 1.2. Specifying Argument Defaults. If default argument values are
extern "C" and .Call wrappers for C++ functions within pack- provided in the C++ function definition then these defaults are
ages. also used for the exported R function. For example, the following
C++ function:
1. Using Attributes DataFrame readData(CharacterVector file,
Attributes are annotations that are added to C++ source files to CharacterVector colNames =
provide additional information to the compiler. Rcpp supports CharacterVector::create(),
attributes to indicate that C++ functions should be made available std::string comment = "#",
as R functions, as well as to optionally specify additional build bool header = true)
dependencies for source files.
C++11 specifies a standard syntax for attributes (Maurer and Will be exported to R as:
Wong, 2008). Since this standard isn’t yet fully supported across
function(file, colNames=character(),
all compilers, Rcpp attributes are included in source files using
comment="#", header=TRUE)
specially formatted comments.
Note that C++ rules for default arguments still apply: they
1.1. Exporting C++ Functions. The sourceCpp function parses a
must occur consecutively at the end of the function signature and
C++ file and looks for functions marked with the Rcpp::export
(unlike R) can’t rely on the values of other arguments.
attribute. A shared library is then built and its exported functions
Not all C++ default argument values can be parsed into their
are made available as R functions in the specified environment.
R equivalents, however the most common cases are supported,
For example, this source file contains an implementation of con-
including:
volve (note the Rcpp::export attribute in the comment above the
function): • String literals delimited by quotes (e.g. "foo")
1.3. Signaling Errors. Within R code the stop function is typi- /*** R
cally used to signal errors. Within R extensions written in C the
Rf_error function is typically used. However, within C++ code # Call the fibonacci function defined in C++
you cannot safely use Rf_error because it results in a longjmp fibonacci(10)
over any C++ destructors on the stack.
The correct way to signal errors within C++ functions is to throw */
an Rcpp::exception. For example:
Multiple R code chunks can be included in a C++ file. The
if (unexpectedCondition)
throw Rcpp::exception("Unexpected " sourceCpp function will first compile the C++ code into a shared
library and then source the embedded R code.
"condition occurred");
There is also an Rcpp::stop function that is shorthand for 1.6. Modifying Function Names. You can change the name of an
throwing an Rcpp::exception. For example: exported function as it appears to R by adding a name parameter
to Rcpp::export. For example:
if (unexpectedCondition)
Rcpp::stop("Unexpected condition occurred"); // [[Rcpp::export(name = ".convolveCpp")]]
NumericVector convolveCpp(NumericVector a,
In both cases the C++ exception will be caught by Rcpp prior NumericVector b)
to returning control to R and converted into the correct signal to R
that execution should stop with the specified message. Note that in this case since the specified name is prefaced by
You can similarly also signal warnings with the Rcpp::warning a . the exported R function will be hidden. You can also use this
function: method to provide implementations of S3 methods (which wouldn’t
otherwise be possible because C++ functions can’t contain a ‘.’ in
if (unexpectedCondition)
their name).
Rcpp::warning("Unexpected condition occurred");
A good guideline is to call Rcpp::checkUserInterrupt every • Be defined in the global namespace (i.e. not within a C++
1 or 2 seconds that your computation is running. In the above code, namespace declaration)
if the user requests an interrupt then an exception is thrown and • Have a return type that is either void or compatible with
the attributes wrapper code arranges for the user to be returned to Rcpp::wrap and parameter types that are compatible with
the REPL. Rcpp::as (see sections 3.1 and 3.2 of the ‘Rcpp-jss-2011’
Note that R provides a C API for the same purpose vignette for more details).
(R_CheckUserInterrupt) however this API is not safe to use in • Use fully qualified type names for the return value and all pa-
C++ code as it uses longjmp to exit the current scope, bypassing any rameters. Rcpp types may however appear without a names-
C++ destructors on the stack. The Rcpp::checkUserInterrupt pace qualifier (i.e. DataFrame is okay as a type name but
function is provided as a safe alternative for C++ code. std::string must be specified fully).
int n = [Link](), k = [Link](); Note the use of the #ifndef include guard, this is important to
ensure that code is not included more than once in a source file.
arma::mat X([Link](), n, k, false); You should use an include guard and be sure to pick a unique name
arma::colvec y([Link](), [Link](), false); for the corresponding #define.
Also note the use of the inline keyword preceding the function.
arma::colvec coef = arma::solve(X, y); This is important to ensure that there are not multiple definitions
arma::colvec rd = y - X*coef; of functions included from header files. Classes fully defined in
header files automatically have inline semantics so don’t require
double sig2 = this treatment.
arma::as_scalar(arma::trans(rd)*rd/(n-k)); To use this code in a source file you’d just include it based on
arma::colvec sderr = arma::sqrt(sig2 * it’s relative path (being sure to use " as the delimiter to indicate a
arma::diagvec(arma::inv(arma::trans(X)*X))); local file reference). For example:
#ifndef __UTILITIES__ 1. Your code can be made available to users without C++ devel-
#define __UTILITIES__ opment tools (at least on Windows or Mac OS X where binary
packages are common)
double timesTwo(double x); 2. Multiple source files and their dependencies are handled au-
tomatically by the R package build system
#endif // __UTILITIES__ 3. Packages provide additional infrastructure for testing, docu-
mentation and consistency
Then actually defining the function in a separate source file with
the same base name as the header file but with a .cpp extension 2.1. Package Creation. To create a package that is based on Rcpp
(in the above example this would be [Link]): you should follow the guidelines in the ‘Rcpp-package’ vignette.
For a new package this is most conveniently done using the
#include "[Link]" [Link] function.
To generate a new package with a simple hello, world function
double timesTwo(double x) { that uses attributes you can do the following:
return x * 2;
} [Link]("NewPackage",
attributes = TRUE)
It’s also possible to use attributes to declare dependencies and
exported functions within shared header and source files. This To generate a package based on C++ files that you’ve been using
enables you to take a source file that is typically used standalone with sourceCpp you can use the cpp_files parameter:
and include it when compiling another source file.
[Link]("NewPackage",
Note that since additional source files are processed as separate
example_code = FALSE,
translation units the total compilation time will increase propor-
cpp_files = c("[Link]"))
tional to the number of files processed. From this standpoint it’s
often preferable to use shared header files with definitions fully
inlined as demonstrated above. 2.2. Specifying Dependencies. Once you’ve migrated C++ code
Note also that embedded R code is only executed for the main into a package, the dependencies for source files are derived from
source file not those referenced by local includes. the Imports and LinkingTo fields in the package DESCRIPTION
file rather than the Rcpp::depends attribute. Some packages also
1.12. Including C++ Inline. Maintaining C++ code in it’s own require the addition of an entry to the package NAMESPACE file to
source file provides several benefits including the ability to use ensure that the package’s shared library is loaded prior to callers
C++ aware text-editing tools and straightforward mapping of com- using the package. For every package you import C++ code from
pilation errors to lines in the source file. However, it’s also possible (including Rcpp) you need to add these entries.
to do inline declaration and execution of C++ code. Packages that provide only C++ header files (and no shared
There are several ways to accomplish this, including passing a library) need only be referred to using LinkingTo. You should
code string to sourceCpp or using the shorter-form cppFunction consult the documentation for the package you are using for the
or evalCpp functions. For example: requirements particular to that package.
For example, if your package depends on Rcpp you’d have the
cppFunction(' following entries in the DESCRIPTION file:
int fibonacci(const int x) {
if (x < 2) Imports: Rcpp (>= 0.11.4)
return x; LinkingTo: Rcpp
else
return (fibonacci(x-1)) + fibonacci(x-2); And the following entry in your NAMESPACE file:
}
') importFrom(Rcpp, evalCpp)
Results in the generation of the following two source files: There is one other mechanism for type visibility in
[Link]. If your package provides a master include
• src/[Link] – The extern "C" wrappers re- file for consumption by C++ clients then this file will also be au-
quired to call exported C++ functions within the package. tomatically included. For example, if the fastcode package had a
• R/RcppExports.R – The .Call wrappers required to call the C++ API and the following header file:
extern "C" functions defined in [Link].
inst/include/fastcode.h
You should re-run compileAttributes whenever functions are
added, removed, or have their signatures changed. Note that if you This header file will also automatically be included in
are using either RStudio or devtools to build your package then [Link]. Note that the convention of using .h for
the compileAttributes function is called automatically whenever header files containing C++ code may seem unnatural, but this
your package is built. comes from the recommended practices described in ‘Writing R
The compileAttributes function deals only with exporting Extensions’ (R Core Team, 2018).
C++ functions to R. If you want the functions to additionally be
publicly available from your package’s namespace another step may 2.6. Roxygen Comments. The roxygen2 package (Wickham et al.,
be required. Specifically, if your package NAMESPACE file does not 2018) provides a facility for automatically generating R documen-
use a pattern to export functions then you should add an explicit tation files based on specially formatted comments in R source
entry to NAMESPACE for each R function you want publicly available. code.
If you include roxygen comments in your C++ source file with
2.4. Package Init Functions. Rcpp attribute compilation will au- a //' prefix then compileAttributes will transpose them into
tomatically generate a package R_init function that does native R roxygen comments within R/RcppExports.R. For example the
routine registration as described here: [Link] following code in a C++ source file:
manuals/r-release/[Link]#Registering-native-routines.
You may however want to add additional C++ code to the //' The length of a string (in characters).
package initialization sequence. To do this, you can add the //'
[[Rcpp::init]] attribute to functions within your package. For //' @param str input character vector
example: //' @return characters in each element of the vector
// [[Rcpp::export]]
// [[Rcpp::init]] NumericVector strLength(CharacterVector str)
void my_package_init(DllInfo *dll) {
// initialization code here Results in the following code in the generated R source file:
}
#' The length of a string (in characters).
In this case, a call to my_package_init() will be added to the #'
end of the automatically generated R_init function within RcppEx- #' @param str input character vector
[Link]. For example: #' @return characters in each element of the vector
strLength <- function(str)
void my_package_init(DllInfo *dll);
RcppExport void R_init_pkgname(DllInfo *dll) {
R_registerRoutines(dll, NULL, CallEntries, NULL, NULL); 2.7. Providing a C++ Interface. The interface exposed from R pack-
R_useDynamicSymbols(dll, FALSE); ages is most typically a set of R functions. However, the R package
my_package_init(dll); system also provides a mechanism to allow the exporting of C and
} C++ interfaces using package header files. This is based on the
R_RegisterCCallable and R_GetCCallable functions described
in ‘Writing R Extensions’ (R Core Team, 2018).
2.5. Types in Generated Code. In some cases the signatures of the C++ interfaces to a package are published within the top level
C++ functions that are generated within [Link] may include directory of the package (which within the package source
have additional type requirements beyond the core standard library directory is located at inst/include). The R build system auto-
and Rcpp types (e.g. CharacterVector, NumericVector, etc.). matically adds the required include directories for all packages
Examples might include convenience typedefs, as/wrap handlers specified in the LinkingTo field of the package DESCRIPTION file.
for marshaling between custom types and SEXP, or types wrapped
by the Rcpp XPtr template. 2.7.1. Interfaces Attribute. The Rcpp::interfaces attribute can be
In this case, you can create a header file that contains these type used to automatically generate a header-only interface to your C++
definitions (either defined inline or by including other headers) and functions within the include directory of your package.
have this header file automatically included in [Link]. The Rcpp::interfaces attribute is specified on a per-source
Headers named with the convention pkgname_types are automat- file basis, and indicates which interfaces (R, C++, or both) should
ically included along with the generated C++ code. For example, if be provided for exported functions within the file.
your package is named fastcode then any of the following header For example, the following specifies that both R and C++ inter-
files would be automatically included in [Link]: faces should be generated for a source file:
// [[Rcpp::depends(MyPackage)]]
#include <MyPackage.h>
void foo() {
MyPackage::bar();
}
PKG_CPPFLAGS += -I../inst/include/