Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Keep XPtr for multiple sessions

Tags:

c++

r

rcpp

I have a R function that creates a Primebase Cpp Class and then returns a XPtr<Primebase> pointer.

As the construction process takes a significant amount of time I'd like to save the instance of Primebase to my session, so that the next time I open up R I can directly access the Primebase instance.

Unfortunately the underlying Object gets deleted as soon as I close R and the XPtr turns into a null pointer.

Is there a way to prevent R from deleting the object or any other way to save the underlying object?

like image 893
Dames Avatar asked Jun 13 '26 20:06

Dames


1 Answers

The C++ object that is managed by Rcpp::Xptr is destroyed when the R session ends. If you want to save the object, you have to serialize it. One nice possibility is offered by the Rcereal package. The following example uses a trivial Primebase class with an artificial sleep in one constructor to simulate heavy processing during the construction. After checking the object's content, it is serialized and destroyed. Afterwards the object is deserialized and wrapped into an Xptr again. Note that deserialization is much cheaper than construction:

#include <Rcpp.h>
// [[Rcpp::plugins("cpp11")]]
// [[Rcpp::depends(Rcereal)]]
#include <cereal/archives/binary.hpp>
#include <chrono>
#include <fstream>
#include <thread>

class Primebase
{
private:
  int x;

public:
  Primebase() : x{0} {};
  Primebase(int x_) : x{x_} {std::this_thread::sleep_for(std::chrono::seconds(1));};

  int answer() {return x;}

  template <class Archive>
  void serialize(Archive & ar)
  {
    ar(x);
  }
};

// [[Rcpp::export]]
Rcpp::XPtr<Primebase> create(int x) {
  Primebase* instance = new Primebase(x);
  return Rcpp::XPtr<Primebase>(instance);
}


// [[Rcpp::export]]
int answer(Rcpp::XPtr<Primebase> xptr) {
  return xptr.get()->answer();
}

// [[Rcpp::export]]
void mySerialize(Rcpp::XPtr<Primebase> xptr, std::string filename) {
  std::ofstream os(filename, std::ios::binary);
  cereal::BinaryOutputArchive archive(os);
  archive(*xptr.get());
}

// [[Rcpp::export]]
Rcpp::XPtr<Primebase> myDeserialize(std::string filename) {
  std::ifstream is(filename, std::ios::binary);
  cereal::BinaryInputArchive archive(is);
  Primebase* instance = new Primebase;
  archive(*instance);
  return Rcpp::XPtr<Primebase>(instance);
}


/*** R
system.time(xptr <- create(42))
answer(xptr)
mySerialize(xptr, "test.cereal")
rm(xptr)
exists("xptr")
system.time(xptr <-myDeserialize("test.cereal")) 
answer(xptr)
*/

Output:

> system.time(xptr <- create(42))
   user  system elapsed 
  0.000   0.000   1.001 

> answer(xptr)
[1] 42

> mySerialize(xptr, "test.cereal")

> rm(xptr)

> exists("xptr")
[1] FALSE

> system.time(xptr <-myDeserialize("test.cereal")) 
   user  system elapsed 
      0       0       0 

> answer(xptr)
[1] 42

References:

  • Cereal documentation
  • Rcpp gallery
like image 151
Ralf Stubner Avatar answered Jun 15 '26 15:06

Ralf Stubner