[Rd] help with gotoExitingHandler(R_NilValue, call, entry); . Implementation of error handling internally

Romain François romain at r-enthusiasts.com
Wed Feb 26 09:41:49 CET 2014


Hello, 

I’m trying to leverage R_ToplevelExec to implement C level try/catch. 

The way it is implemented allows for running a function in a top level context. This context gets an empty handler stack, so either the function runs correctly or it jumps. When it jumps, it does not find a handler, so it jumps to the top level context. 

R does not allow me to call begin context and end context directly, so instead what I do is call R_ToplevelExec, grab the global context inside the function, install my own handler that I don’t set to be a calling one, pretend this context is a CTXT_FUNCTION. 

Eventually I get to jump fun, so that I can later on grab the condition from R_ReturnedValue. 

This works well in the « not simple error » case, i.e. if I call stop( simpleError(...) ), but with simple errors, i.e. calls to Rf_error internally or bare calls to stop( "vvzvz" ) I can’t access the error. 

And this boils down to this call: 

gotoExitingHandler(R_NilValue, call, entry);

inside vsignalError : 

static void vsignalError(SEXP call, const char *format, va_list ap)
{
    char localbuf[BUFSIZE];
    SEXP list, oldstack;

    oldstack = R_HandlerStack;
    Rvsnprintf(localbuf, BUFSIZE - 1, format, ap);
    while ((list = findSimpleErrorHandler()) != R_NilValue) {
	char *buf = errbuf;
	SEXP entry = CAR(list);
	R_HandlerStack = CDR(list);
	strncpy(buf, localbuf, BUFSIZE - 1);
	/*	Rvsnprintf(buf, BUFSIZE - 1, format, ap);*/
	buf[BUFSIZE - 1] = 0;
	if (IS_CALLING_ENTRY(entry)) {
	    if (ENTRY_HANDLER(entry) == R_RestartToken)
		return; /* go to default error handling; do not reset stack */
	    else {
		SEXP hooksym, hcall, qcall;
		/* protect oldstack here, not outside loop, so handler
		   stack gets unwound in case error is protect stack
		   overflow */
		PROTECT(oldstack);
		hooksym = install(".handleSimpleError");
		PROTECT(qcall = LCONS(R_QuoteSymbol,
				      LCONS(call, R_NilValue)));
		PROTECT(hcall = LCONS(qcall, R_NilValue));
		hcall = LCONS(mkString(buf), hcall);
		hcall = LCONS(ENTRY_HANDLER(entry), hcall);
		PROTECT(hcall = LCONS(hooksym, hcall));
		eval(hcall, R_GlobalEnv);
		UNPROTECT(4);
	    }
	}
	else gotoExitingHandler(R_NilValue, call, entry);   // <<<  HERE
    }
    R_HandlerStack = oldstack;
}

Would it be possible to construct a simple condition instead of passing down R_NilValue so that I can grab this error and deal with it. 

The alternative is to set the handler to be a calling one, but I’d like to avoid that as much as possible as this means going back to the R side of things just to get access to the condition. 

My code is here: https://gist.github.com/romainfrancois/9225811

I have only tested this on OSX with R-devel. The code only uses R internal api (not Rcpp*). 

So down to what I’d like you to consider please if you are still reading here. Can we feed gotoExitingHandler with something more interesting than NULL. please. 



The end game is to add one layer of abstraction, e.g. pass to R_ToplevelExec a function that first deals with contexts, then calls an actual function. Combining this with lambda functions in C++ will make quite a nice and elegant way to handle error handling at the C++ level. 

I can provide the code that would create the simpleError, this is just making a simple VECSXP with names and classes, no big trouble here. 

Romain



More information about the R-devel mailing list