简体   繁体   English

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

[英]R package with both .c and .cpp files with Rcpp

I'm trying to build an R package which contains both C (in the form of .c files) and C++ code (in the form of .cpp files) using the Rcpp package as a dependency. 我正在尝试使用Rcpp包作为依赖项来构建一个包含C(以.c文件的形式)和C ++代码(以.cpp文件的形式)的R包。

I have a couple of questions. 我有一些问题。

  1. First, is it actually possible to do this? 首先,实际上有可能这样做吗? Can one call C scripts and C++ scripts that are in the same R package? 可以调用同一R包中的C脚本和C ++脚本吗?
  2. If the previous is possible, how then does one properly register the functions in the C and C++ scripts. 如果可能的话,那么如何在C和C ++脚本中正确注册功能。

To help with this, I have set up a little example which is available on my GitHub page ( https://github.com/tpbilton/testrcpp ). 为了解决这个问题,我建立了一个小例子,可以在我的GitHub页面( https://github.com/tpbilton/testrcpp )上找到。 I have used Rcpp.package.skeleton("testrcpp") to initialize the package and added some functions (from this tutorial https://cran.r-project.org/web/packages/Rcpp/vignettes/Rcpp-introduction.pdf ) and then ran Rcpp::compileAttributes() . 我已经使用Rcpp.package.skeleton("testrcpp")来初始化程序包并添加了一些功能(来自本教程https://cran.r-project.org/web/packages/Rcpp/vignettes/Rcpp-introduction.pdf ),然后运行Rcpp::compileAttributes() I installed the package and the c++ function convolve_cpp works fine but the convolve_c is not registered and I have no idea how to do this properly and my attempts at trying to register both functions have gone nowhere. 我安装了该软件包,并且c ++函数convolve_cpp可以正常运行,但是convolve_c没有注册,我也不知道如何正确执行此操作,尝试注册这两个函数的尝试也没有成功。

It helps to step back and review. 它有助于退后一步并进行审查。 Consider two packages: 考虑两个软件包:

  • convolve_c which you write by hand as a C only package in 'long-form' and do everything manually, including handcrafting the initialization and registration 您将convolve_c编写为“长格式”的仅C包,并手动完成所有操作,包括手工进行初始化和注册
  • convolve_cpp which you write using Rcpp -- and compileAttributes() and other tools do everything for you. convolve_cpp您编写使用RCPP -和compileAttributes()和其他工具为你做的一切。

In essence, you question amount to also having the C part done for you by Rcpp and it just doesn't work that way. 从本质上讲,您质疑Rcpp是否也为您完成了C部分 ,但是那样行不通。 Rcpp does not 'see' your src/convolvec.c so it won't add it. Rcpp不会“看到”您的src/convolvec.c convolvec.c,因此不会添加它。

But if you look at how these function registrations work -- thousand of CRAN packages to look at, and a manual to peruse -- then you can fill it by hand. 但是,如果您查看这些功能注册的工作原理(要查看的数千个CRAN软件包和要阅读的手册),那么您可以手工填写。

Or you could punt. 或者您可以平底锅。 Just add a third function, in C++, which calls your C function. 只需在C ++中添加第三个函数即可调用您的C函数。 Rcpp will take care of everything, and you're done. Rcpp会处理所有事情,您已完成。 Your choice: easy, or elaborate. 您的选择:简单或详尽。

Edit: To be more explicit, option 3 consists of adding 编辑:更明确地说,选项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);
}

Then run compileAttributes() and all is good. 然后运行compileAttributes() ,一切都很好。 The mixing and matching will work too for the usual reason but is more work -- see "Writing R Extensions" for all the details. 由于通常的原因,混合和匹配也可以使用,但是需要更多工作-有关所有详细信息,请参见“编写R扩展”。

Illustration of it working: 工作原理图:

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> 

First, is it actually possible to do this? 首先,实际上有可能这样做吗? Can one call C scripts and C++ scripts that are in the same R package? 可以调用同一R包中的C脚本和C ++脚本吗?

Yes. 是。 Rcpp very famously is taking advantage of R 's C API. Rcpp非常著名地利用了RC API。 (cf Section 1.6.4 Portable C and C++ code of Writing R Extensions . (请参阅第1.6.4节编写R扩展的 可移植C和C ++代码 ”。

If the previous is possible, how then does one properly register the functions in the C and C++ scripts. 如果可能的话,那么如何在C和C ++脚本中正确注册功能。

Ideally, only surface aspects from the C++ script. 理想情况下,仅表面上使用C ++脚本编写。 Otherwise, you're stuck writing the glue. 否则,您将无法书写胶水。

I've taken this approach. 我采用了这种方法。 The post goes on to detail the slight changes. 该帖子继续详细介绍了细微的变化。 A working example can be found off-site at: 一个工作示例可以在以下位置找到:

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


In short, we'll create a header file for the function definitions and include it with the C code. 简而言之,我们将为函数定义创建头文件,并将其包含在C代码中。 From there, we'll create a third file that is in C++ and export that function into R using _Rcpp. 从那里,我们将创建第三个C ++文件,并使用_Rcpp将函数导出到R中。

convolve_in_c.h convolve_in_c.h

Here we use an inclusion guard via #ifndef and #define to ensure the function definitions are not repeated if we reuse the header file multiple times. 在这里,我们通过#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 convolve_in_c.c

Now, let's modify the file to allow for our custom header. 现在,让我们修改文件以允许我们的自定义标头。

#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 convolve_from_c_to_rcpp.cpp

Finally, we incorporate the C code using extern within our C++ file to have the function name in C++ align with the C linkage. 最后,我们使用纳入C代码extern我们的C ++内部文件在C函数名++C链接对齐。 In addition, we manipulate the data type from SEXP to NumericVector . 另外,我们处理从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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM