【问题标题】:Using custom Rcpp class in rcpp-function in an R-package. Both class and function are defined in the same .cpp file在 R 包中的 rcpp-function 中使用自定义 Rcpp 类。类和函数都定义在同一个 .cpp 文件中
【发布时间】:2020-09-28 16:26:41
【问题描述】:

我正在尝试使用 Rcpp 加速 R 数组维度上的循环。 数组类来自rcpp-gallery:(https://github.com/RcppCore/rcpp-gallery/blob/gh-pages/src/2014-03-21-simple-array-class.Rmd

// [[Rcpp::depends(RcppArmadillo)]]
#include <RcppArmadillo.h>
#include <Rcpp.h>
using namespace Rcpp ;

/*
  ******************************************************************************
  Offset and Array classes based on code by Romain Francois copied from
http://comments.gmane.org/gmane.comp.lang.r.rcpp/5932 on 2014-01-07.
******************************************************************************
  */
  
  class Offset{
    private:
      IntegerVector dim ;
    
    public:
      Offset( IntegerVector dim ) : dim(dim) {}
    
    int operator()( IntegerVector ind ){
      int ret = ind[0] ;
      int offset = 1 ;
      for(int d=1; d < dim.size(); d++) {
        offset = offset * dim[d-1] ; 
        ret = ret + ind[d] * offset ;
      }
      return ret ;
    } ;
    
    IntegerVector getDims() const {
      return(dim) ;
    };
    
  } ;

class Array : public NumericVector {
  private:
    // NumericVector value;
  Offset dims ;
  
  public:
    //Rcpp:as
  Array( SEXP x) : NumericVector(x), 
  dims( (IntegerVector)((RObject)x).attr("dim") ) {}
  
  Array( NumericVector x,  Offset d ): NumericVector(x), 
  dims(d) {}
  
  Array( Dimension d ): NumericVector( d ), dims( d ) {}
  
  IntegerVector getDims() const {
    return(dims.getDims());
  };
  
  NumericVector getValue()  const {
    return(*((NumericVector*)(this)));
  };
  
  inline double& operator()( IntegerVector ind) {
    int vecind = dims(ind);
    NumericVector value = this->getValue();  
    return value(vecind);
  } ;
  
  // change dims without changing order of elements (!= aperm)
  void resize(IntegerVector newdim) {
    int n = std::accumulate((this->getDims()).begin(), (this->getDims()).end(), 1, 
                            std::multiplies<int>());
    int nnew = std::accumulate(newdim.begin(), newdim.end(), 1, 
                               std::multiplies<int>());
    if(n != nnew)  stop("old and new old dimensions don't match.");
    this->dims = Offset(newdim);
  } ;
  
} ;

namespace Rcpp {
  // wrap(): converter from Array to an R array
  template <> SEXP wrap(const Array& A) {
    IntegerVector dims = A.getDims();
    //Dimension dims = A.getDims();
    Vector<REALSXP> x = A;
    x.attr( "dim" ) = wrap(dims);
    return x; 
  }
}


// [[Rcpp::export]]
int runloop(Array& myarray) {
  IntegerVector myarrayDims  = myarray.getDims();
  for (int j = 0; j < myarrayDims[1]; j++) {
    for (int k = 0; k < myarrayDims[2]; k++) {
      for (int l = 0; l < myarrayDims[3]; l++) {
        for (int i = 0; i < myarrayDims[0]; i++) {
          myarray({i, j, k, l}) = i + j + k +l;
        }
      }
    }
  }
  return 0;
}

从交互式 R 会话中获取和执行此代码按预期工作。

library(Rcpp)
sourceCpp(file.path(".", "minimalExample.cpp"))

inputArray <- array(data = rnorm(10^4), dim = c(10, 10, 10, 10))
runloop(inputArray)
inputArray

但是当我将 .cpp 文件移动到我的 R 包结构的 src 文件夹中时,构建失败,因为无法识别类 Array (see screenshot of build errors)

构建失败后的RcppExports.cpp内容如下:

// Generated by using Rcpp::compileAttributes() -> do not edit by hand
// Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393

#include <RcppArmadillo.h>
#include <Rcpp.h>

using namespace Rcpp;

// runloop
int runloop(Array& myarray);
RcppExport SEXP _foobar_runloop(SEXP myarraySEXP) {
BEGIN_RCPP
    Rcpp::RObject rcpp_result_gen;
    Rcpp::RNGScope rcpp_rngScope_gen;
    Rcpp::traits::input_parameter< Array& >::type myarray(myarraySEXP);
    rcpp_result_gen = Rcpp::wrap(runloop(myarray));
    return rcpp_result_gen;
END_RCPP
}

static const R_CallMethodDef CallEntries[] = {
    {"_foobar_runloop", (DL_FUNC) &_foobar_runloop, 1},
    {NULL, NULL, 0}
};

RcppExport void R_init_foobar(DllInfo *dll) {
    R_registerRoutines(dll, NULL, CallEntries, NULL, NULL);
    R_useDynamicSymbols(dll, FALSE);
}

如何让我的简单循环运行? 谢谢:)

ps:我在 R-Studio 中的 sessionInfo() 返回:

R version 3.6.1 (2019-07-05)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 19041)

Matrix products: default

locale:
[1] LC_COLLATE=English_Germany.1252  LC_CTYPE=English_Germany.1252    LC_MONETARY=English_Germany.1252 LC_NUMERIC=C                    
[5] LC_TIME=English_Germany.1252    

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
[1] Rcpp_1.0.5

loaded via a namespace (and not attached):
[1] compiler_3.6.1 tools_3.6.1  

【问题讨论】:

    标签: r arrays rcpp


    【解决方案1】:

    有句话说,要先走路才能跑。您错过了步行和冲刺之间的慢跑步骤;-)。

    将代码编译到您源代码的单个函数中与将它放在一个完整的包中相同。你想做的事情可以做到并且已经做到了。简而言之,虽然您在 source 文件中编写了代码,但它也需要存在于 生成的文件 src/RcppExports.cpp

    您现在缺少的一个技巧在 生成代码中的类型部分的 Rcpp 属性小插图中进行了描述,我引用了(编辑掉 markdown/latex)

    生成代码中的类型

    在某些情况下,生成的 C++ 函数的签名 RcppExports.cpp 可能有超出核心的额外类型要求 标准库和Rcpp 类型(例如CharacterVectorNumericVector 等)。示例可能包括便利类型定义, as/wrap 处理程序,用于在自定义类型和 SEXP 或类型之间进行封送处理 由 Rcpp XPtr 模板包裹。

    在这种情况下,您可以创建一个包含这些类型定义的头文件 (定义内联或包含其他标头)并具有此标头 文件自动包含在RcppExports.cpp 中。标头命名为 约定pkgname_types 自动包含在 生成的 C++ 代码。例如,如果您的包名为fastcode 那么以下任何头文件都将自动包含在 RcppExports.cpp:

    src/fastcode_types.h
    src/fastcode_types.hpp
    inst/include/fastcode_types.h
    inst/include/fastcode_types.hpp
    

    [...]

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-12
      • 1970-01-01
      • 2014-06-25
      • 2021-02-25
      • 2020-11-13
      • 1970-01-01
      相关资源
      最近更新 更多