[Rd] Is it possible to pass a function argument from R to compiled code in C?

Dirk Eddelbuettel edd at debian.org
Tue Sep 20 20:35:39 CEST 2011


On 20 September 2011 at 10:26, Alireza Mahani wrote:
| I have a function in R that takes another function as argument:
| 
| f <- function(g, ...) { #g is expected to be a function
| }
| 
| I want to see if there is a way to implement "f" in C and calling it from R
| using ".C" interface. I know that I can use function pointers for my C
| implementation, but I imagine it's going to be nearly impossible to pass a
| function from R to C. Are there any exact or approximate solutions
| available?

Yes you can -- using .Call() with can receive/return SEXP-typed variable, and
you can use an external pointer wrapped up in a SEXP.  The standard C API to
R supports it.

Now, Romain and I argue that the Rcpp interface for C++ makes it easier.
So what I am showing you now uses C++. You could do all that in C as well,
but you'd need to add a lot more hand-holding code which we hide behind the
C++ type system.

To keep this concrete, I have a full example in the Rcpp-using variant of
DEoptim, the RcppDE package which is on CRAN and R-Forge.  Here are some core
pieces of what demo(CompiledBenchmark) does:

R function:

    Wild <- function(x) { 		## 'Wild' function, global minimum at about -15.81515
        sum(10 * sin(0.3 * x) * sin(1.3 * x^2) + 0.00001 * x^4 + 0.2 * x + 80)/length(x)
    }

C++ variant of same function:


    double wild(SEXP xs) {
       Rcpp::NumericVector x(xs);
       int n = x.size();
       double sum = 0.0;
       for (int i=0; i<n; i++) {
          sum += 10 * sin(0.3 * x[i]) * sin(1.3 * x[i]*x[i]) + 0.00001 * x[i]*x[i]*x[i]*x[i] + 0.2 * x[i] + 80;
       }
       sum /= n;
       return(sum);
    }

and the key is then to (using the inline package, wrapping C++ code) create
an external pointer object (using the Rcpp::XPtr type) pointing at this C++
function just shown (and the real version does this for three different
functions with a switch, but the essence is just this):

    ## now via a class returning external pointer
    src.xptr <- 'return(XPtr<funcPtr>(new funcPtr(&wild)));'
    create_xptr <- cxxfunction(signature(funname="character"), body=src.xptr, inc=inc, plugin="Rcpp")

Calling create_xptr() in R gives us the XPtr in R --- and there we just pass
it down to the optimising function which then has a simple switch on the type
it receives to see whether it evaluates an R function, or a C++ function.  So
in the C++ function implementing the inner core of the optimisation (in
devol.cpp), we do

    if (TYPEOF(fcall) == EXTPTRSXP) { 		// non-standard mode: we are being passed an external pointer
	ev = new Rcpp::DE::EvalCompiled(fcall); // so assign a pointer using external pointer in fcall SEXP
    } else {					// standard mode: env_ is an env, fcall_ is a function 
	ev = new Rcpp::DE::EvalStandard(fcall, rho);	// so assign R function and environment
    }

and that simple branches between two cases of evaluator helper class.

To evaluate the R function at the C++ level we do

    double eval(SEXP par) {
	neval++;
	return defaultfun(par);
    }

with

    double defaultfun(SEXP par) { 			// essentialy same as the old evaluate
	SEXP fn = ::Rf_lang3(fcall, par, R_DotsSymbol); 
	SEXP sexp_fvec = ::Rf_eval(fn, env);		
	double f_result = REAL(sexp_fvec)[0];
	if (ISNAN(f_result)) 
	    ::Rf_error("NaN value of objective function! \nPerhaps adjust the bounds.");
	return(f_result); 
    }

whereas to evaluate the C++ function passed in, we do

    EvalCompiled( SEXP xps ) {				// get funptr from external pointer
	Rcpp::XPtr<funcPtr> xptr(xps);
	funptr = *(xptr);
    };
    double eval(SEXP par) {
	neval++;
	return funptr(par);
    }

This can probably be refined further, as it was mostly just one big proof of
concept.  But it works fine, do

   library(RcppDE)
   demo(CompiledBenchmark)

and several R-vs-C++ comparison of objective funtions should be timed for you.

If you're interested, we're always happy to take on Rcpp-specific questions
on the rcpp-devel list.

Hope this helps,  Dirk


-- 
New Rcpp master class for R and C++ integration is scheduled for 
San Francisco (Oct 8), more details / reg.info available at
http://www.revolutionanalytics.com/products/training/public/rcpp-master-class.php



More information about the R-devel mailing list