2014-09-09 14 views
5

È possibile chiamare le funzioni C (o C++) da un pacchetto R esistente, all'interno di un ulteriore codice C?Chiamare il codice C da un pacchetto R, all'interno di C

Ad esempio, la funzione marginTable() nella mia confezione rje utilizza una funzione C con lo stesso nome. Voglio creare un nuovo pacchetto che contiene più codice C, alcuni dei quali potrebbero utilizzare la versione C di marginTable(). Posso chiamare quella funzione all'interno del nuovo codice C, oltre a copiare semplicemente il codice C nel nuovo file e pacchetto?

O è semplicemente una cattiva pratica utilizzare un codice interno come questo?

[Diverse persone hanno chiesto di chiamare il codice compilato da un altro pacchetto R, ma tutti vogliono farlo all'interno R, non con il codice C.]

+1

Vedere la sezione ** [Collegamento alle routine native in altri pacchetti] (http://cran.r-project.org/doc/manuals/r-release/R-exts.html#Linking-to-native- routine-in-altri-pacchetti) ** di "Scrittura delle estensioni R"; la chiave sembra utilizzare una chiamata a 'R_RegisterCCallable' in' R_init_rje' nel file C (l'ultima funzione nel primo blocco dell'esempio @MartinMorgan). Disclaimer: non ho provato questo. – BrodieG

risposta

6

La soluzione R_RegisterCCallable/R_GetCCallable puntato da @BrodieG è probabilmente migliore di quello qui sotto, almeno quando si può modificare il pacchetto in cui è richiesta la registrazione e dove la scelta della funzione da chiamare è diretta (l'esempio qui sotto è il codice R più o meno complicato che sceglie uno dei diverse funzioni passano a C, proprio come l'argomento FUN di lapply, dove la scelta della funzione è molto più facile da implementare in R che C). Rilevante è anche Linking to other packages quando si desidera esporre/accedere a molte funzioni.

Una possibilità correlato è per registrare i tuoi funzioni C nel pacchetto RJE, utilizzando qualcosa di simile, in R_init_rje.c

#include <Rinternals.h> 
#include <R_ext/Rdynload.h> 

SEXP rje(SEXP who) { 
    Rprintf("Hello %s\n", CHAR(STRING_ELT(who, 0))); 
    return R_NilValue; 
} 

static const R_CallMethodDef callMethods[] = { 
    {".rje", (DL_FUNC) &rje, 1}, 
    {NULL, NULL, 0} 
}; 

void R_init_rje(DllInfo * info) 
{ 
    R_registerRoutines(info, NULL, callMethods, NULL, NULL); 
} 

e nello spazio dei nomi

useDynLib(rje, .registration=TRUE) 

L'indirizzo della voce C-level punto è quindi disponibile in R come

rje_c = getNativeSymbolInfo(".rje", PACKAGE="rje") 

e può essere utilizzato in altro pacchetto utilizzando come argomento a una funzione C, ad esempio,

.Call(.use_rje, rje_c$address, "A User") 

con

#include <Rinternals.h> 
#include <R_ext/Rdynload.h> 

/* convenience definition of the function template */ 
typedef SEXP RJE_C_FUN(SEXP who); 

SEXP use_rje(SEXP rje_c_fun, SEXP who) { 
    /* retrieve the function pointer, using an appropriate cast */ 
    RJE_C_FUN *fun = (RJE_C_FUN *) R_ExternalPtrAddr(rje_c_fun); 
    return fun(who); 
} 

È troppo goffa per illustrare questo in un pacchetto, ma il principio è illustrato dal seguente presentare rje.c

#include <Rinternals.h> 
#include <R_ext/Rdynload.h> 

/* convenience definition of the function template */ 
typedef SEXP RJE_C_FUN(SEXP who); 

SEXP rje(SEXP who) { 
    Rprintf("Hello '%s'\n", CHAR(STRING_ELT(who, 0))); 
    return R_NilValue; 
} 

SEXP use_rje(SEXP rje_c_fun, SEXP who) { 
    /* retrieve the function pointer, using an appropriate cast */ 
    RJE_C_FUN *fun = (RJE_C_FUN *) R_ExternalPtrAddr(rje_c_fun); 
    return fun(who); 
} 

static const R_CallMethodDef callMethods[] = { 
    {".rje", (DL_FUNC) &rje, 1}, 
    {".use_rje", (DL_FUNC) &use_rje, 2}, 
    {NULL, NULL, 0} 
}; 

void R_init_rje(DllInfo * info) 
{ 
    R_registerRoutines(info, NULL, callMethods, NULL, NULL); 
} 

Compilare con R CMD SHLIB rje.c, e l'uso come

> dyn.load("rje.so") 
> .Call(".use_rje", getNativeSymbolInfo("rje")$address, "A User") 
Hello 'A User' 
NULL 
3

Sì, è possibile, e sì, ci sono esempi semplici.

veda ad esempio il nostro (recente-ish) RApiSerialize pacchetto che prevede serialize() per l'uso da altri pacchetti CRAN come nostro pacchetto RcppRedis.

Altri pacchetti fanno così:

In tutti gli esempi fa il esportatore dichiara ciò che viene messo a disposizione, e la importatore dichiara come usato.

In tale configurazione, R può quindi fare il resto, senza collegamenti espliciti.