Создание объекта std::shared_ptr и возврат его в сторону R (Rcpp)
Я пытаюсь написать привязки R для сценария C++, используя Rcpp. Одна из функций ожидает std::shared_ptr object
, Мне трудно инициализировать std::shared_ptr
obj и вернуть его в сторону R как Rcpp::XPtr
объект.
Я пробовал (минимальный пример):
#include <iostream>
#include <memory>
#include <Rcpp.h>
using namespace Rcpp;
using std::cout;
class TestClass {
public:
int value;
TestClass(int initial_val) {
value = initial_val;
};
};
//[[Rcpp::export]]
SEXP get_test_obj() {
Rcpp::XPtr<std::shared_ptr<TestClass>> ptr(std::make_shared<TestClass>(5), true);
return ptr;
};
Но получите следующую ошибку:
no matching function for call to 'Rcpp::XPtr<std::shared_ptr<TestClass> >::XPtr(std::shared_ptr<TestClass>, bool)'
Есть идеи, как это сделать? Или я об этом не так?
4 ответа
Я сомневаюсь, что сторона R будет ожидать, что ему будет передан std::shared_ptr. Я предполагаю, что некоторый другой код в вашем источнике полагается на std::shared_ptr, но вы хотите передать внутренний необработанный указатель на R. Я также предполагаю, что время жизни std::shared_ptr более подходящим образом управляется в вашем коде, поскольку представленный код приведет к тому, что std::shared_ptr выйдет из области видимости после функции и завершится аварийным завершением при следующем разыменовании.
В любом случае, если вы хотите просто передать внутренний необработанный указатель на R, вы сделаете это так (надумано):
//[[Rcpp::export]]
SEXP get_test_obj() {
std::shared_ptr<TestClass> s_ptr = std::make_shared<TestClass>(5);
Rcpp::XPtr<TestClass> x_ptr(s_ptr.get(), true);
return x_ptr;
};
Ответ от @d3coy содержит почти всю информацию. Rcpp::XPtr
является классом интеллектуального указателя шаблона, его параметром является класс pointee, а не класс, на который он указывает. Так что Rcpp::XPtr<std::shared_ptr<TestClass>>
будет умным указателем на std::shared_ptr<TestClass>*
, Обратите внимание *
это важный бит.
Когда shared_ptr
выходит из области видимости, если это последний держатель необработанного указателя, необработанный указатель может быть удален. Это определенно не то, что вы хотите.
Вместо этого вы можете создать необработанный указатель с new
и кормить XPtr
с этим. Этот указатель получит delete
d, когда сборщик мусора собирает объект R, не зависящий от XPtr
Это то, что вы обычно хотите, когда имеете дело с внешними указателями.
Я знаю, что текущие рекомендации использовать make_unique
а также make_shared
как можно больше вместо new
но в этом случае вам нужно new
, Умность исходит от XPtr
, если вы смешаете это с shared_ptr
они будут мешать друг другу.
Спасибо за все советы и "указатели". Не уверен, что это правильный способ ответа, но я подумал, что смогу найти решение, которое нашел.
Дело в том, что сторона R действительно не обязательно требует std::shared_ptr
, Однако я взаимодействую с существующей библиотекой C++. Мне нужно создать экземпляр std::shared_ptr<TestClass>
библиотеки (с использованием R) и обратно в алгоритм C++, который ожидает std::shared_ptr<TestClass>
,
Я решил это следующим образом (пример и непроверенный):
#include <iostream>
#include <memory>
#include <Rcpp.h>
using namespace Rcpp;
using std::cout;
class TestClass {
public:
int value;
TestClass(int initial_val): value(initial_val) {}
};
class TestClassContainer {
public
std::shared_ptr<TestClass> test_class_obj;
TestClassContainer(): test_class_obj(std::make_shared<TestClass>()) {}
};
//[[Rcpp::export]]
SEXP get_test_obj() {
Rcpp::XPtr<TestClassContainer> ptr(new TestClassContainer(), true);
return ptr;
};
//[[Rcpp::export]]
SEXP do_something_with_shared_ptr_testclass(SEXP test_container_obj) {
Rcpp::XPtr<ResourceContainer> test_ptr(test_container_obj);
ExternalLib::do_function(test_container_obj->test_class_obj);
};
Тогда в РИ можно сделать:
test_container <- get_test_obj()
do_something_with_shared_ptr_testclass(test_container)
Просто чтобы дополнить эту тему (мне это потребовалось пару дней моей жизни, так как я новичок в C++): приведенный ниже код был основан на приведенном выше решении Барта, протестирован и подтвержден. Если когда-нибудь кто-нибудь предложит более простое решение, пожалуйста, дайте мне знать!
PS: Ресурсы Дирка замечательны (спасибо за всю вашу работу), но мне не удалось найти никаких пакетов на основе Rcpp, решающих эту проблему =/
#include <Rcpp.h>
#include <memory>
using namespace Rcpp;
// A simple class for testing ----------------------------
class A{
public:
int value;
A(int x);
};
A::A(int x) {this->value = x + 10;}
using APtr = std::shared_ptr<A>;
class AWrapper{
public:
APtr a_wrap;
AWrapper(int x): a_wrap(new A(x)){};
};
// The regular way to access an instance of a class using XPtr ----------------------------
// Everything is fine here, nothing to do.
// [[Rcpp::export]]
XPtr<A> A_constructor(int x) {
A *a = new A(x);
XPtr<A> out(a, true);
return out;
}
// [[Rcpp::export]]
int A_test(XPtr<A> x) {
return x->value;
}
// THE PROBLEM ----------------------------
// A given C++ lib expect to receive an shared_ptr instead of a class instance itself.
// [[Rcpp::export]]
XPtr<APtr> APtr_constructor(int x) {
APtr a = std::make_shared<A>(x);
std::cout << a->value << std::endl;
XPtr<APtr> out_ptr(&a, true); // <--------------- how do I wrap 'a' here??
return out_ptr;
}
// [[Rcpp::export]]
int APtr_test(XPtr<APtr> x) {
return x->get()->value; // <--------------- how do I access the A instance pointed by APtr x (which was wrapped by XPtr)?
}
// Wrapper solution ----------------------------
// Using an auxiliary class to store a APtr and the XPtr an instance of it.
// [[Rcpp::export]]
XPtr<AWrapper> AWrapper_constructor(int x) {
AWrapper *a_w = new AWrapper(x);
XPtr<AWrapper> out_ptr(a_w, true);
return out_ptr;
}
// [[Rcpp::export]]
int AWrapper_test(XPtr<AWrapper> x) {
return x->a_wrap->value;
}
/*** R
# Working correctly ------------------------------------
a <- A_constructor(42)
A_test(a)
# > [1] 52
# NOT working ------------------------------------------
a_ptr <- APtr_constructor(42)
APtr_test(a_ptr)
# > [1] -1652502752 # garbage, might vary
# working ------------------------------------------
a_w <- AWrapper_constructor(42)
AWrapper_test(a_w)
# > [1] 52
*/