1---
2title: "Converting from Rcpp"
3output: rmarkdown::html_vignette
4vignette: >
5  %\VignetteIndexEntry{Converting from Rcpp}
6  %\VignetteEngine{knitr::rmarkdown}
7  %\VignetteEncoding{UTF-8}
8---
9
10```{r, include = FALSE}
11knitr::opts_chunk$set(
12  collapse = TRUE,
13  comment = "#>"
14)
15
16should_run_benchmarks <- function(x) {
17  get("requireNamespace")("cpp11test", quietly = TRUE) && asNamespace("cpp11test")$should_run_benchmarks()
18}
19```
20
21In many cases there is no need to convert a package from Rcpp.
22If the code is already written and you don't have a very compelling need to use cpp11 I would recommend you continue to use Rcpp.
23However if you _do_ feel like your project will benefit from using cpp11 this vignette will provide some guidance and doing the conversion.
24
25It is also a place to highlight some of the largest differences between Rcpp and cpp11.
26
27## Class comparison table
28
29| Rcpp                    | cpp11 (read-only)     | cpp11 (writable)   | cpp11 header                 |
30| ---                     | ---                   | ---                | ---                          |
31| NumericVector           | doubles               | writable::doubles  | <cpp11/doubles.hpp>          |
32| IntegerVector           | integers              | writable::integers | <cpp11/integers.hpp>         |
33| CharacterVector         | strings               | writable::strings  | <cpp11/strings.hpp>          |
34| RawVector               | raws                  | writable::raws     | <cpp11/raws.hpp>             |
35| List                    | list                  | writable::list     | <cpp11/list.hpp>             |
36| RObject                 | sexp                  |                    | <cpp11/sexp.hpp>             |
37| XPtr                    |                       | external_pointer   | <cpp11/external_pointer.hpp> |
38| Environment             |                       | environment        | <cpp11/environment.hpp>      |
39| Function                |                       | function           | <cpp11/function.hpp>         |
40| Environment (namespace) |                       | package            | <cpp11/function.hpp>         |
41| wrap                    |                       | as_sexp            | <cpp11/as.hpp>               |
42| as                      |                       | as_cpp             | <cpp11/as.hpp>               |
43| stop                    | stop                  |                    | <cpp11/protect.hpp>          |
44| checkUserInterrupt      | check_user_interrupt  |                    | <cpp11/protect.hpp>          |
45
46## Incomplete list of Rcpp features not included in cpp11
47
48- None of [Modules](https://CRAN.R-project.org/package=Rcpp/vignettes/Rcpp-modules.pdf)
49- None of [Sugar](https://CRAN.R-project.org/package=Rcpp/vignettes/Rcpp-sugar.pdf)
50- Some parts of [Attributes](https://CRAN.R-project.org/package=Rcpp/vignettes/Rcpp-attributes.pdf)
51  - No dependencies
52  - No random number generator restoration
53  - No support for roxygen2 comments
54  - No interfaces
55
56## Read-only vs writable vectors
57
58The largest difference between cpp11 and Rcpp classes is that Rcpp classes modify their data in place, whereas cpp11 classes require copying the data to a writable class for modification.
59
60The default classes, e.g. `cpp11::doubles` are *read-only* classes that do not permit modification.
61If you want to modify the data you need to use the classes in the `cpp11::writable` namespace, e.g. `cpp11::writable::doubles`.
62
63In addition use the `writable` variants if you need to create a new R vector entirely in C++.
64
65## Fewer implicit conversions
66
67Rcpp also allows very flexible implicit conversions, e.g. if you pass a `REALSXP` to a function that takes a `Rcpp::IntegerVector()` it is implicitly converted to a `INTSXP`.
68These conversions are nice for usability, but require (implicit) duplication of the data, with the associated runtime costs.
69
70cpp11 throws an error in these cases. If you want the implicit coercions you can add a call to `as.integer()` or `as.double()` as appropriate from R when you call the function.
71
72## Calling R functions from C++
73
74Calling R functions from C++ is similar to using Rcpp.
75
76```c++
77Rcpp::Function as_tibble("as_tibble", Rcpp::Environment::namespace_env("tibble"));
78as_tibble(x, Rcpp::Named(".rows", num_rows), Rcpp::Named(".name_repair", name_repair));
79```
80
81```c++
82using namespace cpp11::literals; // so we can use ""_nm syntax
83
84auto as_tibble = cpp11::package("tibble")["as_tibble"];
85as_tibble(x, ".rows"_nm = num_rows, ".name_repair"_nm = name_repair);
86```
87
88
89## Appending behavior
90
91One major difference in Rcpp and cpp11 is how vectors are grown.
92Rcpp vectors have a `push_back()` method, but unlike `std::vector()` no additional space is reserved when pushing.
93This makes calling `push_back()` repeatably very expensive, as the entire vector has to be copied each call.
94
95In contrast `cpp11` vectors grow efficiently, reserving extra space.
96Because of this you can do ~10,000,000 vector appends with cpp11 in approximately the same amount of time that Rcpp does 10,000, as this benchmark demonstrates.
97
98```{r, message = FALSE, eval = should_run_benchmarks()}
99library(cpp11test)
100grid <- expand.grid(len = 10 ^ (0:7), pkg = "cpp11", stringsAsFactors = FALSE)
101grid <- rbind(
102  grid,
103  expand.grid(len = 10 ^ (0:4), pkg = "rcpp", stringsAsFactors = FALSE)
104)
105b_grow <- bench::press(.grid = grid,
106  {
107    fun = match.fun(sprintf("%sgrow_", ifelse(pkg == "cpp11", "", paste0(pkg, "_"))))
108    bench::mark(
109      fun(len)
110    )
111  }
112)[c("len", "pkg", "min", "mem_alloc", "n_itr", "n_gc")]
113saveRDS(b_grow, "growth.Rds", version = 2)
114```
115
116```{r, echo = FALSE, dev = "svg", fig.ext = "svg", eval = capabilities("cairo")}
117b_grow <- readRDS("growth.Rds")
118library(ggplot2)
119ggplot(b_grow, aes(x = len, y = min, color = pkg)) +
120  geom_point() +
121  geom_line() +
122  bench::scale_y_bench_time() +
123  scale_x_log10(
124    breaks = scales::trans_breaks("log10", function(x) 10^x),
125    labels = scales::trans_format("log10", scales::math_format(10^.x))
126  ) +
127  coord_fixed() +
128  theme(panel.grid.minor = element_blank()) +
129  labs(title = "log-log plot of vector size vs construction time", x = NULL, y = NULL)
130```
131
132```{r, echo = FALSE}
133knitr::kable(b_grow)
134```
135
136## Random Number behavior
137
138Rcpp unconditionally includes calls to `GetRNGstate()` and `PutRNGstate()` before each wrapped function.
139This ensures that if any C++ code calls the R API functions `unif_rand()`, `norm_rand()`, `exp_rand()` or `R_unif_index()` the random seed state is set accordingly.
140cpp11 does _not_ do this, so you must include the calls to `GetRNGstate()` and `PutRNGstate()` _yourself_ if you use any of those functions in your C++ code.
141See [R-exts 6.3 - Random number generation](https://cran.r-project.org/doc/manuals/r-release/R-exts.html#Random-numbers) for details on these functions.
142
143One convenient way to do safely is to use a simple class:
144
145```cpp
146class local_rng {
147public:
148  local_rng() {
149    GetRNGstate();
150  }
151
152  ~local_rng(){
153    PutRNGstate();
154  }
155};
156
157void foo() {
158  local_rng rng_state;
159  /* my code using the RNG */
160}
161```
162
163## Mechanics of converting a package from Rcpp
164
1651. Add cpp11 to `LinkingTo`
1661. Add C++11 to `SystemRequirements`
1671. Convert all instances of `// [[Rcpp::export]]` to `[[cpp11::register]]`
1681. Clean and recompile the package, e.g. `pkgbuild::clean_dll()` `pkgload::load_all()`
1691. Run tests `devtools::test()`
1701. Start converting function by function
171   - Remember you can usually inter-convert between cpp11 and Rcpp classes by going through `SEXP` if needed.
172   - Converting the code a bit at a time (and regularly running your tests) is the best way to do the conversion correctly and make progress
173   - Doing a separate commit after converting each file (or possibly each function) can make finding any regressions with [git bisect](https://youtu.be/KKeucpfAuuA) much easier in the future.
174
175## Common issues when converting
176
177### STL includes
178
179Rcpp.h includes a number of STL headers automatically, notably `<string>` and `<vector>`, however the cpp11 headers generally do not. If you have errors like
180
181> error: no type named 'string' in namespace 'std'
182
183You will need to include the appropriate STL header, in this case `<string>`.
184
185### R API includes
186
187cpp11 conflicts with macros declared by some R headers unless the macros `R_NO_REMAP` and `STRICT_R_HEADERS` are defined.
188If you include `cpp11/R.hpp` before any R headers these macros will be defined appropriately, otherwise you may see errors like
189
190> R headers were included before cpp11 headers and at least one of R_NO_REMAP or STRICT_R_HEADERS was not defined.
191
192Which indicate that you must either change your include order or add preprocessor definitions for `R_NO_REMAP` and `STRICT_R_HEADERS`.
193Note that transitive includes of R headers (for example, those included by `Rcpp.h`) can also introduce the conflicting macros.
194
195### Type aliases
196
197If you use typedefs for cpp11 types or define custom types you will need to define them in a `pkgname_types.hpp` file so that `cpp_register()` can include it in the generated code.
198
199### `cpp11::stop()` and `cpp11::warning()` with `std::string`
200
201`cpp11::stop()` and `cpp11::warning()` are thin wrappers around `Rf_stop()` and `Rf_warning()`.
202These are simple C functions with a `printf()` API, so do not understand C++ objects like `std::string`.
203Therefore you need to call `obj.c_str()` when passing character data to them.
204
205### Logical vector construction
206
207If you are constructing a length 1 logical vector you may need to explicitly use a `r_bool()` object in the initializer list rather than `TRUE`, `FALSE` or `NA_INTEGER`.
208This issue only occurs with the clang compiler, not gcc.
209When constructing vectors with more than one element this is not an issue
210
211```cpp
212// bad
213cpp11::writable::logicals({FALSE});
214
215// good
216cpp11::writable::logicals({r_bool(FALSE)});
217
218// good
219cpp11::writable::logicals({FALSE, NA_LOGICAL});
220```
221