Weak containers

LIBCXX implements several variations of STL containers that store weak pointers, with an additional aspect that the weak pointer gets removed from the container automatically when its referenced object goes out of scope and gets destroyed.

The container itself is an ordinary reference-counted object (which can certainly have its own weak pointers), and it contains only weak pointers to other objects. When the weakly-referenced object goes out of scope and gets destroyed, not only does the weak pointer, in the weak container, becomes undefined, it gets removed from the container, automatically.

x::weaklist is a template alias for a reference to a container with std::list-like properties. x::weaklistptr is the corresponding x::ptr template alias. The x::weaklist accepts weak pointers:

#include <x/weaklist.H>

class widgetObj;

typedef x:ref<widgetObj> widget;

typedef x::weaklist<widgetObj> weakwidgetlist;

weakwidgetlist currentWidgets(weakwidgetlist::create());

// ...

widget p=widget::create();


The template parameter is a reference-counted object. x::weaklist and x::weaklistptr refer to a std::list-like objects whose push_front() and push_back() methods take a regular, strong reference, but place a weak pointer into the weak list. push_front() and push_back() do not retain the strong reference they receive as a parameter, they retain only a weak pointer.

There is no erase() method in the container. When the weakly-referenced object goes out of scope and gets destroyed, the weak pointer also gets removed from the container.

for (weakwidgetlist::base::iterator b=currentWidgets->begin(),
     e=currentWidgets->end(); b != e; ++b)
    widgetptr p=b->getptr();

    if (!p.null())
       // ...

Weak list containers offer begin(), end(), empty(), size(), push_back(), and push_front(). Iteration over a weak list container iterates over weak pointers, so getptr() must be used to recover a strong reference. Although the weak pointers get purged from the container upon destruction of the referenced object, it's still possible that an occasional null weak pointer gets encountered. When the last reference to the weakly-referenced object goes out of scope and it gets destroyed, the weak pointer to the object normally gets removed from the container automatically. However, this is not possible if there are iterators in scope. They are currently iterating over the weak container, which cannot be modified as long as they remain in scope. For this, and a few other reasons, it's possible that a null pointer gets recovered from a weak pointer. The weak pointer to the destroyed object gets purged from the weak container only after the last weak container iterator goes out of scope and gets destroyed, so nothing else refers to the weak container (except pointers or references to the container itself). Once the contents of the container are safe from potential access, the weak pointers to destroyed objects get purged from the container.

For this reason, iterators over a weak list, or a weak map, should be short lived, and not stashed away someplace. Where possible, use a range iterator.

x::weakmap and x::weakmultimap define analogous containers with map-like properties:

#include <x/weakmap.H>

// Or multimap.H

class widgetObj;

typedef x:ptr<widgetObj> widgetptr;

typedef x::weakmap<std::string, widgetObj> weakwidgetmap;

weakwidgetmap currentWidgets(weakwidgetmap::create());

// ...

widgetptr p=widgetptr::create();

currentWidgets->insert(std::make_pair("left", p));

// ...

weakwidgetmap::base::iterator iter=currentWidgets->find("left");

if (iter != currentWidgets->end())
    widgetptr p=iter->second.getptr();

    if (!p.null())
// ...

Like a weak list container's, the weak maps do not implement erase(); when the weakly-referenced object goes out of scope and gets destroyed, its weak pointers gets removed automatically, if no iterators exist at the time; or after the last iterator goes out of scope and gets destroyed. It follows that iterators over the maps' contents should have a brief existence, to avoid the containers growing with undefined weak pointers.

Not surprisingly, weak map iterators iterate over std::pairs containing the key, and its corresponding weak pointer. Weak map containers implement the following std::map/std::multimap methods: begin(), end(), empty(), size(), find(), count(), lower_bound(), upper_bound(), and equal_range() (meaningful only for x::weakmultimaps).

insert() adds a weak pointer to the map. An alternative version of insert() exists, that takes the key and the pointer separately, without the need for an intermediate std::pair:

currentWidgets->insert("left", p);

The weak map container also offers operator[], as syntactical sugar, but since the container is a reference-counted object that must be dereferenced by a *, this may not be very useful.