创建std::shared_ptr对象并将其返回到R端(Rcpp)

lymgl2op  于 2023-05-11  发布在  其他
关注(0)|答案(4)|浏览(127)

我正在尝试使用Rcpp为C++脚本编写R绑定。其中一个函数需要std::shared_ptr object。我发现很难初始化std::shared_ptr obj并将其作为Rcpp::XPtr对象返回到R端。
我试过(最小的例子):

#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)'

有什么办法吗?还是我做错了

hujrc8aj

hujrc8aj1#

来自@d3coy的答案几乎包含了所有的信息。Rcpp::XPtr是一个模板智能指针类,它的参数是被指针类,而不是它指向的类。因此,Rcpp::XPtr<std::shared_ptr<TestClass>>将是指向std::shared_ptr<TestClass>*的智能指针。注意*,这是重要的一点。
shared_ptr超出作用域时,如果它是原始指针的最后一个保持器,那么原始指针可能会被删除。这绝对不是你想要的
相反,您可以使用new创建一个原始指针,并将其馈送给XPtr。当垃圾收集器通过XPtr收集R对象时,这个指针将得到delete d,这是你在处理外部指针时通常想要的。
我知道目前的指导方针是尽可能多地使用make_uniquemake_shared而不是new,但在这种情况下,您需要new。聪明来自XPtr,如果你把它和shared_ptr混合在一起,它们会互相妨碍。

8cdiaqws

8cdiaqws2#

我怀疑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;
};
piah890a

piah890a3#

感谢所有的提示和“指针”。不知道这是否是正确的回应方式,但我想我会输入我找到的解决方案。
关键是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);
};

在R中,我可以做:

test_container <- get_test_obj()
do_something_with_shared_ptr_testclass(test_container)
mzmfm0qo

mzmfm0qo4#

只是为了补充这个主题(我花了几天的时间,因为我是一个C++新手),下面的代码是基于Bart上面的解决方案,测试和确认。如果有一天有人有更简单的解决方案,请让我知道!
PS:Dirk的资源是了不起的(感谢您的所有工作),但我没有设法找到任何基于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
*/

相关问题