如何(轻松)将c ++函数包装为易于安装的R"包"

How to (easily) wrap a c++ function unto an easy to install R 'package'

本文关键字:易于 安装 包装 函数 轻松 如何      更新时间:2023-10-16

假设我有一个c++代码(见下面的简单示例)。我想让它很容易为期刊裁判安装/运行它。

所以我想最简单的方法就是把它扭曲成一个简化的类似于R包的tar.gz文件,以便裁判可以安装它只需调用install。打包到本地的。tar.gz文件。

这样做的原因是我不知道是什么机器裁判在吸毒,但我很确定裁判会知道如何安装一个R包,所以它更容易把我的代码扭曲成一个R"包"——或者至少,和它足够相似的东西通过简单调用install.package()安装。

前面一个问题的答案似乎表明了这一点确实是可能的。我遵循了其中的建议用我的CPP代码创建了一个/src目录(如下所示)(见下图)和一辆Makevars。Win文件包含:

## This assume that we can call Rscript to ask Rcpp about its locations
## Use the R_HOME indirection to support installations of multiple R version
#PKG_LIBS = $(shell $(R_HOME)/bin/Rscript.exe -e "Rcpp:::LdFlags()")
PKG_CPPFLAGS = -I../inst/include -I.
PKG_CXXFLAGS = -DEIGEN_DONT_PARALLELIZE $(SHLIB_OPENMP_CXXFLAGS)
PKG_LIBS = $(shell $(R_HOME)/bin/Rscript.exe -e "Rcpp:::LdFlags()") $(SHLIB_OPENMP_CXXFLAGS)

和包含

的Makevars文件
## Use the R_HOME indirection to support installations of multiple R version
#PKG_LIBS = `$(R_HOME)/bin/Rscript -e "Rcpp:::LdFlags()"` 
# This was created by RcppEigen.package.skeleton, but the R script that is 
# called creates error message:
# PKG_CPPFLAGS = `$(R_HOME)/bin/Rscript -e "RcppEigen:::RcppEigenCxxFlags()"`
PKG_CPPFLAGS = -I../inst/include
PKG_CXXFLAGS = -DEIGEN_DONT_PARALLELIZE $(SHLIB_OPENMP_CXXFLAGS)
PKG_LIBS = `$(R_HOME)/bin/Rscript -e "Rcpp:::LdFlags()"` $(SHLIB_OPENMP_CXXFLAGS)

。我只是按照SO帖子中的答案四处寻找这是在其他包中完成的(我还将RcppEigen添加到依赖项列表中,因为这保证了Eigen被安装在目标机器上)。我还创建了一个包含文件MSE的/R目录。R包含:

fx01<-function(x){
    x<-as.matrix(x)
    Dp<-rep(0,ncol(x))
    fit<-.C("mse",as.integer(nrow(x)),as.integer(ncol(x)),as.single(x),as.single(Dp))
    as.numeric(fit[[4]])
}

和一个空的/inst/include和一个包含最小(但有效). rd文件的/man目录。我添加了一个命名空间文件,其中包含:

import(Rcpp)
import(RcppEigen)
useDynLib(MySmallExample)

问题是:

  • c++函数编译/运行正常。有没有办法把它转换成一个R包,这样第三者可以很容易地安装/运行它。

下面是本例使用的c++代码。

#include <algorithm>
#include <cstdlib>
#include <ctime>
#include <functional>
#include <fstream>
#include <iostream>
#include <math.h>
#include <time.h>
#include <stdio.h>
#include <stdlib.h>
#include <sstream>
#include <vector>
#include <Eigen/Dense>
#include <Eigen/LU>
#include <Eigen/SVD>
using namespace std;
using namespace Eigen;
using Eigen::MatrixXf;
using Eigen::VectorXf;

