Как безопасно генерировать предупреждение R в Rcpp
Мы знаем, что в Rcpp следует избегать вызова Rf_error()
, поскольку он включает в себя longjmp над деструкторами С++ в стеке. Вот почему мы скорее бросаем исключения С++ в код Rcpp (например, throw Rcpp::exception("...")
или через функцию stop("...")
).
Однако предупреждения R также могут привести к вызову Rf_error()
(это зависит от опции warn
). Таким образом, вызов Rf_warning()
также опасен.
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
Мы видим, что деструктор не был вызван (нет сообщения "end" ).
Как создать предупреждение R на С++ - деструктор-дружественный способ?
Ответы
Ответ 1
Одно из решений, которые я придумал, включает вызов функции R warning
из Rcpp:
// [[Rcpp::export]]
void test() {
Test t;
Function warning("warning");
warning("test"); // here R errors are caught and transformed to C++ exceptions
}
который дает правильное поведение, если warn>2
:
start
end
Error in eval(expr, envir, enclos) : (converted from warning) test
Интересно, имеет ли кто-нибудь лучшую идею для этого.
Ответ 2
Я бы рекомендовал вместо этого использовать stop()
(который является оберткой вокруг try/catch
):
С небольшим изменением кода:
#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()
*/
Я получаю желаемое поведение:
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>
Проблема longjmp
известна, но вы не выигрываете, избегая механизмов, которые мы должны раскручивать.