2014-09-09 13 views
5

Czy można wywoływać funkcje C (lub C++) z istniejącego pakietu R, w ramach dodatkowego kodu C?Wywołanie kodu C z pakietu R, w C

Na przykład funkcja marginTable() w moim pakiecie rje używa funkcji C o tej samej nazwie. Chcę utworzyć nowy pakiet zawierający więcej kodu C, z których niektóre mogą korzystać z wersji C marginTable(). Czy mogę wywołać tę funkcję z nowego kodu C, innego niż tylko poprzez skopiowanie kodu C do nowego pliku i pakietu?

Czy to po prostu kiepska praktyka używania takiego kodu wewnętrznego?

[Różne osób pytało o wywołanie skompilowany kod z innego pakietu R, ale wszyscy chcą to zrobić w ciągu R, nie z kodem C.]

+1

Zobacz ** [Łączenie z procedurami macierzystymi w sekcji Inne pakiety] (http://cran.r-project.org/doc/manuals/r-release/R-exts.html#Linking-to-native- rutyny-w-innych pakietach) ** z "Pisanie rozszerzeń R"; klucz wydaje się używać wywołania 'R_RegisterCCalla' w' R_init_rje' w pliku C (ostatnia funkcja w pierwszym bloku przykładu @MartinMorgan). Zastrzeżenie: Nie próbowałem tego. – BrodieG

Odpowiedz

6

Rozwiązanie R_RegisterCCallable/R_GetCCallable wskazywanego przez @BrodieG jest prawdopodobnie lepszy niż ten poniżej, przynajmniej wtedy, gdy można zmodyfikować pakiet, w którym wymagana jest rejestracja i gdzie wybór funkcji do wywołania jest prosty (poniższy przykład pochodzi z bardziej lub mniej skomplikowanego kodu R, który wybiera jeden z kilka funkcji, które można przekazać do C, podobnie jak lapply's FUN, gdzie wybór funkcji jest łatwiejszy do wdrożenia w R niż C). Istotne jest również, Linking to other packages, gdy chce się ujawnić/uzyskać dostęp do wielu funkcji.

Podobną możliwością jest, aby zarejestrować swoje funkcje C w opakowaniu rje, używając coś podobnego, w 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); 
} 

i w przestrzeni nazw

useDynLib(rje, .registration=TRUE) 

adres wpisu poziomu C punkt jest wtedy dostępny w R jako

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

i może być używany w drugim pakiecie za pomocą tego jako argument do funkcji C, np

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

z

#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); 
} 

to zbyt niezdarny, aby zilustrować to w pakiecie, ale zasada jest zilustrowany brzmienie złożyć 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); 
} 

skompilować z R CMD SHLIB rje.c i używać jako

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

Tak, jest to możliwe, i tak są proste przykłady.

Patrz na przykład nasze (niedawne-owski) RApiSerialize pakiet który zapewnia serialize() do wykorzystania przez inne pakiety Cran takich jak nasz RcppRedis opakowaniu.

inne pakiety znajdują się również:

We wszystkich przykładach robi eksportera deklaruje co zostało udostępnione, a importer deklaruje go jako używany.

W tym ustawieniu R może następnie wykonać resztę bez wyraźnego powiązania.