2009-11-12 6 views
14

Quelle est la meilleure façon d'utiliser une bibliothèque C++ dans R, en conservant, espérons-le, les structures de données C++. Je ne suis pas du tout un utilisateur C++, donc je ne suis pas clair sur les mérites relatifs des approches disponibles. Le manuel R-ext semble suggérer d'encapsuler toutes les fonctions C++ en C. Cependant, il existe au moins quatre ou cinq autres moyens d'incorporer C++. Deux façons sont les paquets w/lignée similaire, le Rcpp (maintenu par les paquets prolifiques Dirk Eddelbuettel) et RcppTemplate (tous les deux sur CRAN), quelles sont les différences entre les deux?Utilisation de bibliothèques C++ dans un package R

Un autre paquet, rcppbind disponible, sur R forge qui prétend adopter une approche différente de la liaison C++ et R (je ne sais pas dire). Le paquet en ligne disponible sur CRAN, prétend autoriser le C/C++ en ligne Je ne suis pas sûr que cela diffère de la fonctionnalité intégrée, en dehors de permettre au code d'être en ligne avec R/R.

Et, finalement, RSwig qui semble être in the wild mais il n'est pas clair comment il est pris en charge, comme le author's page n'a pas été mis à jour depuis des années. Ma question est, quels sont les mérites relatifs de ces différentes approches. Les plus portables et les plus robustes sont les plus faciles à mettre en œuvre. Si vous envisagiez de distribuer un paquet sur CRAN, quelle méthode utiliseriez-vous?

Répondre

15

Tout d'abord, un avertissement: J'utilise Rcpp tout le temps. En fait, quand (ayant été renommé par le temps de Rcpp) RcppTemplate était déjà orphelin et sans mise à jour depuis deux ans, j'ai commencé à le maintenir sous son nom initial de Rcpp (sous lequel il avait été contribué au RQuantLib). C'était il y a environ un an, et j'ai apporté quelques changements incrémentiels que vous pouvez trouver documentés dans le ChangeLog.

Maintenant, RcppTemplate est revenu très récemment après une période de trente-cinq mois sans aucune mise à jour ni correction. Il contient du nouveau code intéressant, mais il semble qu'il ne soit pas rétrocompatible, donc je ne l'utiliserai pas là où j'ai déjà utilisé Rcpp.

Rcppbind n'était pas très activement maintenu chaque fois que j'ai vérifié. Whit Armstrong a également un package d'interface basé sur un modèle appelé rabstraction. Est quelque chose de complètement différent: il facilite le cycle de compilation/liaison en «incorporant» votre programme comme une chaîne de caractères R qui est ensuite compilée, liée et chargée. J'ai parlé à Oleg d'avoir un support en ligne Rcpp ce qui serait bien.

Swig est également intéressant. Joe Wang a fait du bon travail là-bas et a enveloppé tout QuantLib pour R. Mais quand je l'ai essayé pour la dernière fois, ça ne marchait plus à cause de quelques changements dans les internes de R. Selon quelqu'un de l'équipe Swig, Joe pourrait encore travailler dessus. Le but de Swig est de plus grandes bibliothèques de toute façon. Ce projet pourrait probablement faire l'objet d'un renouveau mais il n'est pas sans défis techniques.

Une autre mention devrait aller à RInside qui fonctionne avec Rcpp et vous permet d'intégrer R dans les applications C++. Pour résumer: Rcpp fonctionne bien pour moi, surtout pour les petits projets exploratoires où vous voulez juste ajouter une fonction ou deux. Son but est la facilité d'utilisation, et il vous permet de «cacher» certains des internes de R qui ne sont pas toujours amusants à travailler. Je connais un certain nombre d'autres utilisateurs que j'ai aidé sur et par courrier électronique. Donc je dirais aller pour celui-ci.

