Showing posts with label iterator. Show all posts
Showing posts with label iterator. Show all posts

Thursday, 14 March 2013

A class which implements 'for each' in unmanaged C++

This is a way to make just about any class implement a C++ 'for each'.  In this example the existing way of iterating round a list is as follows:

CTypedPtrList<CPtrList,int*> myList;
POSITION pos = myList.GetHeadPosition();
while(pos)
{
    int* p = myList.GetNext(pos);
}

You need to implement a begin() and end() function which return an iterator class.
The iterator class must implement:
    void operator++();
    T operator*() const;
    bool operator!=(const iterator& other)

Once you have done this then the compiler will automatically allow you to write your iterator like this:
for each(int* p in myList)
{
}

Note that if your variable is const you may need to implement a version of everything which is const.

See below for a full implementation example built on CTypedPtrList:

template <class T> class PList : public CTypedPtrList<CPtrList> 
{
public:

    // Support for iterators.
    class iterator
    {
    public:
        iterator( PList<T>* p, POSITION pos ) 
            : m_pList(p), m_pos(pos), m_pointer(NULL) 
        {
            if( m_pList )
            {
                m_pos = m_pList->GetHeadPosition();
                if( m_pos )
                    m_pointer = m_pList->GetNext(m_pos);
            }
        }

        void operator++(int) 
        {
            if( m_pos==NULL ) m_pointer = NULL;
            else    m_pointer = m_pList->GetNext(m_pos);
        };

        void operator++()
        {
            this->operator++(0);
        };

        T* operator*() const { return m_pointer; }

        bool operator!=(const iterator& other) const 
        {
            return m_pointer!=other.m_pointer;
        }
    protected:
        PList<T>* m_pList;
        POSITION  m_pos;
        T*        m_pointer;
    };
    //
    iterator begin() { return iterator(this, GetHeadPosition()); }
    iterator end()   { return iterator(NULL,NULL); }
};

A word of caution about the unmanaged C++ 'for each' and 'auto' keywords

Recently I had the following code:

 
std::map<CString, std::vector<CString>> mapIdToChildren;
for(std::map<CString,std::vector<CString>>::iterator it = mapIdToChildren.begin();it!=mapIdToChildren.end();++it)
 std::sort( (*it).second.begin(), (*it).second.end() );
 

And tried replacing it with the 'for each' keyword:
for each( auto it in mapIdToChildren )
 std::sort( it.second.begin(), it.second.end() );
 

But found that the result is unsorted..
What is really happening is that the 'auto' is expanded to the following which means the vector is copied, so the original object remains unsorted:
for each( std::pair<CString,std::vector<CString>> it in mapIdToChildren )
 std::sort( it.second.begin(), it.second.end() );
 

Unfortunately the following code doesn't compile..
for each( std::pair<CString,std::vector<CString>>& it in mapIdToChildren )
 std::sort( it.second.begin(), it.second.end() );
 

Another failure:
for each( std::map<CString,std::vector<CString>>::iterator it in mapIdToChildren )
 std::sort( (*it).second.begin(), (*it).second.end() );
 

In conclusion you should be careful when using the 'auto' keyword in conjunction with 'for each' because you may not be getting what you think.