从 Rcpp 函数 return 指向“新”对象的指针的正确方法
Proper way to return a pointer to a `new` object from an Rcpp function
考虑 1) 具有潜在大内存打印的自定义 class,以及 2) 执行一些预处理的顶级函数,然后创建并 returns 我们的新对象自定义 class。为避免不必要的按值复制,该函数分配对象并 return 改为指向它。
基于 ,似乎 return 指向新创建对象的指针的正确方法是用 Rcpp::XPtr<>
包装它。然而,R 然后将它有效地视为 externalptr
,我正在努力寻找使用现代 RCPP_EXPOSED_CLASS
和 RCPP_MODULE
做事方式的正确方法。
另一种方法是 return 原始指针。但是我不能 100% 确定对象内存是否已正确清理。我 运行 valgrind
测试内存泄漏,但没有发现。但是,谁来清理? 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")
;
}
在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<>
是否是 returning 指针的正确方法,如果是这样,我如何让 R 将结果视为 Double
,而不是 externalptr
?或者,如果 return 原始指针不会导致内存问题,谁来清理函数创建的对象?
我认为分开看不同的方法是有意义的。这样区分就更清楚了。请注意,这与 Rcpp 模块小插图中的讨论非常相似。
使用 Rcpp::XPtr
时,您有自己的 class 并为每个要公开的方法提供导出的 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 中,对象只是一个 "pointer"。如果你想要更好的东西,你可以在 R 端添加一个 S4/RC/R6/... class 。
将外部指针包装到 R 端的 class 中是使用 Rcpp 模块免费获得的:
#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::as
和 Rcpp::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 模块都注册了一个调用对象析构函数的默认终结器。如果需要,您还可以添加自定义终结器。
我发现很难对其中一种方法提出建议。也许最好在一些简单的例子中尝试它们中的每一个,看看你发现哪个更自然地使用。
考虑 1) 具有潜在大内存打印的自定义 class,以及 2) 执行一些预处理的顶级函数,然后创建并 returns 我们的新对象自定义 class。为避免不必要的按值复制,该函数分配对象并 return 改为指向它。
基于 Rcpp::XPtr<>
包装它。然而,R 然后将它有效地视为 externalptr
,我正在努力寻找使用现代 RCPP_EXPOSED_CLASS
和 RCPP_MODULE
做事方式的正确方法。
另一种方法是 return 原始指针。但是我不能 100% 确定对象内存是否已正确清理。我 运行 valgrind
测试内存泄漏,但没有发现。但是,谁来清理? 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")
;
}
在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<>
是否是 returning 指针的正确方法,如果是这样,我如何让 R 将结果视为 Double
,而不是 externalptr
?或者,如果 return 原始指针不会导致内存问题,谁来清理函数创建的对象?
我认为分开看不同的方法是有意义的。这样区分就更清楚了。请注意,这与 Rcpp 模块小插图中的讨论非常相似。
使用 Rcpp::XPtr
时,您有自己的 class 并为每个要公开的方法提供导出的 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 中,对象只是一个 "pointer"。如果你想要更好的东西,你可以在 R 端添加一个 S4/RC/R6/... class 。
将外部指针包装到 R 端的 class 中是使用 Rcpp 模块免费获得的:
#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::as
和 Rcpp::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 模块都注册了一个调用对象析构函数的默认终结器。如果需要,您还可以添加自定义终结器。
我发现很难对其中一种方法提出建议。也许最好在一些简单的例子中尝试它们中的每一个,看看你发现哪个更自然地使用。