Fwd: [serialization] is the weak_ptr parent kept inside an iarchive
Hello, I was running some tests involving std::weak_ptr using boost serialization, and I realized that when I read a std::weak_ptr from an archive that doesn't contain the "parent" std::share_ptr, the behavior of the read pointer is "hard to understand". Here is an example : #include <cassert> #include <sstream> #include <memory> #include <boost/archive/binary_iarchive.hpp> #include <boost/archive/binary_oarchive.hpp> #include <boost/serialization/export.hpp> #include <boost/serialization/shared_ptr.hpp> #include <boost/serialization/weak_ptr.hpp> class MyInt { public: MyInt() : value(0) { } int value; template<class Archive> void serialize(Archive &ar, unsigned int) { ar & value; } }; int main() { std::stringstream data; { std::shared_ptr<MyInt> shared = std::make_shared<MyInt>(); assert(shared.use_count() == 1); shared->value = 12; /* Write a weak_ptr */ { std::weak_ptr<MyInt> weak(shared); assert(shared.use_count() == 1); boost::archive::binary_oarchive ar(data); ar << weak; assert(shared.use_count() == 1); } assert(shared.use_count() == 1); /* Read a weak_ptr */ std::weak_ptr<MyInt> weak; { boost::archive::binary_iarchive ar(data); ar >> weak; assert(shared.use_count() == 1); assert(weak.use_count() == 1); { std::shared_ptr<MyInt> loaded = weak.lock(); assert(loaded); /* It looks good */ assert(loaded->value == 12); /* Let's see... */ assert(loaded != shared); shared->value = 42; assert(loaded->value == 12); /* So another shared_ptr exists somewhere... */ assert(shared.use_count() == 1); assert(loaded.use_count() == 2); assert(weak.use_count() == 2); } /* But where ? */ assert(shared.use_count() == 1); assert(weak.use_count() == 1); } /* Let's see */ assert(shared.use_count() == 1); assert(weak.use_count() == 0 /* weak.expired() */); /* Hmm, was it inside the archive itself ? */ } /* Let's try again, we need the input stream to be back to the beginning */ data.seekg(0); std::weak_ptr<MyInt> weak; { boost::archive::binary_iarchive ar(data); assert(weak.use_count() == 0); ar >> weak; assert(weak.use_count() == 1); /* It looks fine */ } assert(weak.use_count() == 0); /* aw, on a second thought, it really doesn't */ return 0; } I am building and running this using g++-9.3.0 with boost-1.72.0-1. Clearly, the weak_ptr gets itself a shared_ptr "somewhere" as it can be used after being read. I was wondering "where it is stored and how to access it, if possible, without some `weak.lock()` call ?" It looks like the shared_ptr gets destroyed at the same time as the archive, so I guess it is cleaned up thanks to the delete_created_pointers method when the iarchive is destroyed : does delete_created_pointers actually contains also shared_ptr ? As far as I can see in the code serialization/weak_ptr.hpp, we actually save then load a std::shared_ptr, so I was expecting the weak_ptr to expire as soon as the call returns : since it is not, there *is* another reference. Now, I know I must save the shared_ptr in the archive myself to have my own reference and to get all weak_ptr running properly. But I came across this use case and I can't take it out of my mind, so if you have an explanation, I'm all ears. Thank you
participants (1)
-
ypotin