|
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
Library):
#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
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk