[Rd] how to call a function from C

luke at stat.uiowa.edu luke at stat.uiowa.edu
Thu Jan 14 18:38:20 CET 2010


On Thu, 14 Jan 2010, Romain Francois wrote:

> On 01/14/2010 12:42 PM, Laurent Gautier wrote:
>>> Hi,
>>> 
>>> In Rcpp, we now have a "Function" class to encapsulate functions
>>> (they cover all three kinds, but this may change).
>> 
>> Just a note on that: there is probably no hurry to do so.
>> rpy2 is also having CLOSXP, BUILTINSXP, and SPECIALSXP represented as
>> one function-like class and seems to be behave reasonably while a lot of
>> other things seem more urgent to sort out.
>> 
>>> To call the function, what we do is generate a call with the function
>>> as the first node and then evaluate the call.
>>> 
>>> SEXP stats = PROTECT( R_FindNamespace( mkString( "stats") ) ); SEXP
>>> rnorm = PROTECT( findVarInFrame( stats, install( "rnorm") ) ) ; SEXP
>>> call = PROTECT( LCONS( rnorm, CONS( ScalarInteger(10),
>>> CONS(ScalarReal(0), R_NilValue ) ) ) ); SEXP res = PROTECT( eval(
>>> call , R_GlobalEnv ) ); UNPROTECT(4) ; return res ;
>>> 
>>> It works, but I was wondering if there was another way. I've seen
>>> applyClosure, but I'm not sure I should attempt to use it or if using
>>> a call like above is good enough.
>> 
>> Using R_tryEval() will let you evaluate an expression in a given
>> environment, as well as capture an eventual error occurring during its
>> evaluation (and translate it as an exception).
>
> Sure. I did not want to over-complicate the question.
>
> I'm currently reviewing tryEval and its underlying R_TopLevelExec which does 
> not give me enough : when the error occurs, it'd be useful that the function 
> returns the condition object instead of NULL.

R_TopLevelExec, and hence R_tryEval, don't quite do what some users
expect and maybe would like. There are two issues.

The TopLevel part means that conceptually this call runs in its own
stack, which means things like calling error handlers set up outside
this call are not visible, and Sys.whatever functions don't see the
rest of the stack (or at least should not). A call to one of these is
conceptually a bit like asking a separate thread to evaluate the
expression and waiting for the result.  (I believe his mechanism was
added to handle finalizer code in the GC, where this makes sense, but
I may have my history wrong.)

The other issue is that what R_tryEval catches is not errors, it is
longjmp attempts.  The vast majority of these will come from attempts
to pass errors to exiting handlers, but there are other possibilities,
such as invoking an abort restart (which jumps to top level).

It might be useful to provide a C level interface for catching errors
as well as other jumps and for allowing C code to do cleanup
operations and then continue those jumps if appropriate, but getting
that right is fairly tricky and certainly hasn't risen high enough on
my priority list to warrant the investment of time.  The interface
would have to abstract over implementation details though: we
definitely do not want to expose and commit to current details of the
context stack implementation.

luke


>
>>> Romain
>>> 
>>> PS: using Rcpp's C++ classes you would express the code above as :
>>> 
>>> Environment stats("package:stats") ; Function rnorm = stats.get(
>>> "rnorm" ) return rnorm( 10, 0.0 ) ;
>> 
>> Feel free to snoop in rpy2's rpy/rinterface/rinterface.c and look for
>> "do_try_eval". The behavior looks very similar, the above snippet in
>> rpy2 would write like:
>> 
>> from rpy2.robjects.packages import importr
>> stats = importr('stats')
>> stats.rnorm(10, 0.0)
>
> nice
>
>

-- 
Luke Tierney
Chair, Statistics and Actuarial Science
Ralph E. Wareham Professor of Mathematical Sciences
University of Iowa                  Phone:             319-335-3386
Department of Statistics and        Fax:               319-335-3017
    Actuarial Science
241 Schaeffer Hall                  email:      luke at stat.uiowa.edu
Iowa City, IA 52242                 WWW:  http://www.stat.uiowa.edu



More information about the R-devel mailing list