问题
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?
回答1:
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
来源:https://stackoverflow.com/questions/53132155/keep-xptr-for-multiple-sessions