By | July 17, 2021

Dalam dua posting sebelumnya kami menjelaskan bagaimana R dapat memanggil kode C dengan .C() dan opsi yang lebih kompleks namun lebih kuat untuk memanggil kode C dengan .Call(). Di sini kami akan menjelaskan bagaimana paket Rcpp dapat digunakan untuk sangat menyederhanakan kode C Anda tanpa memaksa Anda untuk menjadi ahli dalam C++.

Pertama, pujian untuk Dirk Eddelbuettel dan Romain François yang tanpa kenal lelah berupaya untuk meningkatkan, mempromosikan, dan mendokumentasikan Rcpp telah menghasilkan salah satu paket CRAN yang paling populer. Pada Rcpp versi 0.9.15 ada 82 “Reverse depend” — paket lain yang menggunakan Rcpp. Ada juga delapan sketsa yang menggambarkan paket dalam istilah manusia. Yang lebih mudah diakses adalah makalah dan presentasi Dirk dan blog Romain. Sementara kami menumpuk pujian, jangan lupa untuk menyebutkan galeri grafik Romain.

Bahkan setelah semua pujian itu dan semua dokumentasi yang tersedia, kita masih menghadapi masalah dari mana harus memulai. Tidak ada vinyet yang ditargetkan untuk pengguna R yang ingin mencoba beberapa rutinitas C tetapi tidak cenderung mempelajari C++ dan yang tidak ingin menulis seluruh paket — setidaknya belum. Tempat yang baik untuk meninjau motivasi di balik Rcpp dan melihat beberapa contoh kode yang bagus adalah presentasi Dirk’s 62 slide Rcpp: Seamless R and C++ Integration atau yang lebih panjang, 146 slide Rcpp Tutorial Parts I dan II. Di antara dua presentasi dan dokumentasi paket tersebut, Anda benar-benar memiliki akses ke semua informasi yang Anda butuhkan. Namun, demi melanjutkan tema “langkah kecil” kami, kami sekali lagi akan membuat ulang tiga “Halo Dunia!” contoh, kali ini menggunakan Rcpp.

Persiapan

Hal pertama yang harus Anda lakukan adalah menginstal paket Rcpp. (Lihat Menginstal Paket jika ini tidak biasa.) Jika Anda menyimpan versi R Anda di ujung yang berdarah, Anda dapat melakukan ini dengan memanggil R dan menyuruhnya untuk mengambil paket terbaru.

$ R --vanilla

R version 2.14.1 (2011-12-22)
Copyright (C) 2011 The R Foundation for Statistical Computing
...
> install.packages( repos=c('http://cran.fhcrc.org/'), pkgs=c('Rcpp') )
Installing package(s) into ‘/usr/lib/R/library’
(as ‘lib’ is unspecified)
Warning message:
In getDependencies(pkgs, dependencies, available, lib) :
  package ‘Rcpp’ is not available (for R version 2.14.1)

Ups! Beberapa dari kita sedikit ketinggalan zaman. Daripada memutakhirkan versi R saya hari ini, saya akan melihat ke dalam Rcpp Sumber lama: arsip dan menemukan versi yang dirilis mendekati tanggal (2011-12-22) versi R. Versi 0.9. 10 dirilis pada 17-Feb-2012 dan harus kompatibel. Kami akan menginstal versi itu dari baris perintah dengan yang berikut:

$ wget http://cran.r-project.org/src/contrib/Archive/Rcpp/Rcpp_0.9.10.tar.gz
...
$ sudo R CMD INSTALL Rcpp_0.9.10.tar.gz
[sudo] password for jonathan: 
* installing to library ‘/usr/lib/R/library’
* installing *source* package ‘Rcpp’ ...
...
** testing if installed package can be loaded

* DONE (Rcpp)
Making packages.html  ... done

Hal terakhir yang perlu kita lakukan adalah membiarkan compiler dan linker kita tahu di mana library Rcpp baru berada (lihat slide 48 dari Tutorial).

$ export PKG_CPPFLAGS=`Rscript -e "Rcpp:::CxxFlags()"`
$ export PKG_CPPFLAGS=`Rscript -e "Rcpp:::LdFlags()"`

Ya, Wah! sekali lagi. Tapi sekarang kita siap untuk menulis kode C yang lebih mirip kode C dan, begitu kita menggunakan gula sintaksis Rcpp, mungkin bahkan seperti kode R.

Contoh langkah bayi

Saat menggunakan paket Rcpp kami masih menggunakan antarmuka .Call() ke kode C. Semua perubahan akan terlihat pada kode C yang sekarang menjadi kode C++ dengan ekstensi .cpp. Karena C++ adalah superset dari C, ini benar-benar legal tetapi kita perlu mendidik Rcpp dengan menggunakan RcppExport. Menurut slide 136 dari Tutorial:

