Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper way to return a pointer to a `new` object from an Rcpp function

Tags:

c++

r

rcpp

Consider 1) a custom class with a potentially large memory print, and 2) a top-level function that performs some pre-processing, then creates and returns a new object of our custom class. To avoid unnecessary copying by value, the function allocates the object and returns a pointer to it instead.

Based on a previous discussion, it seems that the proper way to return a pointer to a newly-created object is to wrap it with Rcpp::XPtr<>. However, R then sees it effectively as externalptr, and I am struggling to find the proper way to cast it with the modern RCPP_EXPOSED_CLASS and RCPP_MODULE way of doing things.

The alternative is to return the raw pointer. But then I'm not 100% certain that the object memory gets properly cleaned up. I ran valgrind to test for memory leaks, and it didn't find any. However, who does the clean up? R?

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")
    ;
}

In 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

My question is whether Rcpp::Xptr<> is the proper way of returning pointers, and if so, how do I get R to see the result as Double, not externalptr? Alternatively, if returning a raw pointer doesn't cause memory issues, who cleans up the object that the function creates?

like image 350
Artem Sokolov Avatar asked Dec 18 '19 01:12

Artem Sokolov


People also ask

Can you return a Pointer from a function?

We can pass pointers to the function as well as return pointer from a function. But it is not recommended to return the address of a local variable outside the function as it goes out of scope after function returns.

How do you return a pointer from a function in C++?

Return Function Pointer From Function: To return a function pointer from a function, the return type of function should be a pointer to another function. But the compiler doesn't accept such a return type for a function, so we need to define a type that represents that particular function pointer.

How do you call a function from a pointer object?

Using a pointer-to-member-function to call a function Calling the member function on an object using a pointer-to-member-function result = (object. *pointer_name)(arguments); or calling with a pointer to the object result = (object_ptr->*pointer_name)(arguments);


1 Answers

I think it makes sense to look at the different approaches separately. This makes the distinction clearer. Note that this is quite similar to the discussion in the Rcpp Modules vignette.

When using Rcpp::XPtr you have your class and provide exported C++ functions for every method you want to expose:

#include <Rcpp.h>

// Custom class
class Double {
public:
    Double( double v ) : value(v) {}
    double square() {return value*value;}
private:
    double value;
};

// [[Rcpp::export]]
Rcpp::XPtr<Double> makeDouble(double x) {
    Double* pd = new Double(x);
    Rcpp::XPtr<Double> ptr(pd);
    return ptr;
}

// [[Rcpp::export]]
double squareDouble(Rcpp::XPtr<Double> x) {
    return x.get()->square();
}

/***R
(d2 <- makeDouble(5.4))
squareDouble(d2)
*/

Output:

> Rcpp::sourceCpp('59384221/xptr.cpp')

> (d2 <- makeDouble(5.4))
<pointer: 0x560366699b50>

> squareDouble(d2)
[1] 29.16

Note that in R the object is only a "pointer". You could add an S4/RC/R6/... class on the R side if you want something nicer.

Wrapping the external pointer into a class on the R side is something you get for free by using Rcpp modules:

#include <Rcpp.h>

// Custom class
class Double {
public:
    Double( double v ) : value(v) {}
    double square() {return value*value;}
private:
    double value;
};

RCPP_MODULE(double_cpp) {
    using namespace Rcpp;

    class_<Double>("Double")
        .constructor<double>("Wraps a double")
        .method("square", &Double::square, "square of value")
    ;
}

/***R
(d1 <- new(Double, 5.4))
d1$square()
*/

Output:

> Rcpp::sourceCpp('59384221/modules.cpp')

> (d1 <- new(Double, 5.4))
C++ object <0x560366452eb0> of class 'Double' <0x56036480f320>

> d1$square()
[1] 29.16

It is also supported to use a factory method instead of a constructor in C++ but with identical usage on the R side:

#include <Rcpp.h>

// Custom class
class Double {
public:
    Double( double v ) : value(v) {}
    double square() {return value*value;}
private:
    double value;
};

Double* makeDouble( double x ) {
    Double* pd = new Double(x);
    return pd;
}

RCPP_MODULE(double_cpp) {
    using namespace Rcpp;

    class_<Double>("Double")
        .factory<double>(makeDouble, "Wraps a double")
        .method("square", &Double::square, "square of value")
    ;
}

/***R
(d1 <- new(Double, 5.4))
d1$square()
*/

Output:

> Rcpp::sourceCpp('59384221/modules-factory.cpp')

> (d1 <- new(Double, 5.4))
C++ object <0x5603665aab80> of class 'Double' <0x5603666eaae0>

> d1$square()
[1] 29.16

Finally, RCPP_EXPOSED_CLASS comes in handy if you want to combine an R side factory function with Rcpp Modules, since this creates the Rcpp::as and Rcpp::wrap extensions needed to pass objects back an forth between R and C++. The factory could be exported via function as you did or using Rcpp Attributes, which I find more natural:

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

// [[Rcpp::export]]
Double makeDouble( double x ) {
    Double d(x);
    return d;
}

RCPP_MODULE(double_cpp) {
    using namespace Rcpp;

    class_<Double>("Double")
        .method("square", &Double::square, "square of value")
    ;
}

/***R
(d1 <- makeDouble(5.4))
d1$square()
*/

Output:

> Rcpp::sourceCpp('59384221/modules-expose.cpp')

> (d1 <- makeDouble(5.4))
C++ object <0x560366ebee10> of class 'Double' <0x560363d5f440>

> d1$square()
[1] 29.16

Concerning clean-up: Both Rcpp::XPtr and Rcpp Modules register a default finalizer that calls the object's destructor. You can also add a custom finalizer if needed.

I find it difficult to give a recommendation for one of these approaches. Maybe it is best to try each of them on some simple example and see what you find more natural to use.

like image 136
Ralf Stubner Avatar answered Nov 01 '22 18:11

Ralf Stubner