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?
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:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With