就地修改自定义类型 Rcpp::List 个对象
modifying custom type Rcpp::List objects in place
我正在努力弄清楚如何修改 Rcpp::List 中的自定义类型列表元素;下面是一些代码来说明我的问题(使用 Rcpp 模块)。
#include <Rcpp.h>
class Base {
public:
Rcpp::NumericVector data;
Base() {
this->data = Rcpp::NumericVector();
}
void append(double x) {
data.push_back(x);
}
};
RCPP_EXPOSED_CLASS(Base)
class Container {
public:
Rcpp::List objects;
Container(Rcpp::List objects) {
this->objects = objects;
}
void append(double x) {
for (int i = 0; i < objects.length(); i++) {
// cannot modify in-place because objects[i] is a temporary!
const Base *obj = objects[i];
// try to outsmart the compiler by copying to non const - lvalue hell!
Base *ptr = obj;
ptr->append(x);
}
}
Base* at(int i) {
void *ptr = objects[i];
return (Base*) ptr;
}
};
RCPP_EXPOSED_CLASS(Container)
问题是我需要调用方法 Base::append
,因此需要获取指向第 i 个列表元素的非常量指针。因为 objects[i]
是一个临时对象,所以我不能定义一个非常量指针。在这里,我尝试复制 const 指针,但编译器抱怨无法使用 const 左值初始化非 const 指针(我想这意味着它意识到我试图智取它)。
我是否需要使用另一个(类型化的)集合来代替 Rcpp::List,或者我怎样才能让它工作?
我真的没有时间解决你的问题,但乍一看,它似乎与 objects
实例 local 到你的 object 遇到的问题与 C++ 生命周期范围有关,而不是与 Rcpp 接口有关。
当我想要或需要更永久的“collections”事物时,我通常通过确保“收集器”来解决问题,这里您的 objects
是 global 并持续。一个(非常基本的)解决方案是一个静态指针,它有一些辅助函数来 a) 初始化,b) 添加一个 object,c) 获取一个 object(通过键或位置),当然d) 最后展开/清理。这仍然有点棘手,因为您想确保收集器没有多个副本(静态部分有帮助)。
您需要了解 R 对象的工作原理(Rcpp 是一个包装器)。关键是
R对象都是指针(SEXP是指向SEXPREC的指针)
当您 push_back
指向向量时,您可能会更改底层指针。
因此不能保证您的 NumericVectors 在 push_back
之后引用同一个对象。
一种选择是使用引用构建您的 Base
class。这是一个例子。
sourceCpp("mycontainer.cpp")
x <- as.list(1:5)
mc <- new(Container, x)
mc$append(6)
print(x)
[[1]]
[1] 1 6
[[2]]
[1] 2 6
[[3]]
[1] 3 6
...
Rcpp 代码:
#include <Rcpp.h>
class Base {
public:
Rcpp::NumericVector & data;
Base(Rcpp::NumericVector & x) : data(x) {}
void append(double x) {
data.push_back(x);
}
};
RCPP_EXPOSED_CLASS(Base)
class Container {
public:
Rcpp::List objects;
Container(Rcpp::List objects) {
this->objects = objects;
}
void append(double x) {
for (int i = 0; i < objects.length(); i++) {
Rcpp::NumericVector objvec = objects[i];
Base obj( objvec );
obj.append(x);
objects[i] = objvec;
}
}
Rcpp::List output() {
return objects;
}
};
RCPP_EXPOSED_CLASS(Container)
RCPP_MODULE(Container){
using namespace Rcpp;
class_<Container>("Container")
.constructor< List >()
.method( "append", &Container::append )
.method( "output", &Container::output )
;
}
编辑:您可以看到指针 (SEXP) 在 push_back 上如何变化。
// [[Rcpp::export]]
void test() {
IntegerVector x(0);
std::cout << (void*) x.get__() << std::endl;
x[0] = 50;
std::cout << (void*) x.get__() << std::endl; // no change
x.push_back(1);
std::cout << (void*) x.get__() << std::endl; // address change
}
我正在努力弄清楚如何修改 Rcpp::List 中的自定义类型列表元素;下面是一些代码来说明我的问题(使用 Rcpp 模块)。
#include <Rcpp.h>
class Base {
public:
Rcpp::NumericVector data;
Base() {
this->data = Rcpp::NumericVector();
}
void append(double x) {
data.push_back(x);
}
};
RCPP_EXPOSED_CLASS(Base)
class Container {
public:
Rcpp::List objects;
Container(Rcpp::List objects) {
this->objects = objects;
}
void append(double x) {
for (int i = 0; i < objects.length(); i++) {
// cannot modify in-place because objects[i] is a temporary!
const Base *obj = objects[i];
// try to outsmart the compiler by copying to non const - lvalue hell!
Base *ptr = obj;
ptr->append(x);
}
}
Base* at(int i) {
void *ptr = objects[i];
return (Base*) ptr;
}
};
RCPP_EXPOSED_CLASS(Container)
问题是我需要调用方法 Base::append
,因此需要获取指向第 i 个列表元素的非常量指针。因为 objects[i]
是一个临时对象,所以我不能定义一个非常量指针。在这里,我尝试复制 const 指针,但编译器抱怨无法使用 const 左值初始化非 const 指针(我想这意味着它意识到我试图智取它)。
我是否需要使用另一个(类型化的)集合来代替 Rcpp::List,或者我怎样才能让它工作?
我真的没有时间解决你的问题,但乍一看,它似乎与 objects
实例 local 到你的 object 遇到的问题与 C++ 生命周期范围有关,而不是与 Rcpp 接口有关。
当我想要或需要更永久的“collections”事物时,我通常通过确保“收集器”来解决问题,这里您的 objects
是 global 并持续。一个(非常基本的)解决方案是一个静态指针,它有一些辅助函数来 a) 初始化,b) 添加一个 object,c) 获取一个 object(通过键或位置),当然d) 最后展开/清理。这仍然有点棘手,因为您想确保收集器没有多个副本(静态部分有帮助)。
您需要了解 R 对象的工作原理(Rcpp 是一个包装器)。关键是
R对象都是指针(SEXP是指向SEXPREC的指针)
当您
push_back
指向向量时,您可能会更改底层指针。
因此不能保证您的 NumericVectors 在 push_back
之后引用同一个对象。
一种选择是使用引用构建您的 Base
class。这是一个例子。
sourceCpp("mycontainer.cpp")
x <- as.list(1:5)
mc <- new(Container, x)
mc$append(6)
print(x)
[[1]]
[1] 1 6
[[2]]
[1] 2 6
[[3]]
[1] 3 6
...
Rcpp 代码:
#include <Rcpp.h>
class Base {
public:
Rcpp::NumericVector & data;
Base(Rcpp::NumericVector & x) : data(x) {}
void append(double x) {
data.push_back(x);
}
};
RCPP_EXPOSED_CLASS(Base)
class Container {
public:
Rcpp::List objects;
Container(Rcpp::List objects) {
this->objects = objects;
}
void append(double x) {
for (int i = 0; i < objects.length(); i++) {
Rcpp::NumericVector objvec = objects[i];
Base obj( objvec );
obj.append(x);
objects[i] = objvec;
}
}
Rcpp::List output() {
return objects;
}
};
RCPP_EXPOSED_CLASS(Container)
RCPP_MODULE(Container){
using namespace Rcpp;
class_<Container>("Container")
.constructor< List >()
.method( "append", &Container::append )
.method( "output", &Container::output )
;
}
编辑:您可以看到指针 (SEXP) 在 push_back 上如何变化。
// [[Rcpp::export]]
void test() {
IntegerVector x(0);
std::cout << (void*) x.get__() << std::endl;
x[0] = 50;
std::cout << (void*) x.get__() << std::endl; // no change
x.push_back(1);
std::cout << (void*) x.get__() << std::endl; // address change
}