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

Tim*_*ton 3 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没有注册,我不知道如何正确地做到这一点,我试图注册这两个函数的尝试无处可去。

Dir*_*tel 7

它有助于退后一步进行回顾。考虑两个包:

  • convolve_c您可以将其作为“长格式”的纯 C 包手动编写,并手动完成所有操作,包括手工初始化和注册
  • convolve_cpp您可以使用 Rcpp 编写,并且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);
}
Run Code Online (Sandbox Code Playgroud)

然后运行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> 
Run Code Online (Sandbox Code Playgroud)


coa*_*ess 5

首先,是否真的有可能做到这一点?可以调用同一 R 包中的 C 脚本和 C++ 脚本吗?

是的。Rcpp非常著名地利用了RC API。(参见第1.6.4节便携式C和C ++代码写作R附加

如果前一个是可能的,那么如何在 C 和 C++ 脚本中正确注册函数。

理想情况下,只从C++脚本表面方面。否则,你会被困在写胶水里。

我采取了这种方法。这篇文章继续详细介绍这些细微的变化。可以在异地找到一个工作示例:

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


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

convolve_in_c.h

在这里,我们使用了一个包含保护 via #ifndefand#define来确保如果我们多次重用头文件,函数定义不会重复。

#ifndef CONVOLVE_C_H
#define CONVOLVE_C_H

SEXP convolve_c(SEXP a, SEXP b);

#endif /* CONVOLVE_C_H */
Run Code Online (Sandbox Code Playgroud)

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;
}
Run Code Online (Sandbox Code Playgroud)

convolve_from_c_to_rcpp.cpp

最后,我们结合了Ç使用代码extern我们中的C ++文件,必须在函数名C ++与对齐Ç联动。此外,我们操作数据类型从SEXPNumericVector

#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;
}
Run Code Online (Sandbox Code Playgroud)