2013-01-20 5 views
37

consideri il seguente codice R,RcppArmadillo passaggio definito dall'utente funzione

## ----------- R version ----------- 

caller <- function(x=1:3, fun = "identity", ...){ 

    ## do some other stuff 
    ## ... 
    ## then call the function 
    eval(call(fun, x)) 

} 

fun1 <- function(x, ...){ 
    x + x 
} 

fun2 <- function(x, a = 10) a * x 

caller(fun = "fun1") 
caller(fun = "fun2") 

L'utente può passare un nome funzione "fun", che viene utilizzato da caller. Desidero eseguire lo stesso compito con gli oggetti RcppArmadillo (come parte di un'attività più complessa, ovviamente). La funzione dovrebbe essere definita in C++, e l'utente lo seleziona a livello di R facendo riferimento al suo nome:

caller_cpp(1:3, "fun1_cpp") 

o

caller_cpp(1:3, "fun2_cpp") 

ecc

Ecco il mio tentativo ingenuo per la funzione del chiamante, che non riesce nemmeno a compilare:

## ----------- C++ version ----------- 

library(Rcpp) 
require(RcppArmadillo)  

sourceCpp(code = ' 

     // [[Rcpp::depends("RcppArmadillo")]] 

     #include <RcppArmadillo.h> 

     using namespace arma ; 
     using namespace Rcpp ; 


     colvec fun1_cpp(const colvec x) 
     { 
     colvec y ; 
     y = x + x; 
     return (y); 
     } 

     colvec fun2_cpp(const colvec x) 
     { 
     colvec y ; 
     y = 10*x; 
     return (y); 
     } 

    // mysterious pointer business in an attempt 
    // to select a compiled function by its name 

     typedef double (*funcPtr)(SEXP); 
     SEXP putFunPtrInXPtr(SEXP funname) { 
      std::string fstr = Rcpp::as<std::string>(funname); 
      if (fstr == "fun1") 
       return(Rcpp::XPtr<funcPtr>(new funcPtr(&fun1_cpp))); 
      else if (fstr == "fun2") 
      return(Rcpp::XPtr<funcPtr>(new funcPtr(&fun2_cpp))); 

     } 

     // [[Rcpp::export]] 
     colvec caller_cpp(const colvec x, character funname) 
     { 
     Rcpp::XPtr fun = putFunPtrInXPtr(funname); 
     colvec y ; 
     y = fun(x); 
     return (y); 
     } 

    ') 

Modifica: adattato l'esempio dopo aver seguito il suggerimento di Dirk per esaminare RcppDE.

risposta

29

(A volte è necessario utilizzare svn log ... sui file per vedere come essi sono datati ...)

penso che una migliore caso d'uso è nel mio "porta" dei DEoptim C-based a Rcpp/RcppArmadillo: RcppDE. In esso, permetto alla routine di ottimizzazione di utilizzare una funzione R (come fa DEoptim) o una funzione compilata dall'utente - che è ciò che si vuole qui, come ho capito.

C'è un po 'di impalcatura in C++, ma non dovresti avere problemi a seguirlo.

Modifica su 2013-01-21 Di seguito è una soluzione completa che ho anche appena pubblicato come this new post at the Rcpp Gallery - compresi alcuni commenti e l'utilizzo di esempio.

// [[Rcpp::depends(RcppArmadillo)]] 
#include <RcppArmadillo.h> 

using namespace arma; 
using namespace Rcpp; 

vec fun1_cpp(const vec& x) { // a first function 
    vec y = x + x; 
    return (y); 
} 

vec fun2_cpp(const vec& x) { // and a second function 
    vec y = 10*x; 
    return (y); 
} 

typedef vec (*funcPtr)(const vec& x); 

// [[Rcpp::export]] 
XPtr<funcPtr> putFunPtrInXPtr(std::string fstr) { 
    if (fstr == "fun1") 
     return(XPtr<funcPtr>(new funcPtr(&fun1_cpp))); 
    else if (fstr == "fun2") 
     return(XPtr<funcPtr>(new funcPtr(&fun2_cpp))); 
    else 
     return XPtr<funcPtr>(R_NilValue); // runtime error as NULL no XPtr 
} 

// [[Rcpp::export]] 
vec callViaString(const vec x, std::string funname) { 
    XPtr<funcPtr> xpfun = putFunPtrInXPtr(funname); 
    funcPtr fun = *xpfun; 
    vec y = fun(x); 
    return (y); 
} 

// [[Rcpp::export]] 
vec callViaXPtr(const vec x, SEXP xpsexp) { 
    XPtr<funcPtr> xpfun(xpsexp); 
    funcPtr fun = *xpfun; 
    vec y = fun(x); 
    return (y); 
} 
+0

grazie, cercherò questo! spero che la mia totale ignoranza di C++ non mi impedisca di copiare e incollare ancora le cose. – baptiste

+0

Sfortunatamente c'è un po 'troppo in RcppDE per me da seguire. In 'evaluate.h' vedo la definizione di nuove classi, alcune presumibilmente chiamano una funzione arbitraria R, altre (immagino) chiamano una funzione C++ (voglio solo quest'ultima). Se un'anima gentile ha tempo da perdere, sarebbe un ottimo esempio per quanto riguarda la bella http://gallery.rcpp.org/. – baptiste

+0

leggere la vignetta conferma l'impressione che ho avuto guardando il codice. Se voglio occuparmi solo di funzioni compilate, selezionate in base al loro nome, ho bisogno di questo business di classe virtuale? – baptiste