带有Rcpp的.c和.cpp文件的R软件包

时间:2019-01-02 00:26:00

标签: c rcpp r-package

我正在尝试使用Rcpp包作为依赖项来构建一个包含C(以.c文件的形式)和C ++代码(以.cpp文件的形式)的R包。

我有几个问题。

  1. 首先,实际上有可能这样做吗?可以调用同一R包中的C脚本和C ++脚本吗?
  2. 如果可能的话,那么如何在C和C ++脚本中正确注册功能。

为此,我建立了一个小示例,该示例在我的GitHub页面(https://github.com/tpbilton/testrcpp)上可用。我已经使用Rcpp.package.skeleton("testrcpp")来初始化程序包并添加了一些功能(来自本教程https://cran.r-project.org/web/packages/Rcpp/vignettes/Rcpp-introduction.pdf),然后运行了Rcpp::compileAttributes()。我安装了该软件包,并且c ++函数convolve_cpp可以正常运行,但是convolve_c未注册,我也不知道如何正确执行此操作,尝试注册这两个函数的尝试也没有成功。

2 个答案:

答案 0 :(得分:4)

它有助于退后一步并进行查看。考虑两个软件包:

  • convolve_c,您以“长格式”将其作为纯C包手工编写,并手动完成所有操作,包括手工进行初始化和注册
  • 您使用Rcpp编写的
  • convolve_cpp –和compileAttributes()和其他工具可以为您完成一切。

从本质上讲,您质疑的是是否还有Rcpp为您完成的C部分,但那样行不通。 Rcpp不会“看到”您的src/convolvec.c,因此不会添加它。

但是,如果您查看这些功能注册的工作方式-可以查看成千上万的CRAN软件包,以及要仔细阅读的手册-则可以 手动填写。

或者您可以平底锅。只需在C ++中添加第三个函数即可调用您的C函数。 Rcpp会处理所有事情,您已完成。您的选择:简单或详尽。

编辑:为了更加明确,选项3包括添加

#include <Rcpp.h>

extern "C" SEXP convolve_c(SEXP a, SEXP b);

// [[Rcpp::export]]
SEXP callCconvolve(SEXP a, SEXP b) {
    return convolve_c(a, b);
}

然后运行compileAttributes(),一切都很好。出于通常的原因,混合和匹配也可以使用,但是需要更多工作-有关所有详细信息,请参见“编写R扩展”。

它起作用的例证:

R> library(testrcpp)
R> a <- as.double(1:10)
R> b <- as.double(10:1)
R> identical(convolve_cpp(a, b), callCconvolve(a, b))
[1] TRUE
R> 

答案 1 :(得分:2)

  

首先,实际上有可能这样做吗?可以调用同一R包中的C脚本和C ++脚本吗?

是的。 Rcpp 非常著名,它利用了 R C API。 (参见Section 1.6.4 Portable C and C++ code中的Writing R Extensions

  

如果可能的话,那么如何在C和C ++脚本中正确注册功能。

理想情况下,仅是 C ++ 脚本的表面特征。否则,您将无法书写胶水。

我采用了这种方法。该帖子继续详细介绍了细微的变化。一个工作示例可以在以下位置找到:

https://github.com/r-pkg-examples/rcpp-and-c


简而言之,我们将为函数定义创建头文件,并将其包含在 C 代码中。从那里,我们将创建第三个文件,该文件位于 C ++ 中,并使用_Rcpp将函数导出到 R 中。

convolve_in_c.h

这里,我们通过#ifndef#define使用了包含保护,以确保如果多次重复使用头文件,则不会重复使用函数定义。

#ifndef CONVOLVE_C_H
#define CONVOLVE_C_H

SEXP convolve_c(SEXP a, SEXP b);

#endif /* CONVOLVE_C_H */

convolve_in_c.c

现在,让我们修改文件以允许使用自定义标头。

#include <R.h>
#include <Rinternals.h>

// Incorporate our header
#include "convolve_in_c.h"

SEXP convolve_c(SEXP a, SEXP b) {
  int na, nb, nab;
  double *xa, *xb, *xab;
  SEXP ab;
  a = PROTECT(coerceVector(a, REALSXP));
  b = PROTECT(coerceVector(b, REALSXP));
  na = length(a); nb = length(b);
  nab = na + nb - 1;
  ab = PROTECT(allocVector(REALSXP, nab));
  xa = REAL(a); xb = REAL(b); xab = REAL(ab);
  for(int i = 0; i < nab; i++)
    xab[i] = 0.0;
  for(int i = 0; i < na; i++)
    for(int j = 0; j < nb; j++)
      xab[i + j] += xa[i] * xb[j];
  UNPROTECT(3);
  return ab;
}

convolve_from_c_to_rcpp.cpp

最后,我们在{em> C ++ 文件中使用extern合并 C 代码,以使 C ++ 中的函数名称与 C 链接。另外,我们将数据类型从SEXP更改为NumericVector

#include "Rcpp.h"

// Define the method signature

#ifdef __cplusplus
extern "C" {
#endif

#include "convolve_in_c.h"

#ifdef __cplusplus
}
#endif

//' Call C function from Rcpp
//' 
//' Uses the convolve_c function inside of a C++ routine by Rcpp.
//' 
//' @param a,b A `numeric` vector.
//' 
//' @return 
//' A `numeric` vector of length \eqn{N_a + N_b}.
//' 
//' @examples
//' 
//' convolve_from_c(1:5, 5:1)
//' 
//' @export
// [[Rcpp::export]]
Rcpp::NumericVector convolve_from_c(const Rcpp::NumericVector& a,
                                    const Rcpp::NumericVector& b) {

  // Compute the result in _C_ from _C++_.
  SEXP ab = convolve_c(a, b);

  // Cast as an _Rcpp_ NumericVector 
  Rcpp::NumericVector result( ab );

  // Alternatively:
  // Rcpp::NumericVector result( convolve_c(a, b) );

  // Return result
  return result;
}