Пакет R с файлами .c и .cpp с Rcpp - PullRequest
0 голосов
/ 02 января 2019

Я пытаюсь создать пакет R, который содержит как код C (в виде файлов .c), так и код C ++ (в виде файлов .cpp), используя пакет Rcpp в качестве зависимости.

У меня есть пара вопросов.

  1. Во-первых, возможно ли это сделать? Можно ли вызывать сценарии C и C ++, которые находятся в одном пакете R?
  2. Если предыдущее возможно, то как правильно зарегистрировать функции в сценариях C и C ++.

Чтобы помочь с этим, я создал небольшой пример, который доступен на моей странице GitHub (https://github.com/tpbilton/testrcpp). Я использовал Rcpp.package.skeleton("testrcpp") для инициализации пакета и добавил некоторые функции (из этого урока https://cran.r -project.org / web / packages / Rcpp / vignettes / Rcpp-ввод.pdf ) и затем запустил Rcpp::compileAttributes(). Я установил пакет, и функция c ++ convolve_cpp работает нормально, но convolve_c не зарегистрирован, и я не знаю, как это сделать правильно, и мои попытки зарегистрировать обе функции ни к чему не привели.

Ответы [ 2 ]

0 голосов
/ 02 января 2019

Во-первых, возможно ли это сделать?Можно ли вызывать скрипты C и C ++, которые находятся в одном и том же пакете R?

Да. Rcpp очень широко использует API R C .(cf Раздел 1.6.4 Переносимый код C и C ++ из Запись расширений R .

Если возможно предыдущее, как тогда правильно зарегистрироватьфункции в сценариях C и C ++.

В идеале, только поверхностные аспекты из сценария C ++ . В противном случае вы застряли при написании клея.

IЯ использовал этот подход. В посте подробно описываются небольшие изменения. Рабочий пример можно найти за пределами сайта по адресу:

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


Короче, мы "создадим заголовочный файл для определений функций и включим его в код C . Оттуда мы создадим третий файл в C ++ и экспортируем эту функцию в R с использованием _Rcpp.

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

Наконец, мы включаем код C , используя extern в нашем C ++ *Файл 1057 *, в котором имя функции в 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;
}
0 голосов
/ 02 января 2019

Помогает отступить и пересмотреть.Рассмотрим два пакета:

  • convolve_c, которые вы пишете вручную как пакет C only в 'long-form' и выполняете все вручную, включая ручную инициализацию и регистрацию
  • convolve_cpp, который вы пишете с помощью Rcpp - и compileAttributes() и другие инструменты сделают все для вас.

По сути, ваш вопрос составляет , а также часть C сделана для вас Rcpp и так просто не работает.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> 
...