2

I have created a function shown below:

#include <Rcpp.h>


//[[Rcpp::export]]
 void fun(SEXP num = R_NilValue){
        if(!Rf_isNull(num)){
            try{
                  int NUM = Rcpp::as<int>(num);
            }catch(...){
                 Rcpp::stop("Exception occured!");
            }
        }
    }

This function exported as a part of an R package and is included in the Makevars. Now, if I pass in a string value as an argument to this function (fun("a")), I am not able to catch the exception and the R session is aborting and crashing. Is there any way to catch this exception? The goal that I want to achieve in this case is to make sure that incorrect type arguments aren't passed to the function.

If I use the call shown instead of the previous one, the R session aborts:

// [[Rcpp::export]]
std::string fun(int imax = 214748364) {
     return "okay";
} 

Now, a call of fun('a') causes crashes here too even though I have a type mismatch.

Akshit
  • 424
  • 4
  • 15

1 Answers1

2

Your example is not complete as you did not tell us how you build and called the function.

"In general" you do not need to anything as R does it for you. Simplest example:

R> cppFunction("int doubleMe(int x) { return x + x; }")
R> doubleMe(21)
[1] 42
R> doubleMe("this won't work")
Error in doubleMe("this won't work") : 
  Not compatible with requested type: [type=character; target=integer].
R> 

If you use cppFunction or sourceCpp() in verbose mode you see the generated code. If you follow that code including the #define used you will see that it already includes a try/catch block for you which is why I said you do not need to do anything.

Now, you of course build functions "by hand" the (very) old way with R CMD COMPILE and friends---my old presentations from 10+ years ago show how if you need a quick read---and insert manual try/catch blocks to try this yourself.

In short, "inner" Rcpp code does throw exceptions which is why "outer" Rcpp code is set up to catch them. It works pretty well, and was refined over many years to address most head scratchers with possible OS-dependencies in the implementation.

Edit: Here is an old StackOverflow answer of mine from 2009 showing how to compile "by hand" in the days before Rcpp Attributes. It includes a try/catch block which we always (manually) included then.

Edit 2: Thanks for editing your question. If you use [[Rcpp::export]] the simplest version is

//[[Rcpp::export]]
void fun(int num) {
    // do nothing
}

showing the desired behaviour:

R> sourceCpp("~/git/stackoverflow/63049304/answer.cpp")
R> fun(2)
R> fun("lalala")
Error in fun("lalala") : 
  Not compatible with requested type: [type=character; target=integer].
R> 

Edit 3 Your example, as posted in your last edit, doesn't even compile. Repaired, it behaves as usual.

R> cppFunction('std::string fun(int imax=1234) { return("okay"); }')
R> fun("a")
Error in fun("a") : 
  Not compatible with requested type: [type=character; target=integer].
R> fun('a')
Error in fun("a") : 
  Not compatible with requested type: [type=character; target=integer].
R> 
Dirk Eddelbuettel
  • 360,940
  • 56
  • 644
  • 725
  • Thanks for the answer, I have updated my question. This function is exported as a part of an R package and the file containing this function has been included in Makevars. I tried to add multiple catch blocks as you've shown in the link that you've provided but catch(...){} block should catch any exception that has occured. – Akshit Jul 23 '20 at 12:26
  • As i tried to explain, Rcpp's code generation _already adds `try/catch` blocks_ for you. Look at macros `BEGIN_RCPP` and `VOID_END_RCPP` and `END_RCPP` in Rcpp's `include/Rcpp/macros/macros.h`. – Dirk Eddelbuettel Jul 23 '20 at 12:30
  • Thanks, I will go through them thoroughly. Sorry, I have updated my question again as I want the default value of my arguments as `null` and then cast them to an integer.(Not all the parameters are required to be passed on each function call) – Akshit Jul 23 '20 at 12:35
  • That is a different question for which you will find existing answers by searching for `Rcpp::Nullable`. I (extensively) answered your iniial (and, with hindsight, arguably not so well focused) question. I think it would be fair for you to accept this question. Do some homework, and then maybe ask a new question, ideally with a [MCVE](https://stackoverflow.com/help/minimal-reproducible-example). – Dirk Eddelbuettel Jul 23 '20 at 12:38
  • Okay, I have changed my question to the previous edit and have accepted the answer to the question. Thanks for the effort. I used `void fun(Nullable num = R_NilValue){...}` but can't cast it back to an `int` and got this error `error: invalid user-defined conversion from ‘Rcpp::Nullableto ‘int-fpermissive`. I will read more manuals and look for the possible solutions. – Akshit Jul 23 '20 at 12:53
  • Sounds good. Use of `Nullable(var)` has to work with the underlying `SEXP` model and is just "glue". Look at existing examples for `Nullable<>`. You need to check `var` for being `NULL` via a specific member function of `Nullable<>` and if it isn't _then_ you can instantiate the templated type. – Dirk Eddelbuettel Jul 23 '20 at 12:56
  • Thanks for the advice, I checked if var is Null using `var.isNull()` and then instantiated the integer using `int num = Rcpp::as(var);`. This works if I pass a numeric value to the function but the session aborts if I pass a string which was the case previously. The statement which is causing abort is `int num = Rcpp::as(var);`. – Akshit Jul 23 '20 at 13:10
  • Hi Dirk, I implemented the same function call that you have mentioned in edit 2 and I am still getting the R session abort. It'd be great if you could look into it. – Akshit Jul 23 '20 at 15:26
  • Then you are still doing something wrong. Edit 2 was "live", not faked. The one-liner has behind it all the relevant try/catch logic to cope with a bad argument. You appear to insist on redoing it all by hand and may be doing it inccompletely. – Dirk Eddelbuettel Jul 23 '20 at 15:47
  • Thanks, I understand that Rcpp handles these exceptions automatically and I was just mentioning the implementation that I tried and results that I obtained. I also observed that if I give an integer argument value where a string is expected, I am getting an appropriate error `Expecting a single string value: [type=double; extent=1]` but the session aborts when a string is assigned where an integer is expected. – Akshit Jul 23 '20 at 16:07