--- title: "03 - Package Skeleton" output: litedown::html_format: options: toc: true number_sections: true vignette: > %\VignetteIndexEntry{03 - Package Skeleton} %\VignetteEngine{litedown::vignette} %\VignetteEncoding{UTF-8} editor: markdown: wrap: sentence bibliography: ["references.bib"] --- The reference for this vignette is @usethis24. ## Loading the Required R Packages This vignette and the next use the following R packages: ```r library(cpp4r) library(tinydev) ``` ## Creating a Dummy Package You can create a new package in RStudio (or VSCode) by running: ```r pkg_template("~/mypkg") ``` This command will create a new folder containing the `mypkg` package. ## Print a Number The `pkg_template()` function automatically creates `./mypkg/R/mypkg-package.R` with the following content: ```r #' @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: ```cpp /* 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. ## Exporting a Registered Function 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: ```cpp [[cpp4r::register]] int sum(int a, int b) { return a + b; } ``` There are two ways to export a registered function: - Add a `/* 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. - Give the `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: ```cpp [[cpp11::register]] int plus_one_(int x) { return x + 1; } ``` ```r #' @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: ```cpp /* 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: ```r #' 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: ```r cpp4r::register() tinydev::pkg_document(".") tinydev::pkg_load(".") ``` Which should print: ```r > plus_one(1) [1] 2 ``` 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: ```cpp [[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: ```r 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: ```r 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. ## Good Practice It is good practice to include a license for your code. For example, you can use the Apache license by running: ```r 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$` ``` ## References