* note : RcppExport is an alias to ‘extern "C"‘ defined by Rcpp.
*
* It gives C calling convention to the [...] function so that
* it can be called from .Call in R. Otherwise, the C++ compiler mangles the
* name of the function and .Call can’t find it.

BAIK. Ini dia contoh C++ pertama kita: helloA2.cpp.

#include <Rcpp.h>
RcppExport SEXP helloA2() {
  printf("Hello World!n");
  return(R_NilValue);
}

Ini terlihat sangat mirip dengan helloA1.c dari posting sebelumnya dan dikompilasi dan dipanggil dengan cara yang hampir sama.

R CMD SHLIB helloA2.cpp
g++ -m32 -I/usr/include/R -L/usr/lib/R/library/Rcpp/lib -lRcpp -Wl,-rpath,/usr/lib/R/library/Rcpp/lib 
-I/usr/local/include   -I/usr/lib/R/library/Rcpp/include -fpic  -O2 -g -pipe -Wall 
-Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m32 -march=i686 
-mtune=atom -fasynchronous-unwind-tables -c helloA2.cpp -o helloA2.o
g++ -m32 -shared -L/usr/local/lib -o helloA2.so helloA2.o -L/usr/lib/R/library/Rcpp/lib -lRcpp 
-Wl,-rpath,/usr/lib/R/library/Rcpp/lib -L/usr/lib/R/lib -lR

Kode pembungkus R identik dengan yang ada di posting sebelumnya untuk helloA1.c seperti penggunaan dalam sesi R.

# call RCpp C code
dyn.load("helloA2.so")
helloA2 <- function() {
  result <- .Call("helloA2")
}
> source('wrappers.R')
> greeting <- helloA2()
Hello World!
> class(greeting)
[1] "NULL"

Kode C yang lebih sederhana

Kami akan mengabaikan wrapper dan sesi R dalam dua contoh berikutnya karena keduanya identik dengan contoh di posting sebelumnya. Tapi lihat saja betapa sederhananya kode C! Alih-alih mengalokasikan memori, melindungi dari pengumpulan sampah dan semua casting antar tipe, kami mendapatkan ini untuk helloB2.cpp:

#include <Rcpp.h>
RcppExport SEXP helloB2() {
  Rcpp::StringVector result(1);
  result[0] = "Hello World!";
  return(result);
}

dan ini untuk helloC2.cpp:

#include <Rcpp.h>
RcppExport SEXP helloC2(SEXP greetingPointer) {
  Rcpp::StringVector greeting(greetingPointer);
  Rcpp::NumericVector result(greeting.size());
  for (int i=0; i<greeting.size(); i++) {
    result[i] = strlen(greeting[i]);
  }
  return(result);
}

Sekarang kita sedang berbicara. Ini mulai terlihat seperti kode programmer C, Heck, bahkan programmer Java bisa merasa nyaman. Alat yang disediakan oleh Rcpp bersifat sistematis, mudah dibaca, dan sebagai perubahan yang disambut baik, didokumentasikan dengan sangat baik. Sementara saya ragu-ragu untuk merekomendasikan penulisan kode C untuk antarmuka .Call() karena kurva pembelajaran yang menyakitkan, saya senang melaporkan bahwa kurva pembelajaran untuk menggunakan Rcpp tidak tidak mengharuskan Anda terlebih dahulu mendaki gunung untuk belajar C++. Pemrogram C dari semua tingkat keahlian akan mendapat manfaat dari penggunaan Rcpp.

Lebih Banyak Contoh

Sementara “Halo Dunia!” contoh mungkin merupakan tempat yang bagus untuk memulai, tidak mungkin memberikan kode template yang berguna bagi orang-orang. Untuk itu Anda akan ingin melihat-lihat kode sumber dari banyak paket yang bergantung pada Rcpp. Dirk Eddelbuettel tentu saja cukup tertarik dengan paket-paket dependen ini dan menjelaskan beberapa di antaranya pada slide 42 Integrasi R dan C++ Seamless dan slide 29 dari Tutorial Rcpp Bagian I dan II. Saya senang mengetahui bahwa beberapa paket ini ditulis dalam C dan mudah-mudahan akan memberikan contoh kode yang sangat baik.

Hadley Wickham telah menulis tutorial lengkap untuk paket Rcpp.

Untuk semua orang yang mencoba meningkatkan dan memperluas R — Semoga Sukses!


Versi sebelumnya dari artikel ini awalnya muncul pada tahun 2012 di WorkingwithData.