-
-
Notifications
You must be signed in to change notification settings - Fork 218
Unexpected error-handling since 1.0.10 #1268
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
That ... strikes me as odd. I work with Rcpp all day and I surely do see errors caught and returned to R. Mind you the default and standard approach is to let > Rcpp::cppFunction(r"[void foo() { Rprintf("Hi\n"); Rcpp::stop("Bye"); Rprintf("Gone\n"); } ]")
> foo()
Hi
Error: Bye
> Also are you aware that Rcpp injects try catch blocks for you? The above is not what compiles, try adding Lastly, note the first item in the release notes. This may be what rattles you, and as noted you can turn it off. Lines 36 to 43 in 3456999
Finally, if you use your standard C++ exception handling in functions not modified by Rcpp Attributes for you convenience it likely still works, We do not generally muck with exceptions because that would be ... crazy. |
It seems that the behavior changed with d389a8a. |
That is precisely the issue in NEWS that I pointed out to you, and it was preceeded by lengthy discussion. Unlikely to be reverted; I invite you to use the available toggle to turn it off if you do not want that behaviour. We try to give you a choice. |
Yup. Code#define RCPP_NO_UNWIND_PROTECT 1
#include <Rcpp/Rcpp>
// [[Rcpp::export]]
void evalStopFunction(const Rcpp::Function& fn) {
try {
fn();
} catch (const std::exception& e) {
std::rethrow_exception(std::current_exception());
} catch (...) {
throw std::runtime_error("Unknown Error");
}
}
/*** R
packageVersion("Rcpp")
fn <- function() stop("Test")
evalStopFunction(fn)
*/ Output> Rcpp::sourceCpp("/tmp/issue1268.cpp")
> packageVersion("Rcpp")
[1] ‘1.0.10.5’
> fn <- function() stop("Test")
> evalStopFunction(fn)
Error in evalStopFunction(fn) : Evaluation error: Test.
> All good? |
Sorry, I sent my last comment before seeing your answer. Thanks for your example; I see that disabling unwind protect reverts to the original behavior. I will also have a look if that is an option for my actual code. However, I still find the changed behavior odd, since I would have expected that function evaluation errors throw a For a bit of background, the code I used for the reprex is a minimal working example of an actual use case, which is testing that exceptions generated by a C++ wrapper of R's C-API for numerical integration are thrown as intended. Rcpp is only used for convenience in testing the wrapper. # RcppCore/[email protected]
library(Rcpp)
cppFunction(
'void evalStopFunction(const Rcpp::Function& fn) {
try {
fn();
} catch (...) {
// ignore
}
}'
)
fn <- function() stop("Test")
evalStopFunction(fn)
#> Error in (function () : Test Created on 2023-06-29 with reprex v2.0.2 Session infosessioninfo::session_info()
#> ─ Session info ───────────────────────────────────────────────────────────────
#> setting value
#> version R version 4.3.1 (2023-06-16)
#> os macOS Ventura 13.4
#> system aarch64, darwin20
#> ui X11
#> language (EN)
#> collate en_US.UTF-8
#> ctype en_US.UTF-8
#> tz Europe/Berlin
#> date 2023-06-29
#> pandoc 3.1.4 @ /opt/homebrew/bin/ (via rmarkdown)
#>
#> ─ Packages ───────────────────────────────────────────────────────────────────
#> package * version date (UTC) lib source
#> cli 3.6.1 2023-03-23 [1] CRAN (R 4.3.0)
#> digest 0.6.31 2022-12-11 [1] CRAN (R 4.3.0)
#> evaluate 0.21 2023-05-05 [1] CRAN (R 4.3.0)
#> fastmap 1.1.1 2023-02-24 [1] CRAN (R 4.3.0)
#> fs 1.6.2 2023-04-25 [1] CRAN (R 4.3.0)
#> glue 1.6.2 2022-02-24 [1] CRAN (R 4.3.0)
#> htmltools 0.5.5 2023-03-23 [1] CRAN (R 4.3.0)
#> knitr 1.43 2023-05-25 [1] CRAN (R 4.3.0)
#> lifecycle 1.0.3 2022-10-07 [1] CRAN (R 4.3.0)
#> magrittr 2.0.3 2022-03-30 [1] CRAN (R 4.3.0)
#> purrr 1.0.1 2023-01-10 [1] CRAN (R 4.3.0)
#> R.cache 0.16.0 2022-07-21 [1] CRAN (R 4.3.0)
#> R.methodsS3 1.8.2 2022-06-13 [1] CRAN (R 4.3.0)
#> R.oo 1.25.0 2022-06-12 [1] CRAN (R 4.3.0)
#> R.utils 2.12.2 2022-11-11 [1] CRAN (R 4.3.0)
#> Rcpp * 1.0.10 2023-01-22 [1] CRAN (R 4.3.0)
#> reprex 2.0.2 2022-08-17 [1] CRAN (R 4.3.0)
#> rlang 1.1.1 2023-04-28 [1] CRAN (R 4.3.0)
#> rmarkdown 2.22 2023-06-01 [1] CRAN (R 4.3.0)
#> rstudioapi 0.14 2022-08-22 [1] CRAN (R 4.3.0)
#> sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.3.0)
#> styler 1.10.1 2023-06-05 [1] CRAN (R 4.3.0)
#> vctrs 0.6.3 2023-06-14 [1] CRAN (R 4.3.0)
#> withr 2.5.0 2022-03-03 [1] CRAN (R 4.3.0)
#> xfun 0.39 2023-04-20 [1] CRAN (R 4.3.0)
#> yaml 2.3.7 2023-01-23 [1] CRAN (R 4.3.0)
#>
#> [1] /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/library
#>
#> ────────────────────────────────────────────────────────────────────────────── |
If Rcpp's convenience gets in your way, you can disable it (I showed you how) and/or do not use the wrapper creation (see my first comment) and/or do your own try/catch handling and/or write basic We generally do what we do for a reason, and this change was fairly long in coming and fairly important for both performance and edge cases (catching exceptions across compilation units can get hairy). |
For reference, some of the related discussion: Indeed, Rcpp no longer catches R errors and attempts to re-throw those as C++ exceptions, so code of the OP's form won't work as it did in previous builds of Rcpp. The consensus was that this was worth the breakage, since the amount of infrastructure around "safe" evaluation meant executing R code from Rcpp was very expensive. |
And for completeness, note that you are still able to catch those errors in C++, but instead of library(Rcpp)
cppFunction(
'void evalStopFunction(const Rcpp::Function& fn) {
try {
fn();
} catch (const Rcpp::LongjumpException& e) {
std::rethrow_exception(std::current_exception());
} catch (...) {
throw std::runtime_error("Unknown Error");
}
}'
)
fn <- function() stop("Test")
evalStopFunction(fn)
#> Error in (function () : Test |
Closing as documented and planned behavior exhibited here. |
Rcpp does not guard R evaluations as before from Version 1.0.10 due to using UnwindProtect; see RcppCore/Rcpp#1268. Thus, catching R evaluation errors is not possible anymore. To remedy, `RCPP_NO_UNWIND_PROTECT` needs to be defined to get the old behaviour.
Otherwise, whether `RCPP_NO_UNWIND_PROTECT` is defined depends on factors such as the compiler's optimization level; see RcppCore/Rcpp#1268.
Description
Errors produced by R functions (tested with
stop
) evaluated in C++ with Rcpp can no longer be caught as exceptions derived fromstd::expection
as of 1.0.10. For me, the changed behavior does not seem intended. In addition, I have looked into the ChangeLog but have not found any entry that explains or justifies this behavior.Reprex
Version 1.0.9
Created on 2023-06-29 with reprex v2.0.2
Session info
Version 1.0.10
Created on 2023-06-29 with reprex v2.0.2
Session info
The text was updated successfully, but these errors were encountered: