Skip to content

Commit aeb67b3

Browse files
authored
Modernize all vignettes with quarto visual editor preferences (#361)
* Ignore some pkgdown files * Modernize all vignettes with quarto visual editor preferences
1 parent 5f49623 commit aeb67b3

File tree

7 files changed

+359
-315
lines changed

7 files changed

+359
-315
lines changed

.Rbuildignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,5 @@ script.R
3030
^CRAN-SUBMISSION$
3131
.vscode
3232
^\.cache$
33+
^docs$
34+
^pkgdown$

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ TAGS
1919
/Meta/
2020
.vscode
2121
.cache
22+
docs

vignettes/FAQ.Rmd

Lines changed: 54 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ vignette: >
55
%\VignetteIndexEntry{FAQ}
66
%\VignetteEncoding{UTF-8}
77
%\VignetteEngine{knitr::rmarkdown}
8+
editor:
9+
markdown:
10+
wrap: sentence
811
---
912

1013
```{r, include = FALSE}
@@ -20,15 +23,14 @@ If you have a question that you think would fit well here please [open an issue]
2023

2124
#### 1. What are the underlying types of cpp11 objects?
2225

23-
| vector | element |
24-
| --- | --- |
25-
| cpp11::integers | int |
26-
| cpp11::doubles | double |
27-
| cpp11::logicals | cpp11::r_bool |
28-
| cpp11::strings | cpp11::r_string |
29-
| cpp11::raws | uint8_t |
30-
| cpp11::list | SEXP |
31-
26+
| vector | element |
27+
|-----------------|-----------------|
28+
| cpp11::integers | int |
29+
| cpp11::doubles | double |
30+
| cpp11::logicals | cpp11::r_bool |
31+
| cpp11::strings | cpp11::r_string |
32+
| cpp11::raws | uint8_t |
33+
| cpp11::list | SEXP |
3234

3335
#### 2. How do I add elements to a named list?
3436

@@ -133,7 +135,6 @@ my_true()
133135
my_both()
134136
```
135137

136-
137138
#### 8. How do I create a new empty environment?
138139

139140
To do this you need to call the `base::new.env()` function from C++.
@@ -219,7 +220,6 @@ std::string my_string() {
219220
}
220221
```
221222

222-
223223
#### 12. What are the types for C++ iterators?
224224

225225
The iterators are `::iterator` classes contained inside the vector classes.
@@ -228,9 +228,10 @@ For example the iterator for `cpp11::doubles` would be `cpp11::doubles::iterator
228228
#### 13. My code has `using namespace std`, why do I still have to include `std::` in the signatures of `[[cpp11::register]]` functions?
229229

230230
The `using namespace std` directive will not be included in the generated code of the function signatures, so they still need to be fully qualified.
231-
However you will _not_ need to qualify the type names within those functions.
231+
However you will *not* need to qualify the type names within those functions.
232232

233233
The following won't compile
234+
234235
```{cpp11, eval = FALSE}
235236
#include <cpp11.hpp>
236237
#include <string>
@@ -243,8 +244,8 @@ string foobar() {
243244
}
244245
```
245246

246-
247247
But this will compile and work as intended
248+
248249
```{cpp11}
249250
#include <cpp11.hpp>
250251
#include <string>
@@ -262,7 +263,7 @@ std::string foobar() {
262263
In place modification breaks the normal semantics of R code.
263264
In general it should be avoided, which is why `cpp11::writable` classes always copy their data when constructed.
264265

265-
However if you are _positive_ in-place modification is necessary for your use case you can use the move constructor to do this.
266+
However if you are *positive* in-place modification is necessary for your use case you can use the move constructor to do this.
266267

267268
```{cpp11}
268269
#include <cpp11.hpp>
@@ -288,7 +289,8 @@ x
288289

289290
`cpp11::unwind_protect()` is cpp11's way of safely calling R's C API. In short, it allows you to run a function that might throw an R error, catch the `longjmp()` of that error, promote it to an exception that is thrown and caught by a try/catch that cpp11 sets up for you at `.Call()` time (which allows destructors to run), and finally tells R to continue unwinding the stack now that the C++ objects have had a chance to destruct as needed.
290291

291-
Since `cpp11::unwind_protect()` takes an arbitrary function, you may be wondering if you should use it for your own custom needs. In general, we advise against this because this is an extremely advanced feature that is prone to subtle and hard to debug issues.
292+
Since `cpp11::unwind_protect()` takes an arbitrary function, you may be wondering if you should use it for your own custom needs.
293+
In general, we advise against this because this is an extremely advanced feature that is prone to subtle and hard to debug issues.
292294

293295
##### Destructors
294296

@@ -310,15 +312,15 @@ A::~A() {
310312
void test_destructor_ok() {
311313
A a{};
312314
cpp11::unwind_protect([&] {
313-
Rf_error("oh no!");
315+
Rf_error("oh no!");
314316
});
315317
}
316318
317319
[[cpp11::register]]
318320
void test_destructor_bad() {
319321
cpp11::unwind_protect([&] {
320322
A a{};
321-
Rf_error("oh no!");
323+
Rf_error("oh no!");
322324
});
323325
}
324326
```
@@ -334,11 +336,13 @@ test_destructor_bad()
334336
#> Error: oh no!
335337
```
336338

337-
In general, the only code that can be called within `unwind_protect()` is "pure" C code or C++ code that only uses POD (plain-old-data) types and no exceptions. If you mix complex C++ objects with R's C API within `unwind_protect()`, then any R errors will result in a jump that prevents your destructors from running.
339+
In general, the only code that can be called within `unwind_protect()` is "pure" C code or C++ code that only uses POD (plain-old-data) types and no exceptions.
340+
If you mix complex C++ objects with R's C API within `unwind_protect()`, then any R errors will result in a jump that prevents your destructors from running.
338341

339342
##### Nested `unwind_protect()`
340343

341-
Another issue that can arise has to do with _nested_ calls to `unwind_protect()`. It is very hard (if not impossible) to end up with invalidly nested `unwind_protect()` calls when using the typical cpp11 API, but you can manually create a scenario like the following:
344+
Another issue that can arise has to do with *nested* calls to `unwind_protect()`.
345+
It is very hard (if not impossible) to end up with invalidly nested `unwind_protect()` calls when using the typical cpp11 API, but you can manually create a scenario like the following:
342346

343347
```{cpp11}
344348
#include <cpp11.hpp>
@@ -347,19 +351,19 @@ Another issue that can arise has to do with _nested_ calls to `unwind_protect()`
347351
void test_nested() {
348352
cpp11::unwind_protect([&] {
349353
cpp11::unwind_protect([&] {
350-
Rf_error("oh no!");
354+
Rf_error("oh no!");
351355
});
352356
});
353357
}
354358
```
355359

356360
If you were to run `test_nested()` from R, it would likely crash or hang your R session due to the following chain of events:
357361

358-
- `test_nested()` sets up a try/catch to catch unwind exceptions
359-
- The outer `unwind_protect()` is called. It uses the C function `R_UnwindProtect()` to call its lambda function.
360-
- The inner `unwind_protect()` is called. It again uses `R_UnwindProtect()`, this time to call `Rf_error()`.
361-
- `Rf_error()` performs a `longjmp()` which is caught by the inner `unwind_protect()` and promoted to an exception.
362-
- That exception is thrown, but because we are in the outer call to `R_UnwindProtect()` (a C function), we end up throwing that exception _across_ C stack frames. This is _undefined behavior_, which is known to have caused R to crash on certain platforms.
362+
- `test_nested()` sets up a try/catch to catch unwind exceptions
363+
- The outer `unwind_protect()` is called. It uses the C function `R_UnwindProtect()` to call its lambda function.
364+
- The inner `unwind_protect()` is called. It again uses `R_UnwindProtect()`, this time to call `Rf_error()`.
365+
- `Rf_error()` performs a `longjmp()` which is caught by the inner `unwind_protect()` and promoted to an exception.
366+
- That exception is thrown, but because we are in the outer call to `R_UnwindProtect()` (a C function), we end up throwing that exception *across* C stack frames. This is *undefined behavior*, which is known to have caused R to crash on certain platforms.
363367

364368
You might think that you'd never do this, but the same scenario can also occur with a combination of 1 call to `unwind_protect()` combined with usage of the cpp11 API:
365369

@@ -395,32 +399,35 @@ void test_outer() {
395399
}
396400
```
397401

398-
This might seem unsafe because `cpp11::package()` uses `unwind_protect()` to call the R function for `test_inner()`, which then goes back into C++ to call `cpp11::stop()`, which itself uses `unwind_protect()`, so it seems like we are in a nested scenario, but this scenario does actually work. It makes more sense if we analyze it one step at a time:
399-
400-
- Call the R function for `test_outer()`
401-
- A try/catch is set up to catch unwind exceptions
402-
- The C++ function for `test_outer()` is called
403-
- `cpp11::package()` uses `unwind_protect()` to call the R function for `test_inner()`
404-
- Call the R function for `test_inner()`
405-
- A try/catch is set up to catch unwind exceptions (_this is the key!_)
406-
- The C++ function for `test_inner()` is called
407-
- `cpp11::stop("oh no!")` is called, which uses `unwind_protect()` to call `Rf_error()`, causing a `longjmp()`, which is caught by that `unwind_protect()` and promoted to an exception.
408-
- That exception is thrown, but this time it is caught by the try/catch set up by `test_inner()` as we entered it from the R side. This prevents that exception from crossing the C++ -> C boundary.
409-
- The try/catch calls `R_ContinueUnwind()`, which `longjmp()`s again, and now the `unwind_protect()` set up by `cpp11::package()` catches that, and promotes it to an exception.
410-
- That exception is thrown and caught by the try/catch set up by `test_outer()`.
411-
- The try/catch calls `R_ContinueUnwind()`, which `longjmp()`s again, and at this point we can safely let the `longjmp()` proceed to force an R error.
402+
This might seem unsafe because `cpp11::package()` uses `unwind_protect()` to call the R function for `test_inner()`, which then goes back into C++ to call `cpp11::stop()`, which itself uses `unwind_protect()`, so it seems like we are in a nested scenario, but this scenario does actually work.
403+
It makes more sense if we analyze it one step at a time:
404+
405+
- Call the R function for `test_outer()`
406+
- A try/catch is set up to catch unwind exceptions
407+
- The C++ function for `test_outer()` is called
408+
- `cpp11::package()` uses `unwind_protect()` to call the R function for `test_inner()`
409+
- Call the R function for `test_inner()`
410+
- A try/catch is set up to catch unwind exceptions (*this is the key!*)
411+
- The C++ function for `test_inner()` is called
412+
- `cpp11::stop("oh no!")` is called, which uses `unwind_protect()` to call `Rf_error()`, causing a `longjmp()`, which is caught by that `unwind_protect()` and promoted to an exception.
413+
- That exception is thrown, but this time it is caught by the try/catch set up by `test_inner()` as we entered it from the R side. This prevents that exception from crossing the C++ -\> C boundary.
414+
- The try/catch calls `R_ContinueUnwind()`, which `longjmp()`s again, and now the `unwind_protect()` set up by `cpp11::package()` catches that, and promotes it to an exception.
415+
- That exception is thrown and caught by the try/catch set up by `test_outer()`.
416+
- The try/catch calls `R_ContinueUnwind()`, which `longjmp()`s again, and at this point we can safely let the `longjmp()` proceed to force an R error.
412417

413418
#### 16. Ok but I really want to call `cpp11::unwind_protect()` manually
414419

415420
If you've read the above bullet and still feel like you need to call `unwind_protect()`, then you should keep in mind the following when writing the function to unwind-protect:
416421

417-
- You shouldn't create any C++ objects that have destructors.
418-
- You shouldn't use any parts of the cpp11 API that may call `unwind_protect()`.
419-
- You must be very careful not to call `unwind_protect()` in a nested manner.
422+
- You shouldn't create any C++ objects that have destructors.
423+
- You shouldn't use any parts of the cpp11 API that may call `unwind_protect()`.
424+
- You must be very careful not to call `unwind_protect()` in a nested manner.
420425

421426
In other words, if you only use plain-old-data types, are careful to never throw exceptions, and only use R's C API, then you can use `unwind_protect()`.
422427

423-
One place you may want to do this is when working with long character vectors. Unfortunately, due to the way cpp11 must protect the individual CHARSXP objects that make up a character vector, it can currently be quite slow to use the cpp11 API for this. Consider this example of extracting out individual elements with `x[i]` vs using the native R API:
428+
One place you may want to do this is when working with long character vectors.
429+
Unfortunately, due to the way cpp11 must protect the individual CHARSXP objects that make up a character vector, it can currently be quite slow to use the cpp11 API for this.
430+
Consider this example of extracting out individual elements with `x[i]` vs using the native R API:
424431

425432
```{cpp11}
426433
#include <cpp11.hpp>
@@ -432,7 +439,7 @@ cpp11::sexp test_extract_cpp11(cpp11::strings x) {
432439
for (R_xlen_t i = 0; i < size; ++i) {
433440
(void) x[i];
434441
}
435-
442+
436443
return R_NilValue;
437444
}
438445
@@ -446,16 +453,17 @@ cpp11::sexp test_extract_r_api(cpp11::strings x) {
446453
(void) STRING_ELT(data, i);
447454
}
448455
});
449-
456+
450457
return R_NilValue;
451458
}
452459
```
460+
453461
```{r}
454462
set.seed(123)
455463
x <- sample(letters, 1e6, replace = TRUE)
456464
457465
bench::mark(
458-
test_extract_cpp11(x),
466+
test_extract_cpp11(x),
459467
test_extract_r_api(x)
460468
)
461469
```

0 commit comments

Comments
 (0)