2016-01-30 3 views
7

Si supponga di avere due pacchetti in R, il primo denominato foo, il secondo denominato bar. Voglio includere una funzione C in foo e condividere tale funzionalità con bar in modo indipendente dalla piattaforma e coerente con le politiche CRAN.Qual è il metodo preferito per condividere il codice C compilato in un pacchetto R e eseguirlo da un altro?

Qual è il metodo preferito per eseguire questa operazione e come dovrei utilizzare la registrazione delle funzioni e le librerie dinamiche?

Lo scopo della mia domanda è che, anche se ho letto tutta la documentazione che ho trovato, non c'è nulla che mi sia accaduto come la cosa ovvia da fare, e non sono sicuro di quale sia la linea di condotta più sostenibile .

Esempio:

supponga che in un pacchetto foo, definisco una funzione C addinc che aggiunge due numeri.

#include <R.h> 
#include <Rinternals.h> 

SEXP addinc(SEXP x_, SEXP y_) { 
    double x = asReal(x_); 
    double y = asReal(y_); 

    double sum = x + y; 

    return ScalarReal(sum); 
} 

Nello stesso pacchetto, posso provare a chiamare addinc in una funzione denominata R addinr tramite l'interfaccia .Call.

addinr <- function(x,y){ 
    .Call("addinc", x, y, PACKAGE="foo") 
} 

Tuttavia, quando si costruisce, il controllo e l'installazione del pacchetto, in esecuzione addinr restituisce l'errore di seguito, presumibilmente perché la funzione non è ancora registrato entro R.

library(foo) 
addinr(1,2) 

Error in .Call ("addinc", x, y, pACCHETTO = "foo"):
"addinc" non disponibile per .Call() per il pacchetto "foo"

Come sembra a me, il modo più semplice per risolvere questo problema è creare una libreria dinamica per il codice compilato aggiungendo il file NAMESPACE al file s. Questo sembra risolvere il problema perché ora posso chiamare addinr() senza problemi. Inoltre, posso correre .Call("addinc", ..., PACKAGE="foo") direttamente da R.

mio vera questione, tuttavia, si verifica quando un secondo pacchetto, diciamo bar, si suppone di utilizzare foo s addinc. Ad esempio, supponiamo che bar definisca una funzione multiplyinr come segue.

multiplyinr <- function(x,y){ 
    ans <- 0 
    for(i in 1:y) ans <- .Call("addinc", ans, x, PACKAGE="foo") 
    ans 
} 

Questo, infatti, funziona completamente bene, e posso chiamare multiplyinr entro R. Tuttavia, quando la costruzione e la verifica bar, ricevo una nota lamentano il fatto che bar sta chiamando le funzioni di lingua straniera da un diverso pacchetto.

funzione degli Esteri chiamata a un pacchetto diverso:
.Call ("addinc", ..., PACCHETTO = "foo")
vedere il capitolo 'di sistema e di lingua straniera interfacce' in la 'scrittura R Estensioni ' Manuale.

Secondo this question, il pacchetto bar non sarebbe adatto per la presentazione di CRAN perché usando .Call() in questo modo non è considerato "portatile" come spiegato nella Writing R Extensions manual.

In conclusione, la semplice soluzione di avere foo include un useDynLib(foo) nel suo file NAMESPACE non sembra proprio tagliarlo. Quindi la mia domanda: qual è il metodo preferito per condividere una funzione C con altri pacchetti?

Inoltre:

Sta usando useDynLib() veramente pericoloso o in contrasto con le politiche CRAN? Qual è lo scopo di dichiarare useDynLib() nel file NAMESPACE come alternativa alla registrazione e alla creazione manuale della libreria condivisa?

Registrando manualmente la funzione C e modificando la libreria condivisa, è possibile modificare qualsiasi cosa (ad esempio utilizzando R_RegisterCCallable() o R_registerRoutines())?

+0

Hai visto questa sezione di R-ext? https://cran.r-project.org/doc/manuals/r-release/R-exts.html#Linking-to-native-routines-in-other-packages –

+0

Come ho capito, usando 'R_RegisterCCallable' è principalmente inteso per il collegamento a livello C. Tuttavia, voglio solo accedere al codice C a livello R (usando '.Call'). Questo è anche il motivo per cui ho chiesto se la registrazione delle funzioni in questo modo avrebbe offerto alcun miglioramento. – SimonG

risposta

5

L'idea generale è che gli oggetti "informazioni sui simboli nativi" creati utilizzando, ad es. useDynLib(<pkg>, <symbol>) non fanno parte dell'API pubblica di un pacchetto, quindi i pacchetti client non dovrebbero chiamarli direttamente (si presume che potrebbero essere modificati nelle revisioni future del pacchetto).

Ci sono due modi per 'esportare' una routine compilato per l'utilizzo da parte pacchetti client:

  1. Basta esportare una funzione R involucro in foo che chiama direttamente la routine nativa, o
  2. Utilizzare il R_RegisterCCallable()/R_GetCCallable() coppia di funzioni per ottenere un puntatore alla funzione desiderata. (Pacchetto foo chiamerebbe R_RegisterCCallable() per fare qualche funzione disponibile; pacchetto client bar avrebbe chiamato R_GetCCallable() per ottenere un puntatore a tale funzione)

In altre parole, se un pacchetto autore 'registra' le loro funzioni C, sono dichiarando che far parte dell'API C pubblica del proprio pacchetto e consentire l'uso dei pacchetti client/chiamarli tramite questa interfaccia.