Рассмотрим 1) пользовательский класс с потенциально большим объемом памяти и 2) функцию верхнего уровня, которая выполняет некоторую предварительную обработку, а затем создает и возвращает новый объект нашего пользовательского класса. Чтобы избежать ненужного копирования по значению, функция выделяет объект и вместо него возвращает указатель.
Исходя из предыдущего обсуждения , кажется, что правильный способ вернуть указатель на вновь созданный объект - это обернуть его Rcpp::XPtr<>
. Тем не менее, R тогда видит это эффективно externalptr
, и я изо всех сил пытаюсь найти правильный способ придать ему современность RCPP_EXPOSED_CLASS
и RCPP_MODULE
способ делать вещи.
Альтернатива - вернуть необработанный указатель. Но тогда я не уверен на 100%, что память объекта правильно очищена. Я побежал, valgrind
чтобы проверить на утечки памяти, и он не нашел. Однако кто занимается уборкой? Р?
test.cpp
#include <Rcpp.h>
// Custom class
class Double {
public:
Double( double v ) : value(v) {}
double square() {return value*value;}
private:
double value;
};
// Make the class visible
RCPP_EXPOSED_CLASS(Double)
// Option 1: returning raw pointer
Double* makeDouble( double x ) {
Double* pd = new Double(x);
return pd;
}
// Option 2: returning XPtr<>
SEXP makeDouble2( double x ) {
Double* pd = new Double(x);
Rcpp::XPtr<Double> ptr(pd);
return ptr;
}
RCPP_MODULE(double_cpp) {
using namespace Rcpp;
function( "makeDouble", &makeDouble );
function( "makeDouble2", &makeDouble2 );
class_<Double>("Double")
.constructor<double>("Wraps a double")
.method("square", &Double::square, "square of value")
;
}
В R
Rcpp::sourceCpp("test.cpp")
d1 <- makeDouble(5.4) # <-- who cleans this up???
# C++ object <0x56257d628e70> of class 'Double' <0x56257c69cf90>
d1$square()
# 29.16
d2 <- makeDouble2(2.3)
# <pointer: 0x56257d3c3cd0>
d2$square()
# Error in d2$square : object of type 'externalptr' is not subsettable
Мой вопрос заключается в том, является ли Rcpp::Xptr<>
правильный способ возврата указателей, и если да, то как мне получить R, чтобы увидеть результат как Double
, а не externalptr
? Альтернативно, если возврат необработанного указателя не вызывает проблем с памятью, кто очищает объект, который создает функция?
Rcpp::XPtr
создать внешний указатель из кода C ++. И вы хотите, чтобы это было сделано,double *
или какой бы ни была ваша полезная нагрузка. Здесь должны быть примеры, в Галерее, на GitHub ... Может быть, с мотивированным поиском вы можете найти что-то достаточно близкое?CustomClass*
. Настоящее приложение представляет собой пользовательскую структуру данных без эквивалента R, и все взаимодействия выполняются с помощью функциональности, предоставляемойRCPP_MODULE
. Самым близким совпадением, найденным в моем поиске, было сообщение 7 лет назад , где, похоже, мне нужно определитьtemplate <> CustomClass* as()
конвертер. Однако мне неясно, как оно должно взаимодействоватьRCPP_MODULE
иRCPP_EXPOSED_CLASS
, тем более, что я думал, что последнее уже определеноwrap()
иas()
.RCPP_EXPOSED_CLASS
иRCPP_MODULE
действительно ли способ сделать это? Я никогда не использовал и не видел это раньше.Ответы:
Я думаю, что имеет смысл взглянуть на разные подходы отдельно. Это делает различие более ясным. Обратите внимание, что это очень похоже на обсуждение в виньетке модулей Rcpp.
При использовании
Rcpp::XPtr
вы имеете свой класс и предоставляете экспортированные функции C ++ для каждого метода, который хотите представить:Вывод:
Обратите внимание, что в R объект является только «указателем». Вы можете добавить класс S4 / RC / R6 / ... на стороне R, если хотите что-то более приятное.
Обертывание внешнего указателя в класс на стороне R - это то, что вы получаете бесплатно с помощью модулей Rcpp:
Вывод:
Также поддерживается использование фабричного метода вместо конструктора в C ++, но с идентичным использованием на стороне R:
Вывод:
Наконец,
RCPP_EXPOSED_CLASS
очень удобно , если вы хотите совместить фабричную функцию R на стороне с Rcpp модулями, так как это создаетRcpp::as
иRcpp::wrap
расширение , необходимое для передачи объектов обратно в поступательном между R и C ++. Фабрика может быть экспортирована черезfunction
вас или через атрибуты Rcpp, что я считаю более естественным:Вывод:
Относительно очистки: Оба
Rcpp::XPtr
модуля и Rcpp Модули регистрируют финализатор по умолчанию, который вызывает деструктор объекта. Вы также можете добавить пользовательский финализатор, если это необходимо.Мне сложно дать рекомендацию по одному из этих подходов. Может быть, лучше попробовать каждый из них на простом примере и посмотреть, что вы считаете более естественным для использования.
источник
factory
это ключевой элемент разъема, которого мне не хватало.function
регистрирует ли также финализатор, или это толькоfactory
?class_<T>
и не зависит от того, как создается объект.