Как безопасно генерировать предупреждение 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 известна, но вы не выигрываете, избегая механизмов, которые мы должны раскручивать.