我正在尝试使用 Rcpp 包作为依赖项构建一个包含 C(以 .c 文件的形式)和 C++ 代码(以 .cpp 文件的形式)的 R 包。
我有一些问题。
为了帮助解决这个问题,我在我的 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
没有注册,我不知道如何正确地做到这一点,我试图注册这两个函数的尝试无处可去。
它有助于退后一步进行回顾。考虑两个包:
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)
首先,是否真的有可能做到这一点?可以调用同一 R 包中的 C 脚本和 C++ 脚本吗?
是的。Rcpp非常著名地利用了R的C API。(参见第1.6.4节便携式C和C ++代码的写作R附加 。
如果前一个是可能的,那么如何在 C 和 C++ 脚本中正确注册函数。
理想情况下,只从C++脚本表面方面。否则,你会被困在写胶水里。
我采取了这种方法。这篇文章继续详细介绍这些细微的变化。可以在异地找到一个工作示例:
https://github.com/r-pkg-examples/rcpp-and-c
简而言之,我们将为函数定义创建一个头文件并将其包含在C代码中。从那里,我们将创建第三个C++文件,并使用 _Rcpp将该函数导出到R中。
在这里,我们使用了一个包含保护 via #ifndef
and#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)
现在,让我们修改文件以允许我们的自定义标头。
#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)
最后,我们结合了Ç使用代码extern
我们中的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;
}
Run Code Online (Sandbox Code Playgroud)