[shared_ptr] dealing with shared_ptr and cycles

Hi I have this simple program, that has a class with a shared pointer to another object of the same type: #include <boost/shared_ptr.hpp> #include <string> #include <iostream> using namespace std; class C; typedef boost::shared_ptr<C> CP; class C { private: string s; CP cp; public: C(const string &ss, CP c = CP()) : s(ss), cp(c) {} void set(CP c) { cp = c; } ~C() { cout << "deleting C" << endl; } }; int main() { CP cp = CP(new C("C")); CP cp2 = CP(new C("C2")); cp->set(cp2); //cp->set(cp); cout << "use_count: " << cp.use_count() << endl;; return 0; } if I remove the comment for cp->set(cp), i.e., I set the shared_pointer to itself, there's a cycle, and thus use_count returns 2, and the object cp is never deleted (shown also by using valgrind). This is a simple program just to reproduce a much more complex situation with a data structure that deals with mutual recursive references. I do not know in advance whether the shared pointer refers to the same object or to another different object (for instance I perform also cp->set(cp2)). Now, the documentation (http://www.boost.org/libs/smart_ptr/shared_ptr.htm) says that "Because the implementation uses reference counting, cycles of shared_ptr instances will not be reclaimed. For example, if main() holds a shared_ptr to A, which directly or indirectly holds a shared_ptr back to A, A's use count will be 2. Destruction of the original shared_ptr will leave A dangling with a use count of 1. Use weak_ptr to "break cycles."" However, it is not clear to me how to use weak_ptr in this context... can some one provide some clue, please? many thanks in advance Lorenzo

On 06/06/2005 08:58 AM, Lorenzo Bettini wrote:
Hi
I have this simple program, that has a class with a shared pointer to another object of the same type: [snip] I do not know in advance whether the shared pointer refers to the same object or to another different object (for instance I perform also cp->set(cp2)).
[snip]
However, it is not clear to me how to use weak_ptr in this context...
can some one provide some clue, please?
I don't know, but there's been a discussion on comp.lang.c++.moderated where Le Chaud Lapin has volunteered to convert code which used garbage collection (shared_ptr or otherwise) to one which didn't. Try posting there. The header for this post is: From: "Le Chaud Lapin" <unoriginal_username@yahoo.com> Newsgroups: comp.lang.c++.moderated Subject: Re: Pathology Of Bad Software Architecture Date: 6 Jun 2005 05:24:05 -0400

On Mon, Jun 06, 2005 at 03:58:41PM +0200, Lorenzo Bettini wrote:
However, it is not clear to me how to use weak_ptr in this context...
can some one provide some clue, please?
Well, for this context: class C; typedef boost::shared_ptr<C> CP; typedef boost::weak_ptr<C> WP; class C { private: string s; WP wp; public: C(const string &ss, CP c = CP()) : s(ss), wp(c) {} void set(CP c) { wp = c; } ~C() { cout << "destroying C" << endl; } }; Then main() would be unchanged. That might not work for anything less trivial than your example, since you'd need to ensure the shared_ptrs that own the objects are still around when you use the weak_ptrs. jon

Jonathan Wakely wrote:
On Mon, Jun 06, 2005 at 03:58:41PM +0200, Lorenzo Bettini wrote:
However, it is not clear to me how to use weak_ptr in this context...
can some one provide some clue, please?
Well, for this context:
class C;
typedef boost::shared_ptr<C> CP; typedef boost::weak_ptr<C> WP;
class C { private: string s; WP wp;
public: C(const string &ss, CP c = CP()) : s(ss), wp(c) {} void set(CP c) { wp = c; } ~C() { cout << "destroying C" << endl; } };
Then main() would be unchanged.
That might not work for anything less trivial than your example, since you'd need to ensure the shared_ptrs that own the objects are still around when you use the weak_ptrs.
indeed, it only works for that simple main: the main problem I had is just "ensure the shared_ptrs that own the objects are still around when you use the weak_ptrs.". Moreover, I had to deal with mutual dependences, and this makes the whole thing worse...

