Romain François
2014-Feb-26 08:41 UTC
[Rd] help with gotoExitingHandler(R_NilValue, call, entry); . Implementation of error handling internally
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
Romain François
2014-Feb-27 07:13 UTC
[Rd] help with gotoExitingHandler(R_NilValue, call, entry); . Implementation of error handling internally
Of course one problem is that R hides things that I need for this, such as R_HandlerStack and R_ReturnedValue. I understand they have to be hidden, in which case can we have an exposed mechanism similar to what R_ToplevelExec does, but with the added functionality of giving access to the condition where R_ToplevelExec would return TRUE. Would someone be willing to review a patch to R with this added functionality. Please. In any case, I still would like some comments about my initial question about R_NilValue use in gotoExitingHandler(R_NilValue, call, entry); Romain Le 26 f?vr. 2014 ? 09:41, Romain Fran?ois <romain at r-enthusiasts.com> a ?crit :> 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 at r-project.org mailing list > https://stat.ethz.ch/mailman/listinfo/r-devel