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
#include
#include
#include
#include
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