Lorenzo Bettini <bettini@dsi.unifi.it> writes:
Jonathan Wakely wrote:
On Mon, Jun 06, 2005 at 03:58:41PM +0200, Lorenzo Bettini wrote:
However, it is not clear to me how to use weak_ptr in this context...
can some one provide some clue, please? Well, for this context: class C; typedef boost::shared_ptr<C> CP; typedef boost::weak_ptr<C> WP; class C { private: string s; WP wp; public: C(const string &ss, CP c = CP()) : s(ss), wp(c) {} void set(CP c) { wp = c; } ~C() { cout << "destroying C" << endl; } }; Then main() would be unchanged. That might not work for anything less trivial than your example, since you'd need to ensure the shared_ptrs that own the objects are still around when you use the weak_ptrs.
indeed, it only works for that simple main: the main problem I had is just "ensure the shared_ptrs that own the objects are still around when you use the weak_ptrs.".
Moreover, I had to deal with mutual dependences, and this makes the whole thing worse...
Can you deal with deallocation of the whole network at once? It often works to maintain the nodes (C objects) in a container like a deque and let them link to one another with raw pointers. Otherwise you may be in need of a real GC. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
Can you deal with deallocation of the whole network at once? It often works to maintain the nodes (C objects) in a container like a deque and let them link to one another with raw pointers.
Otherwise you may be in need of a real GC.
I solved the problem in a (probably) not too elegant way: when I know that I'm creating objects with mutual dependences say A and B I store weak pointers, and I store a shared pointer to A and B into a list. As long as the list survives the weak pointers are still valid. When I want to deallocate everything, all I have to do is to deallocate the list. This worked, and also valgrind says there are no leaks... Lorenzo -- +-----------------------------------------------------+ | Lorenzo Bettini ICQ# lbetto, 16080134 | | PhD in Computer Science | | Dip. Sistemi e Informatica, Univ. di Firenze | | Florence - Italy (GNU/Linux User # 158233) | | Home Page : http://www.lorenzobettini.it | | http://music.dsi.unifi.it XKlaim language | | http://www.lorenzobettini.it/purple Cover Band | | http://www.gnu.org/software/src-highlite | | http://www.gnu.org/software/gengetopt | | http://www.lorenzobettini.it/software/gengen | | http://www.lorenzobettini.it/software/doublecpp | +-----------------------------------------------------+

On 06/07/2005 01:43 AM, Lorenzo Bettini wrote: [snip]
I solved the problem in a (probably) not too elegant way: when I know that I'm creating objects with mutual dependences say A and B I store weak pointers, and I store a shared pointer to A and B into a list. As long as the list survives the weak pointers are still valid. When I want to deallocate everything, all I have to do is to deallocate the list. This worked, and also valgrind says there are no leaks...
Well, I see how this solution will work for any graph because any graph can be represented by G=(V,A) where V is the set of vertices, and A, the directed edges (or arcs) is VxV; so, as long as you keep shared_ptr<v> for each v in A, and all the A's are weak_ptr's, then dereferencing an A will be valid. The question is, "is this practical?" The extra cost, is of course, V.size * size(shared_ptr<v>). Maybe this is what Le Chaud had in mind when he said "if containers are heavily employed" in one of his posts to comp.lang.c++.moderated.

Lorenzo Bettini <bettini@dsi.unifi.it> writes:
David Abrahams wrote:
Can you deal with deallocation of the whole network at once? It often works to maintain the nodes (C objects) in a container like a deque and let them link to one another with raw pointers. Otherwise you may be in need of a real GC.
I solved the problem in a (probably) not too elegant way: when I know that I'm creating objects with mutual dependences say A and B I store weak pointers, and I store a shared pointer to A and B into a list. As long as the list survives the weak pointers are still valid. When I want to deallocate everything, all I have to do is to deallocate the list. This worked, and also valgrind says there are no leaks...
The smart pointers may not be buying you much of anything in that case. -- Dave Abrahams Boost Consulting www.boost-consulting.com

David Abrahams wrote:
Lorenzo Bettini <bettini@dsi.unifi.it> writes:
David Abrahams wrote:
Can you deal with deallocation of the whole network at once? It often works to maintain the nodes (C objects) in a container like a deque and let them link to one another with raw pointers. Otherwise you may be in need of a real GC.
I solved the problem in a (probably) not too elegant way: when I know that I'm creating objects with mutual dependences say A and B I store weak pointers, and I store a shared pointer to A and B into a list. As long as the list survives the weak pointers are still valid. When I want to deallocate everything, all I have to do is to deallocate the list. This worked, and also valgrind says there are no leaks...
The smart pointers may not be buying you much of anything in that case.
so what could be an alternative? Lore -- +-----------------------------------------------------+ | Lorenzo Bettini ICQ# lbetto, 16080134 | | PhD in Computer Science | | Dip. Sistemi e Informatica, Univ. di Firenze | | Florence - Italy (GNU/Linux User # 158233) | | Home Page : http://www.lorenzobettini.it | | http://music.dsi.unifi.it XKlaim language | | http://www.lorenzobettini.it/purple Cover Band | | http://www.gnu.org/software/src-highlite | | http://www.gnu.org/software/gengetopt | | http://www.lorenzobettini.it/software/gengen | | http://www.lorenzobettini.it/software/doublecpp | +-----------------------------------------------------+

