Skip to contents

Selecting a C++ Standard for a Package

The r11 R package provides an example of choosing the C++ standard that a package is compiled against. The standard is selected through the CXX_STD field of a Makevars file, so the package builds against a known language version regardless of the compiler’s default.

Usage

To install the package, you must first have a compiler on your system that is compatible with R. For help on obtaining a compiler consult either macOS or Windows guides.

With a compiler in hand, one can then install the package from GitHub by:

# install.packages("remotes")
remotes::install_github("coatless-rd-rcpp/rcpp-cpp11-usage")
library("r11")

Implementation Details

Every C++ compiler has a default language standard, and R pins one for the packages it builds. That default has risen over time. When this example was first written it was C++98, and opting in to C++11 was the interesting step. Modern R requires at least C++11, made C++17 the default in R 4.3, and has since moved the default to C++20. The mechanism for choosing a standard has stayed the same throughout: set CXX_STD in Makevars. This package pins C++17 and uses a function from the C++ standard library’s <random> facilities, which have been available since C++11.

.
├── DESCRIPTION                         # Package metadata
├── NAMESPACE                           # Function and dependency registration
├── R                                   # R functions
   ├── RcppExports.R                   # Autogenerated R to C++ bindings by Rcpp
   └── r11-package.r                   # Package documentation
├── README.md
├── man                                 # Package documentation
   ├── r11-package.Rd
   └── runif_cpp.Rd
├── r11.Rproj
└── src                                 # Compiled code
    ├── Makevars                        # Selects the C++ standard via CXX_STD
    ├── Makevars.win
    ├── RcppExports.cpp                 # Autogenerated R bindings
    └── runif_cpp.cpp                   # C++ function using the <random> library

The C++ Function

The function in src/runif_cpp.cpp draws n values from a uniform distribution using std::mt19937_64 and std::uniform_real_distribution, both from the <random> header. These were introduced in C++11, so the package must be compiled against at least that standard.

#include <Rcpp.h>
#include <random>

// [[Rcpp::export]]
Rcpp::NumericVector runif_cpp(unsigned int n, double start = 0.0, double end = 1.0,
                              unsigned int seed = 183) {
  Rcpp::NumericVector u(n);

  std::mt19937_64 engine(seed);                          // Mersenne Twister engine
  std::uniform_real_distribution<double> u_dist(start, end);

  for (unsigned int i = 0; i < n; i++) {
    u[i] = u_dist(engine);
  }

  return u;
}

The exported function also carries //' roxygen comments (trimmed from the excerpt here) that Rcpp::compileAttributes() copies into R/RcppExports.R to generate the help page.

Selecting the Standard with Makevars

src/Makevars, and src/Makevars.win on Windows, set the CXX_STD variable that R reads when building the package. Naming a standard such as CXX11, CXX14, CXX17, or CXX20 causes R to pass the matching flag to the compiler.

# Select the C++ standard the package compiles against.
CXX_STD = CXX17

Pinning a specific standard with CXX_STD makes the package build the same way regardless of the host R’s default. On current R, whose default is C++20, CXX_STD = CXX17 selects C++17 explicitly, and on older R it guarantees at least that standard. Raising it to CXX20 or CXX23 is how you would request a newer standard.

DESCRIPTION

A package can also request a standard through the SystemRequirements field of DESCRIPTION, for example SystemRequirements: C++17. That field-based approach is an alternative to the Makevars CXX_STD shown above, and this package uses the Makevars approach. Surfacing the C++ code with Rcpp requires Rcpp under both LinkingTo (for the headers used at compile time) and Imports (so it is available at run time).

LinkingTo:
    Rcpp
Imports:
    Rcpp (>= 1.0.12)

License

GPL (>= 2)