R Packages
June 21, 2023
This session assumes familiarity with the C++ and Rcpp session of the Advanced R workshop.
Create a new package:
Make your package a Git repo:
Link to GitHub:
Commit your changes to git with a message “use GitHub”.
Use Rcpp and create an initial .cpp file:
/src directory for our C++ filessrc/.gitignore to ignore compiled filesadd_cpp.cpp ready for editing.It also copies some code to the clipboard for us to add to R/<packagename>-package.R which we must create.
<packagename>-package.RThen copy the code from the clipboard into this file:
## usethis namespace: start
#' @useDynLib pkgrcpp, .registration = TRUE
#' @importFrom Rcpp sourceCpp
## usethis namespace: end
NULL
The roxygen2 comments direct to
Rcpp::sourceCppuseDynLib(pkgrcpp,".registration=TRUE") to the NAMESPACE, so that the compiled code can be loaded and all C++ routines will be registered.add_cpp.cpp should already have the following code:
Edit add_cpp.cpp to add a C++ function and the Rcpp export comment, e.g.
Build > Document or Ctrl/Cmd + Shift + D now runs two functions:
Rcpp::compileAttributes() looks for // [[Rcpp::export]] and generates
src/RcppExports.cpp: C routines wrapping exported C++ functionsR/RcppExports.R: R wrappers that call the C routines.devtools::document() that converts roxygen comments as usual, in particular updating the NAMESPACE.
importFrom(Rcpp,sourceCpp)
useDynLib(pkgrcpp, .registration = TRUE)The R wrapper generated by Rcpp is as follows, in R/RcppExports.R:
Currently this is an internal R function - we have not taken any action to add it as an export in the NAMESPACE.
devtools::load_all(), Ctrl/Cmd + Shift + L, makes both internal and exported functions available for testing (without using ::: or ::).
.cpp file in the /src directory (using use_rcpp or File > New File). Save the file as sumC.cpp. Add the C++ functiondevtools::load_all() and try out the new function.The development workflow is the same as for R:
devtools::load_all() will detect changes in the C++ code and
src/RcppExports.cpp and R/RcppExports.RWe only need to document the R wrapper if we plan to export it.
The R wrapper is generated by Rcpp::compileAttributes(), so we cannot add roxygen comments to export or document the function there.
Instead, we add comments above our C++ function, using \\'instead of #'.
In add_cpp.cpp
add_cpp and add it to add_cpp.cpp.add_cpp as a guide, add documentation to sumC.cpp.As we have seen, Rcpp::compileAttributes() creates an R interface to our exported C++ functions.
To make a C++ interface for code in a .cpp file, we should add a custom Rcpp::interfaces() attribute as follows:
We can specify r and/or cpp to ask for an R and/or C++ interface.
Rcpp::compileAttributes() generates the following header files in /inst/include:
<packagename>_RcppExports.h: inline definitions for all exported C++ functions.<packagename>.h: includes the <packagename>_RcppExports.h. Package authors may add additional C++ code here for sharing.All directories in inst are moved to the top level of the package directory on installation.
Rcpp::interfaces() attribute to add_cpp.cpp to request both R and C++ interfaces for the add_cpp function.To use C++ code from another package that has created a C++ interface for their Rcpp functions, in DESCRIPTION add
LinkingTo: otherpkg
e.g. with usethis::use_package(otherpkg, type = "LinkingTo"). Then in .cpp file, add the header file to includes
Then either using namespace otherpkg; to make the functions from otherpackage available globally, or otherpkg::fn() to access functions directly.
In the Advanced R workshop (final exercise of the Rcpp session), we wrote an Rcpp function to approximate \(\pi\):
We can try using the dqrunif C++ functions provided by the dqrng package instead of Rcpp::runif.
See https://daqana.github.io/dqrng/index.html for more info on this package.
usethis::use_package() to add dqrng to LinkingTo. It will show possible headers to include, including dqrng.h which provides the Rcpp exports..cpp file and add the approx_pi function. Make sure to add the necessary headers and Rcpp attribute, so that you can use it after running devtools::load_all().approx_pi function that uses the dqrunif C++ functions from dqrng instead.bench::mark() to benchmark the two versions with N = 1e7, setting check = FALSE in bench::mark() as the generated random numbers will differ.Some packages provide a custom <packagename>.h or other C++ headers.
<packagename>.h!LinkingTo.The documentation should clarify what is needed for a particular package.
The BH package provides header files for the Boost C++ libraries.
The header files have .hpp extension and are nested in directories. The namespace is similarly nested:
Include BH in LinkingTo: to add this function to a package.
The RcppArmadillo package provides headers for the Armadillo library for linear algebra & scientific computing.
The header to include is RcppArmadillo.h, which provides the armadillo header defining the arma namespace, e.g.
To use RcppArmadillo in a package, we need to
RcppArmadillo to LinkingTosrc/Makevars and src/Makevars.win.Both can be done with usethis::use_rcpp_armadillo().
The first time you call it, you may be asked whether to install RcppArmadillo. Choose the affirmative option.
use_rcpp_armadillo()As well as doing the general setup to allow a package to use RcppArmadillo, it also creates a src directory and a .cpp file to edit.
If you already have a .cpp file in a src directory, by default it may try to overwrite it. If it does, select one of the negative options.
You need to specify the name of a new .cpp as an argument instead, e.g. use_rcpp_armadillo("new_file")
The function adds to the top of the .cpp file
#include <Rcpp.h>
using namespace Rcpp;
#include <RcppArmadillo.h>
// [[Rcpp::depends(RcppArmadillo)]]If you have an Apple Silicon Mac, you will also need to do the following:
open your global Makevars by calling usethis::edit_r_makevars()
Add the following code:
FLIBS= -L/opt/R/arm64/gfortran/libSession > Restart R (or Cmd + Shift + 0)
Run usethis::use_rcpp_armadillo("inner_prod") to set the package up to use RcppArmadillo and create a new .cpp file.
In inner_prod.cpp, edit the #include statement for RcppArmadillo and add the code
devtools::load_all() to load the function and try it out.Licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License (CC BY-NC-SA 4.0).