The Filterator

My name is Ahmed Charles and I currently work on Windows Error Reporting.  I believe that this is the first time that someone not on the VC team has written a blog, but I hope you will find it useful anyways.  I’d like to thank Stephan T. Lavavej for the idea and valuable feedback while writing it.  And we both owe the idea for the iterator to Boost.

 

A common question from programmers who have an intermediate amount of experience with using the STL is, “How do I write an STL iterator?”.  Writing an STL iterator is not especially difficult – it just requires some care.  STL iterators must satisfy a number of requirements (given by section 24.2 of the (soon to be) International Standard for C++, draft at http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2010/n3092.pdf), and the code to do so takes roughly 150 editor lines.  Figuring out the code from the requirements can be overwhelming, but once you see the code, it’s easy.

 

Therefore, I’ve written an example STL iterator, whose purpose in life is to wrap an existing iterator and filter the elements based on a supplied predicate, which I’ve imaginatively called Filterator (in homage to the Mallocator).  I’ve carefully implemented all of the checks that would be required in real production code.  And I’ve exhaustively commented the various parts of the iterator to aid in determining which portions should be changed when implementing other iterators.  Hopefully, this should demystify the implementation of STL iterators:

C:Temp>type filterator.cpp

// The following headers are required for all iterators.

#include <iterator>    // Required for iterator_traits

 

// The following headers contain stuff that filterator uses.

#include <type_traits> // Required for std::conditional/

                       // is_same/is_convertible

#include <memory>      // For std::addressof

 

// The following headers contain stuff that main() uses.

#include <algorithm>   // For std::fill

#include <iostream>    // For std::cout

#include <ostream>     // For std::endl

#include <string>      // For std::string

#include <vector>      // For std::vector

 

template <class Iterator, class Predicate>

class filterator {

public:

    // Required to access private members in the converting

    // constructor because different instantiations of a

    // template are different classes without special access

    // rights to each other.

    template <class OtherIterator, class OtherPredicate>

        friend class filterator;

 

    // Define iterator_category to be the iterator category

    // of the Iterator used, unless it is a random access

    // iterator, which cannot provide constant time

    // functions for random access operators because of

    // needing to test the predicate.

    typedef typename std::iterator_traits<

        Iterator>::iterator_category underlying_category;

 

    typedef typename std::conditional<std::is_same<

            underlying_category,

            std::random_access_iterator_tag

        >::value,

        std::bidirectional_iterator_tag,

        underlying_category>::type iterator_category;

 

    // Define the other typedefs required by iterators.

    typedef typename std::iterator_traits<

        Iterator>::value_type value_type;

    typedef typename std::iterator_traits<

        Iterator>::reference reference;

    typedef typename std::iterator_traits<

        Iterator>::pointer pointer;

    typedef typename std::iterator_traits<

        Iterator>::difference_type difference_type;

 

    // Default constructor which value-initializes all of

    // the members.

    filterator() : m_iter(), m_end(), m_pred() {}

 

    // Constructs a filterator from all possible parameters.

    // pred may be default constructed, as well.

    filterator(Iterator begin, Iterator end,

        Predicate pred = Predicate())

        : m_iter(begin), m_end(end), m_pred(pred) {

        validate_predicate();

    }

 

    // Converting constructor which can be used to convert

    // iterators into const_iterators.

 

 

    template <class OtherIterator> filterator(

        const filterator<OtherIterator, Predicate>& f,

        typename std::enable_if<

            std::is_convertible<

                OtherIterator, Iterator

            >::value>::type * = nullptr)

        : m_iter(f.m_iter),

        m_end(f.m_end),

        m_pred(f.m_pred) {}

 

    // Returns a copy of the predicate for this filterator.

    Predicate predicate() const {

        return m_pred;

    }

 

    // Returns a copy of a filterator representing the end

    // of this sequence.

    filterator end() const {

        return filterator(m_end, m_end, m_pred);

    }

 

    // Returns a copy of the end iterator for this

    // filterator.

    Iterator base_end() const {

        return m_end;

    }

 

    // Returns a copy of the iterator which represents the

    // current position.

    Iterator base() const {

        return m_iter;

    }

 

    // Dereferences this iterator

    reference operator*() const {

        return *m_iter;

    }

 

    // Dereferences this iterator

    pointer operator->() const {

        return std::addressof(*m_iter);

    }

 

    // Pre-increment operator which checks the predicate

    // before stopping.

    filterator& operator++() {

        ++m_iter;

        validate_predicate();

        return *this;

    }

 

    // Post-increment operator which checks the predicate

    // before stopping.

    filterator operator++(int) {

        filterator temp(*this);

        ++*this;

        return temp;

   }

 

    // Pre-decrement operator which checks the predicate

    // before stopping.

    filterator& operator–() {

        –m_iter;

        // note that we don’t store a begin iterator to

        // check for passing the beginning of the sequence,

        // unlike the end. This is because decrementing the

        // first iterator is undefined and applying a filter

        // on top of the underlying iterator doesn’t change

        // this, even if the resulting sequence is empty.

        while (!m_pred(*m_iter)) –m_iter;

        return *this;

    }