2017-07-22 2 views
-2

J'essaie d'implémenter une simple intégration de R avec C. Initialement, c'est simple: je veux passer valeurs de R à une fonction C intégrée dans une bibliothèque partagée .o via la fonction .C ou .Call. La fonction C devrait simplement imprimer les valeurs transmises via printf.Réception *** saisie segmentée ***, 'mémoire non mappée' et résultats tronqués après avoir transmis les valeurs de R à C

Voici ma méthode .Call:

.Call("test", as.integer(5), as.character("A"), as.character("string_test")) 

Et mon code C:

#include <stdio.h> 

void test(int integer, char character, char **str) { 
     printf("Integer: %i\nChar: %c\nString: %s\n", integer, character, *str); 
} 

Mais quand je l'appelle la fonction C de R via la console (rstudio se bloque) avec gdb permis, je reçois:

Integer: 1466480376 
Char: � 
Float: -100407552.000000 
String: 
***caught segfault *** 
address 0x20000090, cause 'memory not mapped' 

Traceback: 
1: .Call("test", as.integer(5), as.character("A"), as.character("string_test")) 

Comme si cela ne suffisait pas, comme nous pouvons voir les valeurs pas sed in sont imprimés très étrangement.

Détails de ce que je faisais, étape par étape:

j'ai construit la .o bibliothèque partagée avec gcc:

gcc -shared -o func_teste.o -fPIC func_teste.c 

et préparé pour le chargement dynamique dans un environnement R:

$ R CMD SHLIB func_teste.o 
gcc -m64 -I/usr/include/R -DNDEBUG -I/usr/local/include -fpic -O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -m64 -mtune=generic -c func_teste.c -o func_teste.o 
gcc -m64 -shared -L/usr/lib64/R/lib -Wl,-z,relro -specs=/usr/lib/rpm/redhat/redhat-hardened-ld -o func_teste.so func_teste.o -L/usr/lib64/R/lib -lR 

Et enfin, à l'intérieur de la console R, j'ai couru:

>dyn.load('func_teste.o') 
>.Call("test", as.integer(5), as.character("A"), as.character("string_test")) 

Quelqu'un a-t-il une idée de ce qui se passe?

+7

Il semble que GNU (ou l'équipe R) fournisse une documentation exceptionnelle. Voir aussi [Writing R Extensions] (https: // cran.r-project.org/doc/manuals/r-release/R-exts.html). Plus généralement, voir [The R Manuals] (https://cran.r-project.org/manuals.html). Je suis un peu envieux de la qualité des docs. – jww

+0

OP semble confondre la convention d'appel '.C()' avec l'utilisation de '.Call()'. –

Répondre

2

R propose deux fonctions principales d'interface à partir du code C (et donc du code C++, ou toute autre langue capable d'utiliser une interface C): - .C() est l'ancienne interface en utilisant int*, double* ... et aussi bien - .Call() est la plus récente, interface plus puissante en utilisant des objets SEXP

maintenant, .Call() semble plus compliqué, mais il est tellement plus puissant ainsi que plus sûr. Il y a un consensus quasi universel sur le fait que .C() ne devrait plus être utilisé (voir les diverses discussions sur la liste r-devel et d'autres endroits). L'inconvénient principal avec .Call() est que vous devez apprendre comment emballer et déballer vos valeurs. Ou ... vous trichez et laissez Rcpp le faire pour vous. Donc, avec cela, voici la solution d'une ligne de l'exemple de l'OP:

> library(Rcpp) 
> cppFunction("void mytest(int i, char c, std::string str) { printf(\"Integer: %i Char: %c String: %s\\n\", i, c, str.c_str()); }") 
> mytest(42L, 'Q', "a boat") 
Integer: 42 Char: Q String: a boat 
> 

J'ai fait la char* une chaîne. Notez que cppFunction() nécessite l'échappement des chaînes, vous pouvez regarder dans sourceCpp() et les paquets pour le travail réel. La documentation de Rcpp a des détails.