You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: vignettes/FAQ.Rmd
+54-46Lines changed: 54 additions & 46 deletions
Original file line number
Diff line number
Diff line change
@@ -5,6 +5,9 @@ vignette: >
5
5
%\VignetteIndexEntry{FAQ}
6
6
%\VignetteEncoding{UTF-8}
7
7
%\VignetteEngine{knitr::rmarkdown}
8
+
editor:
9
+
markdown:
10
+
wrap: sentence
8
11
---
9
12
10
13
```{r, include = FALSE}
@@ -20,15 +23,14 @@ If you have a question that you think would fit well here please [open an issue]
20
23
21
24
#### 1. What are the underlying types of cpp11 objects?
22
25
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 |
32
34
33
35
#### 2. How do I add elements to a named list?
34
36
@@ -133,7 +135,6 @@ my_true()
133
135
my_both()
134
136
```
135
137
136
-
137
138
#### 8. How do I create a new empty environment?
138
139
139
140
To do this you need to call the `base::new.env()` function from C++.
@@ -219,7 +220,6 @@ std::string my_string() {
219
220
}
220
221
```
221
222
222
-
223
223
#### 12. What are the types for C++ iterators?
224
224
225
225
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
228
228
#### 13. My code has `using namespace std`, why do I still have to include `std::` in the signatures of `[[cpp11::register]]` functions?
229
229
230
230
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.
232
232
233
233
The following won't compile
234
+
234
235
```{cpp11, eval = FALSE}
235
236
#include <cpp11.hpp>
236
237
#include <string>
@@ -243,8 +244,8 @@ string foobar() {
243
244
}
244
245
```
245
246
246
-
247
247
But this will compile and work as intended
248
+
248
249
```{cpp11}
249
250
#include <cpp11.hpp>
250
251
#include <string>
@@ -262,7 +263,7 @@ std::string foobar() {
262
263
In place modification breaks the normal semantics of R code.
263
264
In general it should be avoided, which is why `cpp11::writable` classes always copy their data when constructed.
264
265
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.
266
267
267
268
```{cpp11}
268
269
#include <cpp11.hpp>
@@ -288,7 +289,8 @@ x
288
289
289
290
`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.
290
291
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.
292
294
293
295
##### Destructors
294
296
@@ -310,15 +312,15 @@ A::~A() {
310
312
void test_destructor_ok() {
311
313
A a{};
312
314
cpp11::unwind_protect([&] {
313
-
Rf_error("oh no!");
315
+
Rf_error("oh no!");
314
316
});
315
317
}
316
318
317
319
[[cpp11::register]]
318
320
void test_destructor_bad() {
319
321
cpp11::unwind_protect([&] {
320
322
A a{};
321
-
Rf_error("oh no!");
323
+
Rf_error("oh no!");
322
324
});
323
325
}
324
326
```
@@ -334,11 +336,13 @@ test_destructor_bad()
334
336
#> Error: oh no!
335
337
```
336
338
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.
338
341
339
342
##### Nested `unwind_protect()`
340
343
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:
342
346
343
347
```{cpp11}
344
348
#include <cpp11.hpp>
@@ -347,19 +351,19 @@ Another issue that can arise has to do with _nested_ calls to `unwind_protect()`
347
351
void test_nested() {
348
352
cpp11::unwind_protect([&] {
349
353
cpp11::unwind_protect([&] {
350
-
Rf_error("oh no!");
354
+
Rf_error("oh no!");
351
355
});
352
356
});
353
357
}
354
358
```
355
359
356
360
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:
357
361
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.
363
367
364
368
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:
365
369
@@ -395,32 +399,35 @@ void test_outer() {
395
399
}
396
400
```
397
401
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.
412
417
413
418
#### 16. Ok but I really want to call `cpp11::unwind_protect()` manually
414
419
415
420
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:
416
421
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.
420
425
421
426
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()`.
422
427
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:
0 commit comments