Boost logo

Boost :

From: Chuck Messenger (chuckm_at_[hidden])
Date: 2003-05-29 09:45:07

I tried out sp_collector.cpp. Here's my code (the Network Simulation

     #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);
         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"; }
         int id_;
         set<Node> nodes_;
         friend struct Node;

     NodeImpl* NodeImpl_Create(int id) { return new NodeImpl(id); }

     void Node::Connect(const Node& n) {

     int main() {
             Node n1(1);

                 Node n2(2), n3(3), n4(4);


             cout << "n1 points to the cluster {n2, n3, n4}\n";

         cout << "n2, n3, n4 remain alive. That sucks.\n";

         void 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:

> const int MAX_NODES = 10;
     < NodeImpl(int id) : id_(id) { cout << id_ << " lives\n"; }
> NodeImpl(int id) : id_(id), nnodes_(0) {
                                        cout << id_ << " lives\n"; }
     < set<Node> nodes_;
> Node nodes_[MAX_NODES];
> int nnodes_;
     < 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

Boost list run by bdawes at, gregod at, cpdaniel at, john at