c++ - 从 Rcpp 函数返回指向 `new` 对象的指针的正确方法

标签 c++ r rcpp

考虑 1) 一个具有潜在大内存打印的自定义类,以及 2) 一个执行一些预处理的顶级函数,然后创建并返回自定义类的新对象。为了避免不必要的按值复制,该函数分配对象并返回指向它的指针。

基于 previous discussion ,似乎返回指向新创建对象的指针的正确方法是将其用 Rcpp::XPtr<> 包装。 。然而,R 随后将其有效地视为 externalptr ,我正在努力寻找用现代 RCPP_EXPOSED_CLASS 来转换它的正确方法。和RCPP_MODULE做事的方式。

另一种方法是返回原始指针。但我并不能 100% 确定对象内存是否得到正确清理。我跑了valgrind测试内存泄漏,但没有发现任何内存泄漏。然而,谁来清理呢? R?

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

在 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

我的问题是 Rcpp::Xptr<> 是否是返回指针的正确方法,如果是这样,我如何让 R 将结果视为 Double ,不是externalptr ?或者,如果返回原始指针不会导致内存问题,那么谁来清理函数创建的对象?

最佳答案

我认为单独研究不同的方法是有意义的。这使得区别更加清晰。请注意,这与 Rcpp 模块小插图中的讨论非常相似。

使用 Rcpp::XPtr 时,您拥有自己的类,并为您想要公开的每个方法提供导出的 C++ 函数:

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

输出:

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

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

> squareDouble(d2)
[1] 29.16

请注意,在 R 中,对象只是一个“指针”。如果你想要更好的东西,你可以在 R 端添加 S4/RC/R6/... 类。

使用 Rcpp 模块可以免费将外部指针包装到 R 端的类中:

#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()
*/

输出:

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

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

> d1$square()
[1] 29.16

还支持在 C++ 中使用工厂方法代替构造函数,但在 R 端具有相同的用法:

#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()
*/

输出:

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

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

> d1$square()
[1] 29.16

最后,如果您想将 R 端工厂函数与 Rcpp 模块结合起来,RCPP_EXPOSED_CLASS 会派上用场,因为这会创建 Rcpp::asRcpp::wrap 扩展需要在 R 和 C++ 之间来回传递对象。工厂可以像您一样通过 function 导出,也可以使用 Rcpp 属性导出,我觉得这更自然:

#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()
*/

输出:

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

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

> d1$square()
[1] 29.16

关于清理:Rcpp::XPtr 和 Rcpp 模块都会注册一个调用对象析构函数的默认终结器。如果需要,您还可以添加自定义终结器。

我发现很难对其中一种方法提出建议。也许最好在一些简单的例子中尝试它们中的每一个,看看你发现哪种使用起来更自然。

关于c++ - 从 Rcpp 函数返回指向 `new` 对象的指针的正确方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59384221/

相关文章:

r - 在其他 c++ 函数中使用 c++ 函数(在带有 Rcpp 的 R 包中)

Rcpp 和 R : pass by reference

RCPP错误: undefined symbol: _ZTIN4Rcpp7RObjectE on package install

c++ - R 中的 Rcpp Quicksort 错误?

C++11 移动和重新分配

c++ - boost 绑定(bind)功能

c++ - 将 std::string 转换为 nsstring 问题

c++ - 如何分配 C 结构内联?

r - 有没有办法在 R 中显示 .gif 文件?

r - 选择输入 : Have Multiple = TRUE and filter based off that