Boost logo

Boost :

From: Ariane van der Steldt (ariane_at_[hidden])
Date: 2007-04-11 00:27:46


Matt Calabrese wrote:
> On 4/10/07, Ariane van der Steldt <ariane_at_[hidden]> wrote:
>
>>I was wondering if there's interest in a smart pointer, which is able to
>>cope with circular references. I think it would make a nice addition to
>>the smart_ptr classes already in boost.
>>
>>The code I have aims to solve this problem, by using two types of
>>pointers. The execution thread will work with boost::intrusive_ptr
>>(which are allocated on the stack) while classes wishing to point to
>>other classes, will use a new type of pointer (named 'reference') that I
>>designed. This pointer is aware of the owner of the pointer, in addition
>>of the object it points to.
>>
>
>
> I might be more interested if I more clearly saw your proposed design and
> solution. Could you provide a brief code example, even if it is just
> pseudo-code, and detail exactly what the user witnesses (I.e. when
> destruction occurs, etc.)? As well, what is the "non-standard"
> initialization you speak of?

Hmm, I admit the "non-standard" initialization is a badly chosen way to
describe it. What I meant to say is that the reference constructor
requires a parameter, the owner of the reference: if class A contains a
reference with name ptr to class B, the constructor of class A needs be
thus:
A::A() : ptr(*this) { }
which creates a variable ptr pointing to null, which knows its parent is
*this.
Had I stated this constructor:
A::A(my_class* other) : ptr(*this, other) { }
I would have created a ptr pointing to other.

Had ptr been a boost::shared_ptr, this would have been the two constructors:
A::A() : ptr() { }
A::A(my_class* other) : ptr(other) { }
but then, circular references would not be detected.

I have some actual code from one of my test cases at the end of my post.

Basically, the reference class has been designed to mimic the behaviour
of shared_ptr. The referenced class is present to handle reference
counters, reachability and provide a virtual destructor for the garbage
collector. I'm not really happy that the name differs only one character.

The garbage collector is a form of a mark sweep collector, the
tri-colour variant to be specific.
The basic algorithm is described on wikipedia:
http://en.wikipedia.org/wiki/Mark_and_sweep#Basic_algorithm
This implementation means that, in a multithreading environment,
other threads can still create and destroy references while the
garbage collector is running.

Here's sample code using the reference system.

--- small_loop.cc ---
#include "small_loop.h"

#include <string>

#include <boost/test/unit_test.hpp>

#include <reference/reference.h>

namespace small_loop
{
        int test_class_count = 0;

        class test_class : public reference::referenced
        {
                public:
                        const std::string id;
                        reference::reference<test_class> next;
        
                        test_class(std::string id) :
                                id(id),
                                /* construct reference owned by *this */
                                next(*this)
                        {
                                ++test_class_count;
                                return;
                        }

                        test_class(std::string id,
                                        const test_class& rhs) :
                                id(id),
                                /* construct reference owned by *this */
                                next(*this)
                        {
                                ++test_class_count;
                                /* assignment */
                                next = rhs.next;
                                return;
                        }

                        ~test_class() throw ()
                        {
                                --test_class_count;
                                return;
                        }
        };

        void test()
        {
                /* Construction of reachable test_class. */
                boost::intrusive_ptr<test_class> test0 =
                        new test_class("#00");
                {
                        /* Construction of reachable test_class. */
                        boost::intrusive_ptr<test_class> test1 =
                                new test_class("#01");
                        /* Construction of reachable test_class. */
                        boost::intrusive_ptr<test_class> test2 =
                                new test_class("#02");

                        BOOST_CHECK_EQUAL(test_class_count, 3);

                        /* test_class->next is a pointer to test_class.
                         * The following lines assign the next pointers
                         * to form a circular list. */
                        test0->next = test1;
                        test1->next = test2;
                        test2->next = test1;
                        /* test1 and test2 go out of scope, triggering
                         * the garbage collector, since they contain a
                         * circular reference.
                         * However, the garbage collector will not erase
                         * any of these 3 instances, since they are
                         * reachable from test0.
                         *
                         * The garbage collector is called twice,
                         * first when test2 goes out of scope and the
                         * second time when test1 goes out of scope. */
                }
                BOOST_CHECK_EQUAL(test_class_count, 3);

                /* The next statement runs the garbage collector,
                 * which will actually remove all 3 created test
                 * instances, since the last pointer to it has
                 * gone out of scope.
                 * If the line 'test2->next = test1' had not been
                 * present, the delete operator would have been
                 * called, instead of the garbage collector,
                 * since there would not be any circular references
                 * present. */
                test0 = 0;
                BOOST_CHECK_EQUAL(test_class_count, 0);
                return;
        }
}
--- end of small_loop.cc ---


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk