Skip to contents

The reference for this vignette is Wickham et al. (2024).

Loading the Required R Packages

This vignette and the next use the following R packages:

Creating a Dummy Package

You can create a new package in RStudio (or VSCode) by running:

pkg_template("~/github/cpp4r/cpp4rexamples")

This command will create a new folder containing the cpp4rexamples package. Afterward, you can run use_cpp4r() to add the necessary code to the package.

The pkg_template() function automatically creates ./cpp4rexamples/R/cpp4rexamples-package.R with the following content:

#' @useDynLib cpp4rexamples, .registration = TRUE
#' @keywords internal
"_PACKAGE"

It also creates ./cpp4rexamples/src/main.cpp. The rest of src/ aims to provide an organization to keep the code organized into different files according to their purpose.

Give a look at ./cpp4rexamples/src/01_plus_one.h, which contains the following C++ code:

/* roxygen
@title Plus 1 (C++)
@param x integer
@description It adds 1 to an integer value.
@export
@examples plus_one(1)
*/
[[cpp4r::register]]
int plus_one(int x) {
  return x + 1;
}

This code is called by main.cpp and it makes sense to keep this organization when the codebase grows, otherwise you can end up with a single large cpp file containing thousands of lines of code that will be harder to maintain. With R packages, there is no need to create a main() function in C++, which is the entry point of a standalone C++ program.

Unlike cpp11, cpp4r can document the C++ functions using roxygen2 comments. The comments must be placed before the [[cpp4r::register]] attribute.

With cpp11, you would need to write a separate wrapper, like this:

[[cpp4r::register]]
int plus_one_(int x) {
  return x + 1;
}
#' @title Plus 1 (C++)
#' @param x integer
#' @description It adds 1 to an integer value.
#' @export
#' @examples plus_one(1)
plus_one <- function(x) {
  plus_one_(x)
}

The R version of the previous function is as follows:

#' Plus one (R)
#' @param x integer
#' @description It adds 1 to an integer value.
#' @export
#' @examples plus_one_r(1)
plus_one_r <- function(x) {
  x + 1
}

To use the C++ function, you can register and document it by running:

cpp4r::register()
devtools::document()
devtools::load_all()

Which should print:

> plus_one(1)
[1] 2

For development and live testing, you should use the load_all():

Try plus_two(1) and plus_two(1.0). Why does 1 and 1.0 matter in C++?

Number’s Sign

A more complex function is one that returns the sign of a number:

[[cpp4r::register]] int sign_cpp(double x) {
  if (x > 0) {
    return 1;
  } else if (x == 0) {
    return 0;
  } else {
    return -1;
  }
}

Add this function to a new file src/03_sign.h.

Here is the R version of the function:

sign_r <- function(x) {
  if (x > 0) {
    1
  } else if (x == 0) {
    0
  } else {
    -1
  }
}

Do not forget to include the new header file in main.cpp.

Document both functions and compare their outputs.

Installing the Package

To document and install the package as an R library, you can run the following functions:

cpp4r::register()
devtools::document()
devtools::install()

Afterward, you can access the functions by loading the package with library(cpp4rexamples).

Each time you need to make changes to the C++ code, you can run load_all() again to test and then reinstall the package.

Good Practice

It is good practice to include a license for your code. For example, you can use the Apache license by running:

Additionally, it is recommended to use use_build_ignore() to ignore files that are unnecessary for package installation. For example, to ignore the docs folder, you can run:

usethis::use_build_ignore("docs")

Your .Rbuildignore file can include the following lines, which you can edit manually to exclude specific files and directories:

^\.vscode$
^LICENSE\.md$
^cpp4rexamples\.Rproj$

References

Wickham, Hadley, Jenny Bryan, Malcolm Barrett, and Andy Teucher. 2024. “Automate Package and Project Setup.” https://usethis.r-lib.org/.