Jeroen Ooms
Jeroen Ooms

Reputation: 32978

How to signal an R exception from C code

The stop function in R can be called with a list object in order to signal an exception of a particular type. For example to signal a foo exception, we can do:

foo <- list (
  message = "my foo error"
)
class(foo) <- c("foo", "condition")

tryCatch (
  stop(foo), 
  foo = function(e){"never mind foo."}
)

Is there an interface for doing this (signaling custom exceptions) from C code? The Rf_error only allows for raising generic errors, it does not allow for specifying a exception class.

Upvotes: 1

Views: 246

Answers (2)

Jeroen Ooms
Jeroen Ooms

Reputation: 32978

Based on the suggestions above I wrote this function that does what I need:

void signal_exception(const char* message, const char* condition){
  //the condition object
  SEXP vec = PROTECT(allocVector(VECSXP, 1));
  SET_VECTOR_ELT(vec, 0, mkString(message));
  setAttrib(vec, R_NamesSymbol, mkString("message"));

  //the class vector
  SEXP cls = PROTECT(allocVector(STRSXP, 3));
  SET_STRING_ELT(cls, 0, mkChar(condition));
  SET_STRING_ELT(cls, 1, mkChar("error"));
  SET_STRING_ELT(cls, 2, mkChar("condition"));
  setAttrib(vec, R_ClassSymbol, cls);

  //signal the condition by calling base::stop
  SEXP stop_sym  = PROTECT(Rf_install("stop"));
  SEXP call = PROTECT(Rf_lang2(stop_sym, vec));
  UNPROTECT(4);
  Rf_eval(call, R_GlobalEnv);
}

It basically generalizes Rf_error with a second argument that you can use to specify the class of the error.

I don't think it is very elegant to call base::stop from C, only to have it call back to .Internal(.signalCondition()) but I it looks like there is currently no API for signalling the condition directly from C.

Upvotes: 4

Dirk is no longer here
Dirk is no longer here

Reputation: 368191

A standard example of how to do it in C++ is in this Rcpp Gallery post.

Taking just the first part with one minor edit:

#include <Rcpp.h>

using namespace Rcpp;

// [[Rcpp::export]]
double takeLog(double val) {
    try {
        if (val <= 0.0) {           // log() not defined here
            throw std::range_error("Inadmissible value");
        }
        return log(val);
    } catch(std::exception &ex) {   
        forward_exception_to_r(ex);
    } catch(...) { 
        stop("c++ exception (unknown reason)"); 
    }
    return NA_REAL;             // not reached
}

works as expected:

R> sourceCpp("/tmp/ex.cpp")
R> takeLog(-1)
Error: Inadmissible value
R> 

The remainder of that post has some details.

Upvotes: 2

Related Questions