The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.
The reference for this vignette is Wickham 2024.
This vignette and the next use the following R packages:
library(cpp4r)
library(tinydev)
You can create a new package in RStudio (or VSCode) by running:
pkg_template("~/mypkg")
This command will create a new folder containing the mypkg package.
The pkg_template() function automatically creates ./mypkg/R/mypkg-package.R with the following content:
#' @title Package Title
#' @description Some description. CRAN asks for at least one paragraph with two full sentences. Like this.
#' @useDynLib mypkg, .registration = TRUE
"_PACKAGE"
It also creates ./mypkg/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 ./mypkg/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 Roxygen comments. The comments must be placed before the
[[cpp4r::register]] attribute.
Decorating a function with [[cpp4r::register]] only makes it callable via .Call() from R; it does not export or
document it. For example, this alone leaves sum undocumented and unexported:
[[cpp4r::register]] int sum(int a, int b) { return a + b; }
There are two ways to export a registered function:
/* roxygen ... */ comment block, including an @export tag, directly before the decorated function in the
C++ file, as shown above for plus_one(). register() copies it verbatim onto the generated R wrapper, so
running tinydev::pkg_document() afterwards will export the wrapper and document it as usual.C++ function a name you don’t want exposed directly (e.g. sum_), then write your own R wrapper that
calls it and add a normal #' Roxygen comment block with @export above that wrapper instead.Either way, register() and [[cpp4r::register]] alone never modify NAMESPACE; running tinydev::pkg_document(".")
(or hand-editing NAMESPACE) is what actually exports the function.
With cpp11, you would need to write a separate wrapper, like this:
[[cpp11::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)
}
With cpp4r you can type:
/* 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;
}
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()
tinydev::pkg_document(".")
tinydev::pkg_load(".")
Which should print:
> plus_one(1)
[1] 2
Try plus_two(1) and plus_two(1.0). Why does 1 and 1.0 matter in C++?
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.
To document and install the package as an R library, you can run the following functions:
cpp4r::register()
tinydev::pkg_document(".")
tinydev::pkg_install(".")
Afterward, you can access the functions by loading the package with library(mypkg).
Each time you need to make changes to the C++ code, you can run load_all() again to test and then reinstall the package.
It is good practice to include a license for your code. For example, you can use the Apache license by running:
tinydev::license_apache(".")
You also need to ignore files that are unnecessary for package installation. For example, to ignore the docs folder, add
^docs$ to .Rbuildignore. Your .Rbuildignore file can include the following lines, which you can edit manually to exclude
specific files and directories:
^\.vscode$
^LICENSE\.txt$
`^docs$`
Wickham H, Bryan J, Barrett M, Teucher A (2024). “Automate Package and Project Setup.” https://usethis.r-lib.org/.
These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.