containers/safe_iterators.hpp
The STLplus Safe-Iterator

Philosophy

Iterators in the STL are badly designed - there are several major pitfalls with using them as a result of their design. However, I wanted STLplus to feel like the STL to make it easier to use, so felt I should use the iterator concept despite the problems associated with them. Some of the STLplus components therefore also use iterators.

The problems with STL iterators are:

Errors Caught by the Safe Iterator

The STLplus safe iterators were designed to solve the problems with the STL iterators. The following solutions have been implemented:

Classes Using the Safe Iterator

The following STLplus classes use safe iterators, all of them based on the safe_iterator class:

Interface

Here is the user interface to the safe_iterator class:

template<typename O, typename N>
class stlplus::safe_iterator
{
public:
  // construct a null iterator
  safe_iterator(void) throw();

  // construct a valid iterator from the owner node's master iterator
  safe_iterator(const master_iterator<O,N>&) throw();

  // comparison - used to create comparison operators for putting
  // iterators into maps and hashes
  bool equal(const safe_iterator<O,N>& right) const throw();
  int compare(const safe_iterator<O,N>& right) const throw();

  // dereference
  N& operator*(void) const throw(null_dereference,end_dereference);
  N* operator->(void) const throw(null_dereference,end_dereference);

  // a null iterator is one that has not been initialised with a value yet
  // i.e. you just declared it but didn't assign to it
  bool null(void) const throw();

  // an end iterator is one that points to the end element of the list of nodes
  // in STL conventions this is one past the last valid element and must not be dereferenced
  bool end(void) const throw();

  // a valid iterator is one that can be dereferenced
  // i.e. non-null and non-end
  bool valid(void) const throw();

  // called by the owner class to check the rules
  void assert_valid(const O* owner) const throw(wrong_object,null_dereference,end_dereference);
};

The void constructor ensures that an unassigned iterator is classified as a null iterator.

The second iterator is used internally by the container object to create a valid iterator.

Built-in comparison methods allow for comparison operators to be written for any iterator derived from safe_iterator, for example to test for the end condition in a for loop or any other conditional. They also allow iterators to be added to sets (the < operator) and hashes (the == operator).

The dereference operators are only legal on valid iterators - any attempt to dereference a null or end iterator will throw an exception.

The methods null(), end() and valid() allow the programmer to test the validity of an iterator before dereferencing it.

The assert_valid method is called by the owning container to check that the iterator belongs to it before using it. If not, it will throw the wrong_object exception. Also, if the owner needs to dereference the iterator, it will check whether it is valid and if not, throw either the null_dereference or end_dereference exception.