float median(VectorXf& x) {
    int n=x.rows();
    int half=(n+1)/2;   
    half--;                 
    float med;
    nth_element(x.data(),x.data()+half,x.data()+x.size());  
    if((n%2)==1){
        med=x(half);
    } else {
        float tmp0=x(half);
        float tmp1=x.segment(half+1,half-1).minCoeff(); 
        med=0.5*(tmp0+tmp1);
    }
    return med;
}
VectorXf fx01(MatrixXf& x){
    int p=x.cols();
    int n=x.rows();
    VectorXf Recept(n);
    VectorXf Result(p);
    for(int i=0;i<p;i++){
        Recept=x.col(i);
        Result(i)=median(Recept);
    }
    return Result;
}
extern "C"{
    void mse(int* n,int* p,float* x,float* medsout){
        MatrixXf x_cen=Map<MatrixXf>(x,*n,*p);  
        VectorXf MedsOut=fx01(x_cen);
        Map<VectorXf>(medsout,*p)=MedsOut.array();
    }
}

你读过关于如何与R交互的"编写R扩展"手册吗?

您当然可以自由地在没有Rcpp的情况下这样做,但是我们编写Rcpp是为了我们使用,因为我们发现它使这些交换更容易。在CRAN上使用Rcpp的94个包似乎同意…

您正在使用Eigen,并且您希望将其打包给第三方("裁判")。现在,如果您要使用RcppEigen,您将确保Eigen存在于RcppEigen中。以你的工作,你不会…

另外,.C()是一个比.Call()更严格的接口。

拥有一个合适的DESCRIPTION文件非常重要。我用的是这个:

Package: MySmallExample
Type: Package
Title: MysmallExample
Version: 0.0.0
Date: 2012-12-24
Depends: Rcpp (>= 0.9.10)
Imports: RcppEigen (>= 0.2.0)
Suggests: mvtnorm
LinkingTo: Rcpp, RcppEigen
Description: A small minimal Package.
License: GPL (>= 2)
LazyLoad: yes
Authors@R: person("joe", "programer", email =
        "joe.programer@joe_inc.com", role = c("aut", "cre"))
Collate: 'MSE.R'
Packaged: 2012-12-24 12:34:56 UTC; andi
Author: joe programer [aut, cre]
Maintainer: joe programer <joe.programer@joe_inc.com>
Repository: CRAN
Date/Publication: 2012-12-24 12:34:56

最重要的似乎是'Collate:'字段:它应该正确列出/R目录下的所有.R文件。The Depends &;Imports字段也应该与命名空间文件一致。

文件命名空间包含一行

也是非常重要的
export("fx01","fx02")

,其中"fx01","fx02"为/R/*。R文件(在本例中为fx01)。

然后,我把整个东西打包成一个。tar.gz文件。和run

install.packages("MySmallExample.tar.gz",repos=NULL,type="source")

> install.packages("/MySmallExample.tar.gz",repos=NULL,type="source")
Installing package(s) into ‘/R/x86_64-pc-linux-gnu-library/2.15’
(as ‘lib’ is unspecified)
* installing *source* package ‘MySmallExample’ ...
** libs
g++ -I/usr/share/R/include -DNDEBUG -I../inst/include  -I"/R/x86_64-pc-linux-gnu-library/2.15/Rcpp/include" -I"/R/x86_64-pc-linux-gnu-library/2.15/RcppEigen/include"  -DEIGEN_DONT_PARALLELIZE -fopenmp -fpic  -O3 -pipe  -g  -c MSE.cpp -o MSE.o
g++ -shared -o MySmallExample.so MSE.o -L/R/x86_64-pc-linux-gnu-library/2.15/Rcpp/lib -lRcpp -Wl,-rpath,/R/x86_64-pc-linux-gnu-library/2.15/Rcpp/lib -fopenmp -L/usr/lib/R/lib -lR
installing to /R/x86_64-pc-linux-gnu-library/2.15/MySmallExample/libs
** R
** inst
** preparing package for lazy loading
** help
*** installing help indices
** building package indices
** testing if installed package can be loaded
* DONE (MySmallExample)