Перемещение из sourceCpp в пакет с помощью Rcpp
В настоящее время у меня есть файл .cpp
, который я могу скомпилировать с помощью sourceCpp()
. Как и ожидалось, создается соответствующая функция R, и код работает так, как ожидалось.
Вот он:
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
NumericVector exampleOne(NumericVector vectorOne, NumericVector vectorTwo){
NumericVector outputVector = vectorOne + vectorTwo;
return outputVector;
}
Теперь я конвертирую свой проект в пакет с помощью Rcpp
. Поэтому я создал скелет с rStudio и начал смотреть, как конвертировать вещи.
В Hadley отличный праймер на Cpp, он говорит в разделе "Использование Rcpp в пакете":
Если ваши пакеты используют атрибут Rcpp:: export, то добавляется еще один шаг в процессе сборки пакета. Функция compileAttributes проверяет исходные файлы в пакете для атрибутов Rcpp:: export и генерирует код, необходимый для экспорта функций в R.
Вы должны повторно запускать compileAttributes всякий раз, когда функции добавляются, удаляются или изменяются подписи. Обратите внимание: если вы создаете свой пакет с помощью RStudio или devtools, то этот шаг выполняется автоматически.
Итак, похоже, что код, скомпилированный с помощью sourceCpp()
, должен работать так же, как в пакете.
Я создал соответствующий R файл.
exampleOne <- function(vectorOne, vectorTwo){
outToR <- .Call("exampleOne", vectorOne, vectorTwo, PACKAGE ="testPackage")
outToR
}
Затем я (re) построил пакет, и я получаю эту ошибку:
Error in .Call("exampleOne", vectorOne, vectorTwo, PACKAGE = "voteR") :
C symbol name "exampleOne" not in DLL for package "testPackage"
Кто-нибудь имеет представление о том, что еще мне нужно делать, когда вы делаете код, который компилируется с помощью sourceCpp(), а затем используете его в пакете?
Я должен отметить, что я прочитал: "Написание пакета, использующего Rcpp" http://cran.rstudio.com/web/packages/Rcpp/vignettes/Rcpp-package.pdf, и понять представленную там базовую структуру. Однако, посмотрев исходный код RcppExamples
, кажется, что структура в виньетках не совсем такая же, как в примере пакета. Например, не используются файлы .h. Также ни виньетка, ни исходный код не используют атрибут [[Rcpp:: export]]. Это все затрудняет отслеживание того, где моя ошибка.
Ответы
Ответ 1
Вот моя "прогулка" о том, как перейти от использования sourceCpp() к пакету, который использует Rcpp. Если есть ошибка, вы можете отредактировать это или сообщить мне, и я отредактирую его.
[ПРИМЕЧАНИЕ: Я настоятельно рекомендую использовать RStudio для этого процесса.]
Итак, у вас есть свойство sourceCpp(), и теперь вам нужно создать пакет. Это не сложно, но может быть немного сложнее, потому что информация о создании пакетов с Rcpp варьируется от исчерпывающей полной документации, которую вы хотите с любым пакетом R (но это выше вашей головы как новичок), и новичок, чувствительный (которые могут не учитывать детали, которые вам понадобятся).
Здесь я использую oneCpp.cpp
и twoCpp.cpp
как имена двух файлов .cpp, которые вы будете использовать в своем пакете.
Вот что я предлагаю:
а. Сначала я предполагаю, что у вас есть версия theCppFile.cpp
, которая компилируется с помощью sourceCpp() и работает так, как вы ожидаете. Это не обязательно, но если вы новичок в Rcpp OR-пакетах, приятно убедиться, что ваш код работает в этой простой ситуации, прежде чем переходить к более сложному случаю ниже.
В. Теперь создайте свой пакет с помощью Rcpp.package.skeleton()
или используйте мастер проекта Project > Create Project > Package w/Rcpp в RStudio (настоятельно рекомендуется). Вы можете найти информацию об использовании Rcpp.package.skeleton() в [hadley/devtools] [1] или [Rcpp Attributes Vignette] [2]. Полная документация для написания пакетов с Rcpp находится в [Написание пакета, который использует Rcpp] [3], однако это предполагает, что вы хорошо знаете свой путь по С++ и не используете новый способ "Атрибуты" для выполнения Rcpp. Это будет неоценимо, хотя если вы перейдете к созданию более сложных пакетов.
Теперь у вас должна быть структура каталогов для вашего пакета, которая выглядит примерно так:
yourPackageName
- DESCRIPTION
- NAMESPACE
- \R\
- RcppExports.R
- Read-and-delete-me
- \man\
- yourPackageName-package.Rd
- \src\
- Makevars
- Makevars.win
- oneCpp.cpp
- twoCpp.cpp
- RcppExports.cpp
Как только все настроено, выполните "Сборка и перезагрузка" при использовании RStudio или compileAttributes()
, если вы не находитесь в RStudio.
С. Теперь вы должны увидеть в своем каталоге \R файл с именем RcppExports.R
. Откройте его и проверьте. В RcppExports.R
вы должны увидеть функции обертки R для всех файлов .cpp
, которые у вас есть в каталоге \src
. Довольно мило, а?..
D) Попробуйте выполнить функцию R, соответствующую функции, которую вы написали в theCppFile.cpp
. Это работает? Если это так, продолжайте.
E) Теперь вы можете просто добавить новые .cpp
файлы, такие как otherCpp.cpp
, в каталог \src
при их создании. Затем вам просто нужно перестроить пакет, и обертки R будут сгенерированы и добавлены к RcppExports.R
для вас. В RStudio это просто "Build and Reload" в меню "Построение". Если вы не используете RStudio, вы должны запустить compileAttributes()
Ответ 2
Короче говоря, трюк заключается в вызове compileAttributes()
из корня пакета. Так, например, для пакета foo
$ cd /path/to/foo
$ ls
DESCRIPTION man NAMESPACE R src
$ R
R> compileAttributes()
Эта команда генерирует отсутствующие RcppExports.cpp
и RcppExports.R
.
Ответ 3
Вам не хватает леса для деревьев.
sourceCpp()
- это недавняя функция; это часть того, что мы называем атрибутами Rcpp, у которого есть собственная виньетка (с тем же названием, в пакете, на моем веб-сайте и на CRAN), которую вы можете прочитать. В нем, помимо прочего, подробно описано, как превратить что-то, скомпилированное и запущенное с помощью sourceCpp()
в пакет. Это то, что вы хотите.
Случайное переключение между документацией не поможет вам, и в конце подлинной исходной документации авторы пакетов могут быть предпочтительнее. Или для того, чтобы развернуть его: вы используете новую функцию, но старую документацию, которая ее не отражает. Попробуйте написать базовый пакет с Rcpp, то есть приступить к нему с другого конца.
Наконец, есть список рассылки...
Ответ 4
Я изменил policyEconomist и Calimo ответы на коды, которые создают пакет из одного простого файла Rcpp.
package_name = "abc"
fpath_cpp_func = "C:/temp/cppfile.cpp" #a file generates no errors in soruceCpp(fpath_cpp_func)
path_build = "c:/r/rtoolbox"
r_exe = "C:/r/r-3.3.1/bin/r.exe"
#convert one file Rcpp .cpp file to a package
require(Rcpp)
setwd(path_build)
#create package skeleton , new folder
Rcpp.package.skeleton(package_name)
#copy cpp file to src/
file.copy(fpath_cpp_func , file.path(package_name,"src"))
#copy other R codes to R/ (optional)
#file.copy(fpath_R_func, file.path(package_name,"R"))
#compileAttributes()
compileAttributes(pkgdir = package_name)
#to generate manual, edit .Rd files in man/
#build, create 'abc_1.0.tar.gz'
cmd = paste0( '"' ,r_exe, '"', " CMD build ", package_name)
shell(cmd)
#check,
#cmd_check = paste0( '"' ,r_exe, '"', " CMD check ", package_name) #to generate manual
cmd_check = paste0( '"' ,r_exe, '"', " CMD check ", package_name, " --no-manual") #no manual
shell(cmd_check)
#install
install.packages(paste0(package_name, "_1.0.tar.gz"))
#load package
#require(package_name, character.only=TRUE)