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

______________________________________________
R-devel@r-project.org mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel

Reply via email to