Ненавязчивое расширение Rcpp с помощью Rcpp::особенности::Экспортер

#r #rcpp #r-package #s-expression

Вопрос:

Я пишу пакет Rcpp с классами, содержащими базовые объекты Rcpp, которые ненавязчиво экспортируются в типы C (см. Расширение Rcpp от Eddelbuettel и Франсуа и эту полезную виньетку от coatless).

Это означает , что если у меня есть класс foo::bar и конструктор для foo::bar данного a SEXP , я могу вызвать функцию, подобную этой:

 //[[Rcpp::export]]
void func(foo::bar object){
   // do something
}
 

Моя проблема в том, что это sourceCpp("foobar.cpp") работает точно так, как ожидалось, но R CMD build жалуется на RcppExports.cpp отсутствие в файле объявлений для этого нового класса.

Вот минимальный воспроизводимый пример, содержащий foo::bar класс, содержащий один Rcpp::NumericVector . Он компилируется с sourceCpp() , но пакет не будет собран, потому foobar что не найден в RcppExports.cpp :

foobar.cpp:

 #include <RcppCommon.h>

// forward declare class
namespace foo {
    class bar;
}

// forward declare Rcpp::as<> Exporter
template <>
class Rcpp::traits::Exporter<foo::bar>;

#include <Rcpp.h>

// now fully declare class, since Rcpp objects 
//   are now loaded from Rcpp.h
namespace foo {
    class bar {
        public:
        Rcpp::NumericVector x;
        bar(Rcpp::NumericVector x) : x(x) {};
    };
}

// now fully declare Rcpp exporter, since we can
//   deal with Rcpp objects from Rcpp.h
namespace Rcpp {
    namespace traits {
        template <>
        class Exporter<foo::bar> {
            Rcpp::NumericVector x_;

            public:
            Exporter(SEXP x) : x_(x) {}

            foo::bar get() {
                return foo::bar(x_);
            }
        };
    }
}

//[[Rcpp::export]]
Rcpp::NumericVector test(foo::baramp; A) {
    return A.x;
}
 

Конкретные ошибки, которые я получаю при запуске R CMD check , все в src/RcppExports.cpp файле:

  • «фу» не было объявлено
  • «А» не было объявлено в этой области
  • «foo» не был объявлен в этой области
  • ‘аргумент шаблона 1 недопустим
  • ‘квалифицированный идентификатор в декларации перед «А»

Что должно произойти, чтобы пакет создавался точно так sourceCpp же ?

Спасибо за любые решения!

Ответ №1:

RcppExports.cpp файл отсутствующих объявлений для этого нового класса.

См.виньетку Атрибутов. Пользовательское поведение заключается в том, чтобы заголовок назывался пакетом (т. Е. foo.h для пакета foo ) и/или foo-types.h ), который будет добавлен в сгенерированный RcppExports.cpp .

Это задокументировано, но скрыто слишком эффективно :-/ Это тоже здесь, так что мы могли бы закрыть это как обман, но у меня нет времени искать это сейчас.

Комментарии:

1. Спасибо @DirkEddelbuettel! Действительно, хорошо задокументировано, но эффективно скрыто. Это потрясающе, и это действительно помогает с пакетом RcppSparse, над которым я работаю, и функциональный заголовок класса разреженной матрицы Rcpp теперь должен быть в CRAN в кратчайшие сроки.

2. Классно! И поздравляю с получением RcppClock-хотя у меня не было возможности взглянуть на него.

3. всегда рад написать виньетку для RcppGallery на RcppClock. На самом деле довольно забавно сравнивать фрагменты кода Rcpp с помощью таймеров C и получать результаты прямо в сеансе R во время выполнения 🙂

4. Однажды я «запустил» (в смысле начального тестового пакета) два тестовых пакета, основанных на инфраструктуре C . Но кажется трудной / невозможной / реальной работой вернуть данные «обратно в R», поэтому я так и не продвинулся. Первоначальные исследования все еще находятся на github в rcppgeiger и rcppbenchmark … Так что да, если у вас есть интересное замечание или иллюстрация, Галерея всегда готова вас выслушать 🙂

5. Только что отправил пиар на RcppGallery. Чтобы получить данные «обратно в R», я просто использую Rcpp для создания data.frame (ну, класса S3) в качестве глобальной переменной в среде R. На самом деле это довольно просто-оберните часы std::chrono::high_resolution на стороне C , напишите несколько методов S3 на стороне R. Похоже, вы пошли немного глубже с rcppgeiger на стороне сравнительного анализа, я уверен, что можно сделать лучше, чем просто перенос вызовов в C chrono.