21

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?

gagolews
  • 12,836
  • 2
  • 50
  • 75

2 Answers2

14

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.

gagolews
  • 12,836
  • 2
  • 50
  • 75
10

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.

Dirk Eddelbuettel
  • 360,940
  • 56
  • 644
  • 725
  • OK, @DirkEddelbuettel, I'm perfectly aware the `longjmp` issue, but I generally wish to generate a warning, not an error. But if `warn>2` I want it to fail cleanly. – gagolews Jul 03 '14 at 15:35
  • 3
    Oh, sorry, then I may have misread the question. It's still early in the day here at useR :) Maybe we need a C++ access point into R's `warning()` ... – Dirk Eddelbuettel Jul 03 '14 at 15:36
  • I sense you feel like prototyping one ;-) – Dirk Eddelbuettel Jul 03 '14 at 16:01
  • The simplest solution for me is to get `warning` from the `base` environment & call it (see my answer above) :) – gagolews Jul 03 '14 at 16:33
  • Actually, even `Rf_warning` seems fine, and you could then either `throw` if you want to exit (and then get your objects unwound) and just carry on. `Rf_warning`, per Section 6.3 is the function to use to signal a warning. – Dirk Eddelbuettel Jul 03 '14 at 21:16
  • 1
    I just want to make sure my software doesn't produce mem leaks if user sets `warn>2`. If he/she wants warnings, then he/she gets warnings. If warn->errs, then safe errors. Rf_warning in Rcpp is not safe in this setting (I know it's not a common problem, just hypothesising; basically it's a mindbreaker for my teaching purposes) :) – gagolews Jul 04 '14 at 06:33