Suppressing Call Stack Info in Rcpp-Generated Errors and Warnings
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
Introduction
Rcpp has an elegant mechanism of exception handling
whereby C++ exceptions are automatically translated to errors in R. For most
projects, the Rcpp::stop wrapper (in conjunction with the BEGIN_RCPP and 
END_RCPP macros automatically inserted by 
RcppAttributes)
is sufficient and easy to use, providing an Rcpp equivalent of base::stop.
By default, it captures the call stack and attaches it to the exception
in R, giving informative error messages:
#include "Rcpp.h"
using namespace Rcpp; 
//[[Rcpp::export]]         
NumericVector add1(NumericVector x, NumericVector y){
    if(x.size() != y.size()){
        stop("x and y are not the same length!");
    }
    return x + y; 
}add1(1:5, 1:3)Error in add1(1:5, 1:3): x and y are not the same length!
This matches the default behavior of base::stop() which captures the call info.
For complex calling patterns (e.g., creating an argument list and calling the
Rcpp function with do.call), the resulting error messages are less helpful:
#include "Rcpp.h"
using namespace Rcpp; 
// [[Rcpp::export]]
NumericVector internal_function_name(NumericVector x, NumericVector y){
    if(x.size() != y.size()){
        stop("x and y are not the same length!");
    }
    return x + y; 
}add2 <- function(x, y){
    if(!is.numeric(x)){
        x <- as.numeric(x)
    }
    do.call(internal_function_name, list(x, y))
}
add2(1:5, 1:3)Error in (function (x, y) : x and y are not the same length!
If the internal error were being generated in R code, we might choose to use
the call.=FALSE argument to base::stop to suppress the unhelpful (function (x, y)
part of the error message, but we don’t (immediately) have a corresponding option
in Rcpp. In this gallery post, we show how to suppress the call-stack capture
of Rcpp::stop to give cleaner error messages.
Error Messages
The key functionality was added to Rcpp by Jim Hester in 
Rcpp Pull Request #663. 
To generate an R-level exception without a call stack, we pass an optional
false flag to Rcpp::exception. For example,
#include "Rcpp.h"
using namespace Rcpp; 
// [[Rcpp::export]]
NumericVector internal_function_name2(NumericVector x, NumericVector y){
    if(x.size() != y.size()){
        throw Rcpp::exception("x and y are not the same length!", false);
    }
    return x + y; 
}add3 <- function(x, y){
    if(!is.numeric(x)){
        x <- as.numeric(x)
    }
    do.call(internal_function_name2, list(x, y))
}
add3(1:5, 1:3)Error: x and y are not the same length!
This can’t capture the R level call stack, but it is at least cleaner than
the error message from the previous example.
Note that here, as elsewhere in C++, we need to handle exceptions using a 
try/catch structure, but we do not add it explicitly because
RcppAttributes
automatically handles this for us.
Warnings
Similar to Rcpp::stop, Rcpp also provides a warning function to generate 
R level warnings. It has the same call-stack capture behavior as stop.
For the direct call case:
#include "Rcpp.h"
using namespace Rcpp; 
//[[Rcpp::export]]         
NumericVector add4(NumericVector x, NumericVector y){
    if(x.size() != y.size()){
        warning("x and y are not the same length!");
    }
    return x + y; 
}add4(1:5, 1:3)Warning in add4(1:5, 1:3): x and y are not the same length! [1] 2 4 6 4 5
For the indirect call case:
#include "Rcpp.h"
using namespace Rcpp; 
// [[Rcpp::export]]
NumericVector internal_function_name3(NumericVector x, NumericVector y){
    if(x.size() != y.size()){
        warning("x and y are not the same length!");
    }
    return x + y; 
}add5 <- function(x, y){
    if(!is.numeric(x)){
        x <- as.numeric(x)
    }
    do.call(internal_function_name3, list(x, y))
}
add5(1:5, 1:3)Warning in (function (x, y) : x and y are not the same length! [1] 2 4 6 4 5
If we want to suppress the call stack info in this warning, we have to drop
down to the C-level R API. In particular, we use the Rf_warningcall function,
which takes the call as the first argument. By passing a NULL, we suppress the call:
#include "Rcpp.h"
using namespace Rcpp; 
// [[Rcpp::export]]
NumericVector internal_function_name5(NumericVector x, NumericVector y){
    if(x.size() != y.size()){
        Rf_warningcall(R_NilValue, "x and y are not the same length!");
    }
    return x + y; 
}add6 <- function(x, y){
    if(!is.numeric(x)){
        x <- as.numeric(x)
    }
    do.call(internal_function_name5, list(x, y))
}
add6(1:5, 1)Warning: x and y are not the same length! [1] 2 2 3 4 5
A C++11 Implementation
The above methods work, but they are not as clean as their Rcpp::stop and
Rcpp::warning counterparts. We can take advantage of C++11 to provide 
similar functionality for our call-free versions.
Basing our implementation on the C++11 implementation
of Rcpp::stop and Rcpp::warning we can define our own
stopNoCall and warningNoCall
#include "Rcpp.h"
using namespace Rcpp; 
// [[Rcpp::plugins(cpp11)]]
template <typename... Args>
inline void warningNoCall(const char* fmt, Args&&... args ) {
    Rf_warningcall(R_NilValue, tfm::format(fmt, std::forward<Args>(args)... ).c_str());
}
template <typename... Args>
inline void NORET stopNoCall(const char* fmt, Args&&... args) {
    throw Rcpp::exception(tfm::format(fmt, std::forward<Args>(args)... ).c_str(), false);
}
// [[Rcpp::export]]
NumericVector internal_function_name6(NumericVector x, NumericVector y, bool warn){
    if(x.size() != y.size()){
        if(warn){
            warningNoCall("x and y are not the same length!");  
        } else {
            stopNoCall("x and y are not the same length!");
        }
        
    }
    return x + y; 
}add7 <- function(x, y, warn=TRUE){
    if(!is.numeric(x)){
        x <- as.numeric(x)
    }
    do.call(internal_function_name6, list(x, y, warn))
}add7(1:5, 1:3, warn=TRUE)Warning: x and y are not the same length! [1] 2 4 6 4 5
add7(1:5, 1:3, warn=FALSE)Error: x and y are not the same length!
Note that we used C++11 variadic templates here – if we wanted to do something
similar in C++98, we could use essentially the same pattern, but would need to
implement each case individually.
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.
