[Gc] Using C++ Standard Template Library with GC.

David Jones dej@inode.org
Thu, 30 Oct 2003 09:06:53 -0400


On October 30, 2003 01:13 am, Fred Gylys-Colwell wrote:
> Hello.
>
> Does anyone have an example of using the garbage collector with the
> STL containers?  I have read the documentation in gcinterface.html
> and am still confused.  In particular, I have an example where I put
> several gc_cleanup objects into a container, and find that they are
> being prematurely collected.
>
> I am using GC 6.3alpha2, on Linux (2.4.22), with gcc 3.2.2.
>
> If you can give me some pointers on fixing my example, I would be
> very happy.  A working example would be even better!

The basic problem is that STL containers need to allocate memory, and by 
default they do so in the "malloc heap", which is neither scanned nor 
collectible.

You need to allocate from a collectible heap.

The STL supports this through the use of "allocators" which control how the 
containers allocate memory.  Recent versions of the collector supposedly 
contain template code for the necessary allocators, but the code I started 
using did not, so I wrote my own allocators.

So, cut from my sources, here we go:

#include "gc_cpp.h"

// These allocators permit memory used by STL objects to be managed by
// the GC.  Taken from Josuttis.
template <class T>
class GC_collectible {
public:
    typedef std::size_t		size_type;
    typedef std::ptrdiff_t	difference_type;
    typedef T*			pointer;
    typedef const T*		const_pointer;
    typedef T&			reference;
    typedef const T&		const_reference;
    typedef T			value_type;

    template <class U>
    struct rebind {
	typedef GC_collectible<U> other;
    };

    pointer address(reference value) const
    {
	return &value;
    }

    const_pointer address(const_reference value) const
    {
	return &value;
    }

    GC_collectible() throw() {}
    GC_collectible(const GC_collectible &) throw() {}

    template <class U>
    GC_collectible(const GC_collectible<U> &) throw() {}

    ~GC_collectible() throw() {}

    size_type max_size() const throw()
    {
	return std::numeric_limits<std::size_t>::max() / sizeof(T);
    }

    pointer allocate(size_type num, void *hint = 0)
    {
	return (pointer)GC_malloc(num * sizeof(T));
    }

    void construct(pointer p, const T &value)
    {
	new ((void*)p)T(value);
    }

    void destroy(pointer p)
    {
	p->~T();
    }

    void deallocate(pointer p, size_type num)
    {
    }
};

template <class T1, class T2>
bool operator==(const GC_collectible<T1> &, const GC_collectible<T2> &) 
throw()
{
    return true;
}

template <class T1, class T2>
bool operator!=(const GC_collectible<T1> &, const GC_collectible<T2> &) 
throw()
{
    return false;
}


// This allocator uses GC_malloc_atomic(), which is useful for containers
// of objects that do not contain pointers.
//
// THIS WORKS FOR VECTORS ONLY!!!  Other containers use pointers internally,
// and will not work - they must be collectible.
template <class T>
class GC_atomic {
public:
    typedef std::size_t		size_type;
    typedef std::ptrdiff_t	difference_type;
    typedef T*			pointer;
    typedef const T*		const_pointer;
    typedef T&			reference;
    typedef const T&		const_reference;
    typedef T			value_type;

    template <class U>
    struct rebind {
	typedef GC_atomic<U> other;
    };

    pointer address(reference value) const
    {
	return &value;
    }

    const_pointer address(const_reference value) const
    {
	return &value;
    }

    GC_atomic() throw() {}
    GC_atomic(const GC_atomic &) throw() {}

    template <class U>
    GC_atomic(const GC_atomic<U> &) throw() {}

    ~GC_atomic() throw() {}

    size_type max_size() const throw()
    {
	return std::numeric_limits<std::size_t>::max() / sizeof(T);
    }

    pointer allocate(size_type num, void *hint = 0)
    {
	return (pointer)GC_malloc(num * sizeof(T));
    }

    void construct(pointer p, const T &value)
    {
	new ((void*)p)T(value);
    }

    void destroy(pointer p)
    {
	p->~T();
    }

    void deallocate(pointer p, size_type num)
    {
    }
};

template <class T1, class T2>
bool operator==(const GC_atomic<T1> &, const GC_atomic<T2> &) throw()
{
    return true;
}

template <class T1, class T2>
bool operator!=(const GC_atomic<T1> &, const GC_atomic<T2> &) throw()
{
    return false;
}