Lorenzo Bettini <bettini@dsi.unifi.it> writes:
David Abrahams wrote:
Lorenzo Bettini <bettini@dsi.unifi.it> writes:
David Abrahams wrote:
Can you deal with deallocation of the whole network at once? It often works to maintain the nodes (C objects) in a container like a deque and let them link to one another with raw pointers. Otherwise you may be in need of a real GC.
I solved the problem in a (probably) not too elegant way: when I know that I'm creating objects with mutual dependences say A and B I store weak pointers, and I store a shared pointer to A and B into a list. As long as the list survives the weak pointers are still valid. When I want to deallocate everything, all I have to do is to deallocate the list. This worked, and also valgrind says there are no leaks... The smart pointers may not be buying you much of anything in that case.
so what could be an alternative?
What I suggested up there in the message you quoted. Note your class was called 'C'; I wasn't referring to the C language. -- Dave Abrahams Boost Consulting www.boost-consulting.com

Lorenzo Bettini wrote:
Jonathan Wakely wrote:
On Mon, Jun 06, 2005 at 03:58:41PM +0200, Lorenzo Bettini wrote:
However, it is not clear to me how to use weak_ptr in this context...
can some one provide some clue, please?
the main problem I had is just "ensure the shared_ptrs that own the objects are still around when you use the weak_ptrs.".
Moreover, I had to deal with mutual dependences, and this makes the whole thing worse...
A couple of options, none of them very pretty. 1) Ask Peter Dimov to give you his experimental cycle collection code for shared_ptr. Last I saw, this had exception safety issues because it didn't provide a no-throw guarantee, which you would need if you were to use it from a destructor (which is likely). 2) Grab tracking_ptr.hpp from boost-sandbox/boost/xpressive/detail/utility (can also be found in xpressive.zip from the Boost Sandbox File Vault). I had to solve the same problem for xpressive, where regex objects can embed other regex objects, either "by value" or "by reference". This leads to all sorts of interesting cyclic ref-count scenarios. I use tracking_ptr to handle this. Unfortunately, tracking_ptr is not documented. I forces you to separate your object into a handle-body, where the body inherits from enable_reference_tracking<Body>, and the handle holds a tracking_ptr<Body>. There's more, but maybe you can figure out the rest by seeing how xpressive uses tracking_ptr. HTH, -- Eric Niebler Boost Consulting www.boost-consulting.com

On 06/06/2005 11:45 AM, Eric Niebler wrote: [snip]
I had to solve the same problem for xpressive, where regex objects can embed other regex objects, either "by value" or "by reference". This leads to all sorts of interesting cyclic ref-count scenarios. I use tracking_ptr to handle this.
Maybe you could also post your problem in response to Le Chaud Lapin's post I mentioned in a previous reponse to Lorenzo. It might help in ending that long thread in the c.l.c++.m news group :)

Eric Niebler wrote:
1) Ask Peter Dimov to give you his experimental cycle collection code for shared_ptr. Last I saw, this had exception safety issues because it didn't provide a no-throw guarantee, which you would need if you were to use it from a destructor (which is likely).
Here it is: http://lists.boost.org/boost-users/2005/05/11805.php http://lists.boost.org/boost-users/att-11805/reset_and_collect.cpp

Eric Niebler wrote:
2) Grab tracking_ptr.hpp from boost-sandbox/boost/xpressive/detail/utility (can also be found in xpressive.zip from the Boost Sandbox File Vault).
I had to solve the same problem for xpressive, where regex objects can embed other regex objects, either "by value" or "by reference". This leads to all sorts of interesting cyclic ref-count scenarios. I use tracking_ptr to handle this.
indeed also my context has to do with regular expressions and mutual cyclic ref-count scenarios (in source-highlight http://www.gnu.org/software/src-highlite). As I said in a response to a previous post, I solved the problem by storing weak pointers in the cyclic structures and store a shared pointer into a list that is kept alive for all the life of the cyclic structures.
Unfortunately, tracking_ptr is not documented. I forces you to separate your object into a handle-body, where the body inherits from enable_reference_tracking<Body>, and the handle holds a tracking_ptr<Body>. There's more, but maybe you can figure out the rest by seeing how xpressive uses tracking_ptr.
OK, thanks! Lore -- +-----------------------------------------------------+ | Lorenzo Bettini ICQ# lbetto, 16080134 | | PhD in Computer Science | | Dip. Sistemi e Informatica, Univ. di Firenze | | Florence - Italy (GNU/Linux User # 158233) | | Home Page : http://www.lorenzobettini.it | | http://music.dsi.unifi.it XKlaim language | | http://www.lorenzobettini.it/purple Cover Band | | http://www.gnu.org/software/src-highlite | | http://www.gnu.org/software/gengetopt | | http://www.lorenzobettini.it/software/gengen | | http://www.lorenzobettini.it/software/doublecpp | +-----------------------------------------------------+
participants (6)
-
David Abrahams
-
Eric Niebler
-
Jonathan Wakely
-
Larry Evans
-
Lorenzo Bettini
-
Peter Dimov