问题
I'm trying to write a R bindings for a C++ script using Rcpp. One of the functions expects a std::shared_ptr object
. I find it difficult to initialize the std::shared_ptr
obj and return it to the R side as a Rcpp::XPtr
object.
I've tried (minimal example):
#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;
};
But get the following error:
no matching function for call to 'Rcpp::XPtr<std::shared_ptr<TestClass> >::XPtr(std::shared_ptr<TestClass>, bool)'
Any ideas on how to do this? Or am I going about this wrong?
回答1:
I doubt that the R side would expect to have a std::shared_ptr passed to it. I'm assuming that some other code in your source relies on std::shared_ptr, but you want to pass the inner raw pointer to R. I'm also assuming that the std::shared_ptr's lifetime is more appropriately managed in your code, since the code as presented would make the std::shared_ptr fall out of scope after the function and crash on the next dereference.
In any case, if you wanted to just pass the inner raw pointer to R, you'd do it like this (contrived):
//[[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;
};
回答2:
The answer from @d3coy has pretty much all the information. Rcpp::XPtr
is a template smart pointer class, it's parameter is the pointee class, not the class it points to. So a Rcpp::XPtr<std::shared_ptr<TestClass>>
would be a smart pointer to a std::shared_ptr<TestClass>*
. Note the *
, that's the important bit.
When a shared_ptr
goes out of scope, if that's the last holder of the raw pointer, the raw pointer might get deleted. That's definitely not what you want.
Instead you can create a raw pointer with new
and feed XPtr
with that. This pointer will get delete
d when the garbage colector collects the R object undelying the XPtr
, which is what you usually want when you deal with external pointers.
I know the current guidelines is to use make_unique
and make_shared
as much as possible instead of new
but in this case you need new
. The smartness comes from XPtr
, if you mix it with shared_ptr
they will get in the way of each other.
回答3:
Thanks for all the tips and 'pointers'. Not sure if this is the correct way to respond, but I thought I'd enter the solution that I found.
The point is that the R side indeed doesn't necessarily require the std::shared_ptr
. However I'm interfacing with an existing C++ library. I need to instantiate a std::shared_ptr<TestClass>
of the library (using R) and feed in back into the C++ algorithm which expects a std::shared_ptr<TestClass>
.
The way I solved it is as follows (example and untested):
#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);
};
Then in R I can do:
test_container <- get_test_obj()
do_something_with_shared_ptr_testclass(test_container)
来源:https://stackoverflow.com/questions/26217102/creating-a-stdshared-ptr-object-and-returning-it-to-the-r-side-rcpp