#include <iostream> #include <set> #include <boost/utility.hpp> #include <boost/shared_ptr.hpp>
using namespace std; using namespace boost;
struct NodeImpl;
NodeImpl* NodeImpl_Create(int id);
struct Node { Node(int id) : pimpl_(NodeImpl_Create(id)) { } Node(const Node& n) : pimpl_(n.pimpl_) { } Node() { } void Connect(const Node& n); private: shared_ptr<NodeImpl> pimpl_; friend bool operator<(const Node& n1, const Node& n2); };
bool operator<(const Node& n1, const Node& n2) { return n1.pimpl_ < n2.pimpl_; }
struct NodeImpl : noncopyable { NodeImpl(int id) : id_(id) { cout << id_ << " lives\n"; } ~NodeImpl() { cout << id_ << " dies\n"; } private: int id_; set<Node> nodes_; friend struct Node; };
NodeImpl* NodeImpl_Create(int id) { return new NodeImpl(id); }
void Node::Connect(const Node& n) { pimpl_->nodes_.insert(n); }
int main() { { Node n1(1);
{ Node n2(2), n3(3), n4(4);
n1.Connect(n2); n2.Connect(n2); n3.Connect(n3); n4.Connect(n4); }
cout << "n1 points to the cluster {n2, n3, n4}\n"; }
cout << "n2, n3, n4 remain alive. That sucks.\n";
void free_unreachable_objects(); free_unreachable_objects();
cout << "Done\n"; }
Running it, you'll see that free_unreachable_objects() fails to find/delete the dangling n2, n3 and n4.
Investigating, I determined that the reason is this: in order for sp_collector to do its magic, it must always be the case that shared_ptr's are contained directly within things pointed to by other shared_ptr's. In my example, this invariant is violated, because I'm storing Node's in a set. A Node contains a shared_ptr. You can have a Node directly in a NodeImpl, no problem. But you can't have a Node in a disembodied bit of memory, as produced by set to store its contents.
In the following modification, I change NodeImpl so that it contains the the shared_ptr's directly:
26a27,28 > const int MAX_NODES = 10; > 28c30 < NodeImpl(int id) : id_(id) { cout << id_ << " lives\n"; } --- > NodeImpl(int id) : id_(id), nnodes_(0) { cout << id_ << " lives\n"; } 32c34,35 < set<Node> nodes_; --- > Node nodes_[MAX_NODES]; > int nnodes_; 39c42,43 < pimpl_->nodes_.insert(n); --- > if (pimpl_->nnodes_ < MAX_NODES) > pimpl_->nodes_[pimpl_->nnodes_++] = n;
Now, sp_collector succeeds in finding & deleting the stale objects. Unfortunately, it means that I don't get to use the standard containers. All I get to use is a rotten array. Boo hoo!
And so, sp_collector suffers the same problem as shared_cyclic_ptr, and requires the same remedy: that is, it is necessary to be able to "tag" a container as being one which might hold "internal" shared_ptr's.
Any interest in modifying shared_ptr to support something like this?
- Chuck
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost