Создание объекта 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 с этим. Этот указатель получит deleted, когда сборщик мусора собирает объект 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
*/
Другие вопросы по тегам