问题
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?
回答1:
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.
回答2:
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.
来源:https://stackoverflow.com/questions/24557711/how-to-generate-an-r-warning-safely-in-rcpp