We know that calling Rf_error()
should be avoided in Rcpp as it involves a longjmp over C++ destructors on the stack. This is why we rather throw C++ exceptions in Rcpp code (like throw Rcpp::exception("...")
or via the stop("...")
function).
However, R warnings may also result in a call to Rf_error()
(this behavior depends on the warn
option). So, a call to Rf_warning()
is also risky.
Rcpp::sourceCpp(code = '
#include <Rcpp.h>
using namespace Rcpp;
class Test {
public:
Test() { Rcout << "start\\n"; }
~Test() { Rcout << "end\\n"; }
};
// [[Rcpp::export]]
void test() {
Test t;
Rf_warning("test");
}
')
options(warn=10)
test()
## start
## Error in test() : (converted from warning) test
We see that the destructor hasn't been called (there's no "end" message).
How to generate an R warning in a C++-destructor-friendly way?
One of the solutions I came up with involves calling the R's warning
function from Rcpp:
// [[Rcpp::export]]
void test() {
Test t;
Function warning("warning");
warning("test"); // here R errors are caught and transformed to C++ exceptions
}
which gives the correct behavior if warn>2
:
start
end
Error in eval(expr, envir, enclos) : (converted from warning) test
I wonder if anybody has a better idea for that.
I would recommend using stop()
(which is a wrapper around try/catch
) instead:
With your code slightly modified:
#include <Rcpp.h>
using namespace Rcpp;
class Test {
public:
Test() { Rcout << "start\n"; }
~Test() { Rcout << "end\n"; }
};
// [[Rcpp::export]]
void test() {
Test t;
Rf_warning("test");
}
// [[Rcpp::export]]
void test2() {
Test t;
stop("test2");
}
/*** R
options(warn=10)
#test()
test2()
*/
I get the desired behaviour:
R> sourceCpp("/tmp/throw.cpp")
R> options(warn=10)
R> #test()
R> test2()
start
end
Error in eval(expr, envir, enclos) (from srcConn#3) : test2
R>
The longjmp
issue is known, but you do not win by avoiding the mechanisms we have to unwind objects.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With