Site icon R-bloggers

Introduction to exception handling

[This article was first published on Rcpp Gallery, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.

One of the many features that make C++ different from C is exception handling. This is a somewhat big topic, and large codebases sometimes eschew exceptions for lack of traceability in truly large programs (and eg the Google in-house C++ style guide is a well-known example of the Just say no school). Opinions are divided; exceptions are generally seen as a useful tool for smaller-scale projects.

We tend to agree. For our purposes, exceptions are just fine. They allow for a fine-grained way to report errors to R.

The basic idea is the that we must surround code which could throw an exception by a block of try and catch.

A simple example will help.

#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(...) { 
	::Rf_error("c++ exception (unknown reason)"); 
    }
    return NA_REAL;             // not reached
}

We can look at this example with a valid, and an invalid argument:

  takeLog(exp(1))   # works


[1] 1

  takeLog(-1.0)     # throws exception


Error: Inadmissible value

As we can see, execptions works as expected. By throwing an exception derived from the standard exception call, we arrive in the case first catch branch where the exception text can be captured and turned into a standard R error message.

The scaffolding of the try and catch is even automatically added by our common tools cxxfunction() (from the inline package) and sourceCpp(). So this shorter function is equivalent when these tools are used. Otherwise the macros BEGIN_CPP and END_CPP can be used.

#include <Rcpp.h>

using namespace Rcpp;

// [[Rcpp::export]]
double takeLog2(double val) {
    if (val <= 0.0) {         	// log() not defined here
        throw std::range_error("Inadmissible value");
    }
    return log(val);
}

Again, we can look at this example with a valid, and an invalid argument:

  takeLog2(exp(1))   # works


[1] 1

  takeLog2(-1.0)     # throws exception


Error: Inadmissible value

This shows that due to the automatic addition of the needed infrastructure, exception handling can add a useful mechanism to signal error conditions back to R.

There is even a shortcut defined as Rcpp function stop:

#include <Rcpp.h>

using namespace Rcpp;

// [[Rcpp::export]]
double takeLog3(double val) {
    if (val <= 0.0) {         	// log() not defined here
        stop("Inadmissible value");
    }
    return log(val);
}

  takeLog3(exp(1))   # works


[1] 1

  takeLog3(-1.0)     # throws exception


Error: Inadmissible value

To leave a comment for the author, please follow the link and comment on their blog: Rcpp Gallery.

R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.