3

I'm working on an R package where the same input-checking functions are called by multiple "actual" functions that are exported to users. If I use a simple stop() call, the error message is going to say that an error occurred in the input-checking function, which is not that useful...

I thought I'd get around this by wrapping the call to the input-checking function inside a tryCatch(), and then handling the error in the outer function. This does mostly what I want, but doesn't quite give the output that I'd like. The closest I've come is the following:

f <- function(i) {
    tryCatch({
        check_input(i)
    }, error = function(e) stop("in f: ", e$message, call. = FALSE)
    )
}

check_input <- function(i) {
    if(i < 0)
        stop("i is negative, value given was ", i)
}

f(-1)
# Error: in f: i is negative, value given was -1

Ideally, I'd like the error message to be

Error in f: i is negative, value given was -1

, which would be the case if stop were called within f() instead of check_input().

Hao Ye
  • 205
  • 1
  • 8
  • 1
    It would probably be easier to pass the name of the calling function to your `check_input` function and format the error message there. But there is no way to fool the build in `stop()` function to use the name a function other than the one where the stop actually occurred. You'd need to format the message yourself as you have done, – MrFlick Sep 24 '18 at 15:47
  • 1
    Possibly helpful: https://stackoverflow.com/questions/15595478/how-to-get-the-name-of-the-calling-function-inside-the-called-routine – MrFlick Sep 24 '18 at 15:55
  • Thanks @MrFlick, I was just playing around with `sys.call()` myself, but missed the use of `deparse()`. I'll keep this open for a while longer, but it sounds like this is as close as it will get. – Hao Ye Sep 24 '18 at 16:00

1 Answers1

2

Here's how you can grab the name of the function from the call stack and paste it in to the error message

f <- function(i) {
  check_input(i)
}
g <- function(i) {
  check_input(i)
}

check_input <- function(i, from=deparse(sys.calls()[[sys.nframe()-1]][[1]])) {
  getmsg <- function(m) ifelse(!is.null(from), paste0("in ", from, ": ", m), m)
  if(i < 0)
    stop(getmsg(paste0("i is negative, value given was ", i)), call. = FALSE)
}

f(-1)
#  Error: in f: i is negative, value given was -1
g(-1)
#  Error: in g: i is negative, value given was -1

You could also call check_input(i, from="otherfunction") to show whatever function name you want or check_input(i, from=NULL) to suppress the function name.

MrFlick
  • 195,160
  • 17
  • 277
  • 295