The reference for this vignette is Wickham et al. (2024).
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.
Print a Number
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:
#' @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:
Which should print:
For development and live testing, you should use the
load_all()
:
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:
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:
usethis::use_apache_license()
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$