Mes tutoriels 'Intro to HPC with R' ont quelques exemples de Rcpp, RInside et inline. Examinons donc un exemple concret (tiré des diapositives 'HPC avec R Intro' et emprunté à Stephen Milborrow qui l'a pris de Venables et Ripley). La tâche consiste à énumérer toutes les combinaisons possibles du déterminant d'une matrice 2x2 contenant seulement un seul chiffre dans chaque position. Cela peut se faire de moyens astucieux vectorisées (comme nous le verrons dans les coulisses de tutoriel) ou par la force brute comme suit:

#include <Rcpp.h> 

RcppExport SEXP dd_rcpp(SEXP v) { 
    SEXP rl = R_NilValue;  // Use this when there is nothing to be returned. 
    char* exceptionMesg = NULL; // msg var in case of error 

    try { 
    RcppVector<int> vec(v);  // vec parameter viewed as vector of ints 
    int n = vec.size(), i = 0; 
    if (n != 10000) 
     throw std::length_error("Wrong vector size"); 
    for (int a = 0; a < 9; a++) 
     for (int b = 0; b < 9; b++) 
     for (int c = 0; c < 9; c++) 
      for (int d = 0; d < 9; d++) 
      vec(i++) = a*b - c*d; 

    RcppResultSet rs;   // Build result set to be returned as list to R 
    rs.add("vec", vec);   // vec as named element with name 'vec' 
    rl = rs.getReturnList(); // Get the list to be returned to R. 
    } catch(std::exception& ex) { 
    exceptionMesg = copyMessageToR(ex.what()); 
    } catch(...) { 
    exceptionMesg = copyMessageToR("unknown reason"); 
    } 

    if (exceptionMesg != NULL) 
    Rf_error(exceptionMesg); 

    return rl; 
} 

Si vous enregistrez ce que, disons, dd.rcpp.cpp et ont Rcpp installé, utilisez simplement

PKG_CPPFLAGS=`Rscript -e 'Rcpp:::CxxFlags()'` \ 
    PKG_LIBS=`Rscript -e 'Rcpp:::LdFlags()'` \ 
    R CMD SHLIB dd.rcpp.cpp 

pour créer une bibliothèque partagée. Nous utilisons Rscript (ou r) pour poser à Rcpp l'information concernant l'en-tête et la bibliothèque. Une fois construit, nous pouvons charger et utiliser de R comme suit:

dyn.load("dd.rcpp.so") 

dd.rcpp <- function() { 
    x <- integer(10000) 
    res <- .Call("dd_rcpp", x) 
    tabulate(res$vec) 
} 

De la même manière, vous pouvez envoyer des vecteurs, MATRICS, ... de divers R et les types de données C de back-end en avant avec facilité. J'espère que cela aide un peu.

Edit 2 (environ cinq ans plus tard +):

donc cette réponse juste obtenu un upvote et donc barboter dans ma file d'attente. Un lot du temps est passé depuis que je l'ai écrit, et Rcpp a obtenu un lot plus riche en fonctionnalités. Donc, j'ai très vite écrit ce

#include <Rcpp.h> 

// [[Rcpp::export]] 
Rcpp::IntegerVector dd2(Rcpp::IntegerVector vec) { 
    int n = vec.size(), i = 0; 
    if (n != 10000) 
     throw std::length_error("Wrong vector size"); 
    for (int a = 0; a < 9; a++) 
     for (int b = 0; b < 9; b++) 
      for (int c = 0; c < 9; c++) 
       for (int d = 0; d < 9; d++) 
        vec(i++) = a*b - c*d; 
    return vec; 
} 

/*** R 
x <- integer(10000) 
tabulate(dd2(x)) 
*/ 

qui peut être utilisé comme suit avec le code dans un fichier /tmp/dd.cpp

R> Rcpp::sourceCpp("/tmp/dd.cpp") # on from any other file and path 

R> x <- integer(10000) 

R> tabulate(dd2(x)) 
[1] 87 132 105 155 93 158 91 161 72 104 45 147 41 96 
[15] 72 120 36 90 32 87 67 42 26 120 41 36 27 75 
[29] 20 62 16 69 19 28 49 45 12 18 11 57 14 48 
[43] 10 18 7 12 6 46 23 10 4 10 4 6 3 38 
[57] 2 4 2 3 2 2 1 17 
R> 

Certaines des principales différences sont les suivantes:

  • construction plus simple: il suffit sourceCpp() it; même exécute le code de test R à la fin
  • à part entière IntegerVector Type
  • emballage-manutention exception ajoutée automatiquement par sourceCpp() générateur de code
+0

Merci pour la réponse! Pourriez-vous clarifier ce que vous entendez par "nouveau code intéressant" dans RcppTemplate. En quoi le package diffère-t-il de Rcpp? Je ne sais presque rien sur C++, comment fonctionnent ces paquets? – Peter

+2

Dirk, peut-être qu'il est temps pour la vignette rapide parlé de faire fonctionner C++ avec R ... :(! – knguyen

+0

Il n'est pas facile d'expliquer comment ils diffèrent comme c'est ... dans le C++ qu'ils diffèrent –

Questions connexes