就地修改自定义类型 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”事物时,我通常通过确保“收集器”来解决问题,这里您的 objectsglobal 并持续。一个(非常基本的)解决方案是一个静态指针,它有一些辅助函数来 a) 初始化,b) 添加一个 object,c) 获取一个 object(通过键或位置),当然d) 最后展开/清理。这仍然有点棘手,因为您想确保收集器没有多个副本(静态部分有帮助)。

您需要了解 R 对象的工作原理(Rcpp 是一个包装器)。关键是

  1. R对象都是指针(SEXP是指向SEXPREC的指针)

  2. 当您 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
}