STL iterators and MFC

What's It All About?

This article is an introduction to STL-style iterators, and how by writing your own iterators, the use of certain containers can be simplified and made STL compatible. The containers used in this example are the MFC framework application, document template, and document classes (CWinApp, CDocTemplate, and CDocument), which form a hierarchy with the lowest level, the document, containing views (CView).

If you can answer 'Yes' to one or more of the following questions, this article may be interesting (or even useful) to you:

Are you ever irritated by the GetFirstPosition / GetNextItem style of iteration through MFC's window containers?

Does 'POSITION' leave you cold?

Do you have a nagging feeling that there must be a simpler way?

Have you thought about using the STL, but can't see much use for it with MFC?

Do you use the STL, but wish MFC classes could be more involved?

Have you ever wondered what iterators really do, and how they can be useful?

Would you like to see how to construct a single iterator that can return you a pointer to every View in your application?

STL Iterators

STL iterators are used to access items held in STL containers. A C++ pointer is a kind of iterator which can be used to access items in an array. Other STL iterators are generally class objects that have similar operations to C++ pointers, such as ++ (increment) and * (dereference). They are arranged in a hierarchy (not an inheritance hierarchy) according to the operations they support, so the most restricted Input and Output iterators support incrementing with ++, dereferencing with *, and equality checking with == and !=. Input iterators only read the elements, Output iterators only write them. They are typically used with input and output streams. Next come Forward iterators, which combine the read and write capabilities of Input and Output iterators, and Bidirectional iterators are Forward iterators that can also go backwards using the -- (decrement) operator. Random Access iterators can do all this, and also use the [] operator to randomly access items by some key or index. C++ pointers are Random Access iterators.

STL containers generally define their own iterators as class members, and provide a begin() function to get an iterator to the first element, and an end() function that returns an iterator *past* the end of the container. The end iterator doesn't point to a valid item, but is only used to test whether another iterator has reached the end of the container, using the != operator, typically like this:


 typedef vector < int >	IntArray;
 IntArray myArray;
 IntArray::iterator vi;     // construct iterator for an array of int

 for (vi = myArray.begin(); vi != myArray.end(); ++vi)
 { 
    // Note that the loop test is always: iterator != end(), never:  iterator > end()
 }  

The Main Bit

In this article I describe a template class that will make an STL-style input iterator for any class that contains elements accessible by GetFirstPosition / GetNextItem style member functions, and present some classes that use it to make iterators for MFC CViews, CDocuments, and CDocTemplates. I also show a class that will permit these iterators to be nested, allowing such joys as iteration over all the CViews of all the CDocuments for a CDocTemplate, and even all the CViews of all the CDocuments for all the CDocTemplates in a CWinApp.

Because the MFC containers don't have a begin() function to return an iterator, my iterators set themselves to the first item on construction, and they contain their own end() function to test for reaching the end of the container.

For example, the traditional MFC iteration of Views might be something like this:


void CMyDoc::OnRepaintAllViews()
{   
   POSITION pos = GetFirstViewPosition();
   while (pos != NULL)   
   {      
      CView* pView = GetNextView(pos);
      pView->UpdateWindow();   
   }   
}
using the ViewIterator it becomes:

void CMyDoc::OnRepaintAllViews()
{   
   for (ViewIter vi(this); vi != vi.end(); ++vi)   
   {      
      (*vi)->UpdateWindow();   
   }   
}
The MFC code to get the first View for a Document usually looks like this:

POSITION pos = pDoc->GetFirstViewPosition();   
CView* pView = pDoc->GetNextView(pos);
using the ViewIterator it becomes:

ViewIter vIt(pDoc);	// Construct view iterator - points to first view
CView* pView = *vIt;	// dereference iterator to get view pointer
which can be shortened to:

CView* pView = *ViewIter(pDoc);   // Construct temporary ViewIter and dereference it
The other iterators for CDocuments and and CDocTemplates work in the same way. Because the base class for these iterators is templated with the GetFirstxxx/GetNextxxx functions, it is flexible enough to make iterators for other common classes with suitable functions, such as the CTreeCtrl with GetFirstVisibleItem() and GetNextVisibleItem().

Here is the iterator base class, BaseMFCIter:


#ifndef BASEMFCITERATOR_H
#define	BASEMFCITERATOR_H

//***************************************************************************/
//
// BaseMFCIter class implementation.
//
// Base iterator class for iterating MFC-style containers that use GetFirstPos,
// GetNextItem semantics (where GetFirstPos returns a value to be passed to
// GetNextItem, which updates it and returns an item).
//
// NOTE: The Item type must have a default constructor if it is not a basic type.
//***************************************************************************

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include < iterator >

// Define BaseMFCIter as a standard input iterator.
//
// The template arguments are: 
//	Item: the contained element type
//	Cont: the container type
//	Key:  the access key (defaults to POSITION)

template < class Item, class Cont, class Key = POSITION >
class BaseMFCIter : public std::iterator < std::input_iterator_tag, Item >
{
public:
   // Define types for the 2 member functions to be used:
   typedef Key  (Cont::*GetFirstFunctionPtr) ()     const;
   typedef Item	(Cont::*GetNextFunctionPtr)  (Key&) const;
	
   // Default constructor, makes a null iterator, equal to BaseMFCIter::end()
   BaseMFCIter() : m_pCont(0), m_Pos(0), m_GetFirstFunc(0), m_GetNextFunc(0), m_End(true) {}

   // Constructor taking pointer to container and the iteration functions
   BaseMFCIter(Cont* pCont, GetFirstFunctionPtr pFF, GetNextFunctionPtr pNF) 
      : m_pCont(pCont), m_Pos(0), m_GetFirstFunc(pFF), m_GetNextFunc(pNF)
   { init(); }

   // Copy constructor, initialises iterator to first element
   BaseMFCIter(const BaseMFCIter& vi) : m_pCont(vi.m_pCont), m_Pos(0),
   	m_GetFirstFunc(vi.m_GetFirstFunc), m_GetNextFunc(vi.m_GetNextFunc)
   { init(); }

   // Assignment operator, initialises iterator to first element
   BaseMFCIter& operator=(const BaseMFCIter& vi)
   {
      m_pCont		= vi.m_pCont; 
      m_GetFirstFunc	= vi.m_GetFirstFunc;
      m_GetNextFunc	= vi.m_GetNextFunc;
      init();  
      return *this; 
   }

   bool operator == (const BaseMFCIter& rhs) const
   { return (m_Pos == rhs.m_Pos && m_End == rhs.m_End); }

   bool operator != (const BaseMFCIter& rhs) const 
   { return !operator==(rhs); }

   BaseMFCIter&	operator ++ ()    { advance(); return *this; }
   BaseMFCIter&	operator ++ (int) { BaseMFCIter ret(*this); advance(); return ret; }
   Item	        operator *  ()    { return m_Item; }
   Item	        operator -> ()    { return m_Item; }

   static BaseMFCIter end   ()    { return BaseMFCIter(); }	// end() returns default null iterator

private:
   Item	 m_Item;      // Current item from container
   Cont* m_pCont;     // Pointer to container
   Key	 m_Pos;       // Key to item in container
   bool	 m_End;       // Flag to indicate end of container reached

   // Pointers to container iteration functions
   GetFirstFunctionPtr m_GetFirstFunc;
   GetNextFunctionPtr  m_GetNextFunc;

   // Use container GetFirst & GetNext functions to set to first element, or end() if not found
   void init() 
   {
      m_Pos = 0;
      m_End = true;

      if (m_pCont && m_GetFirstFunc != 0)
      {
         m_Pos = (m_pCont->*m_GetFirstFunc)();
         advance();
      }
   }

   // Use container GetNext function to find next element in container
   void advance()
   {
      m_End = m_Pos ? false : true;
      m_Item = (m_Pos && m_pCont && m_GetNextFunc != 0) ? 
               (m_pCont->*m_GetNextFunc)(m_Pos) : Item();
   }
};

#endif
BaseMFCIter (above) is templated to take the item type to be returned, the container type that holds the items, and the key type used to access the items in the container (defaulted to POSITION). BaseMFCIter also needs to hold pointers to the container functions it must call to get the first item position and next item. These function pointers should be passed to the BaseMFCIter constructor along with a pointer to the container to be used. If you haven't used pointers to member functions before, you will see they're really not that difficult to use.

Here are the iterator classes, derived from BaseMFCIter. Notice how simple they are, just initialising the base class with the appropriate container and the functions to use on it:


#ifndef MFCITERATORS_H
#define	MFCITERATORS_H

//***************************************************************************/
//
// ViewIter, DocIter, and DocTemplateIter class implementations.
//
// Iterator classes for iterating views, documents, and doctemplates.
//
//***************************************************************************

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include "BaseMFCIter.h"

class ViewIter : public BaseMFCIter < CView*, CDocument >
{
public:
   ViewIter(CDocument* pDoc = 0) : BaseMFCIter< CView*, CDocument >
      (pDoc, CDocument::GetFirstViewPosition, CDocument::GetNextView)
   {}
};

class DocIter : public BaseMFCIter< CDocument*, CDocTemplate >
{
public:
   DocIter(CDocTemplate* pDT = 0) : BaseMFCIter< CDocument*, CDocTemplate >
      (pDT, CDocTemplate::GetFirstDocPosition, CDocTemplate::GetNextDoc)
   {}
};

class CDocTemplateIter : public BaseMFCIter< CDocTemplate*, CWinApp >
{
public:
   CDocTemplateIter(CWinApp* pApp = 0) : BaseMFCIter< CDocTemplate*, CWinApp >
      (pApp, CWinApp::GetFirstDocTemplatePosition, CWinApp::GetNextDocTemplate)
   {}
};

#endif

Nesting Iterators

So far so good. We now have convenient iterators to get doctemplates from applications, documents from doctemplates, and views from documents. It should now also be fairly clear how you can use BaseMFCIter to make other iterators that fit this GetFirstPos / GetNext idiom.

Interestingly, the containers for these particular iterators (above) form a nested hierarchy: documents within doctemplates within applications. It might be useful if we could somehow combine them so as to be able iterate over, for example, all the documents in all the doctemplates in an application, without having to code a nested loop to do it.

The solution, unsurprisingly, is another templated iterator class that wraps two appropriate iterators and presents them as one. I have called it NestedMFCIter, and it is templated to take an inner iterator which accesses the items we are interested in, an outer iterator that accesses the containers holding the items, and an outer container that holds those containers. I'm sorry if that's less than not very clear, but it's a little hard to describe clearly... anyway, this is the class:


#ifndef NESTEDMFCITERATOR_H
#define	NESTEDMFCITERATOR_H
//****************************************************************************
//
// NestedMFCIter class implementation.
//
// Iterator class for iterating through the contents of nested containers
// using iterators for each container type.
//
// class OuterCont is the container that holds the collections iterated over by
// class OuterIter. OuterIter dereferences to a container of items iterated over
// by class InnerIter. NestedMFCIter itself dereferences to the item iterated by
// InnerIter, thus allowing iteration over every item in every container in OuterCont.
//
//***************************************************************************

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

template < class InnerIter, class OuterIter, class OuterCont > 
class NestedMFCIter : public std::iterator < std::input_iterator_tag, InnerIter::value_type >
{
public:
   NestedMFCIter(OuterCont* pOC = 0) : 
         m_OuterIt(pOC), m_InnerIt(pOC ? *m_OuterIt : 0) 
   {}

   NestedMFCIter& operator=(OuterCont* pOC)
   { m_OuterIt = pOC; m_InnerIt = *m_OuterIt; return *this; }
		
   bool operator == (const NestedMFCIter& rhs) const
   { return m_InnerIt == rhs.m_InnerIt; }

   bool operator != (const NestedMFCIter& rhs) const 
   { return !operator==(rhs); }

   NestedMFCIter& operator ++ () { advance(); return *this; }

   NestedMFCIter& operator ++ (int) 
   { NestedMFCIter ret(*this); advance(); return ret; }

   InnerIter::value_type operator*()   { return *m_InnerIt; }
   InnerIter::value_type operator->()  { return *m_InnerIt; }

   static NestedMFCIter end()          { return NestedMFCIter(); }

private:
   OuterIter	m_OuterIt;
   InnerIter	m_InnerIt;

   // Advance to next inner item. If null, advance outer iterator to next inner
   void advance()
   {
      if (m_InnerIt != InnerIter::end())
         ++m_InnerIt;

      while (m_InnerIt == InnerIter::end() && (++m_OuterIt) != OuterIter::end())
      {
         m_InnerIt = *m_OuterIt;
      }
   } 
};
#endif
As you can see, it is constructed with a pointer to the outer container, and iterates over the contents of all the containers held in the outer container. For example, to extract the titles of all the documents in all the doctemplates in an application:

   // define a convenient type name
   typedef NestedMFCIter < DocIter, DocTemplateIter, CWinApp > AppDocIter;

   for (AppDocIter adi(AfxGetApp()); adi != adi.end(); ++adi)
   {
      CString title = (*adi)->GetTitle();
      ...
   }
Here, we template NestedMFCIter with the types of the inner iterator: DocIter, the outer iterator: DocTemplateIter, and the outer container: CWinApp. When constructed with an application instance, the resulting iterator will find the first CDocument in the first CDocTemplate in the application. When advanced, it will iterate through each of the CDocuments in each of the CDocTemplates in turn.

If this isn't enough, you can even use a NestedMFCIter type as one of the iterators passed to another NestedMFCIter type. This allows you to iterate over the items at the bottom of arbitrarily deep nestings of containers. For example, to access the window handles of all the views in an application, you can create a NestedMFCIter iterator for all the views in a doctemplate, and use that together with an iterator that accesses doctemplates in an application, in another NestedMFCIter:


   typedef NestedMFCIter < ViewIter, DocIter, CDocTemplate >	DocTemplateViewIter;
   typedef NestedMFCIter< DocTemplateViewIter, CDocTemplateIter, CWinApp >	AppViewIter;
   
   for (AppViewIter avi(AfxGetApp()); avi != avi.end(); ++avi)
   {
      HWND hWnd = (*avi)->GetSafeHwnd();   // Extract hWnd of view
      ...
   }
This version of a nested iterator type relies on the sort of iterators I have provided for the MFC classes discussed, but it would not be difficult to modify it to use the STL containers and iterators provided by Microsoft. This would allow simplified traversal of all the elements in multi-dimensional arrays made with nested STL containers.

So What's the Big Deal?

These MFC iterators are quite elegant in their own way, but let's face it, they don't really save more than a couple of lines of code, and they take up more than that behind the scenes. Why go to all this trouble just to avoid using POSITION ?

Well, firstly, this is just an example by way of an introduction to roll-your-own iterators to show what can be done. However, the real answer is that the use of iterators is the key to the STL. Any container that has STL iterators has a huge range of functions and algorithms available in the STL for manipulating it's contents. A typical STL algorithm or function will take a start iterator, an end iterator, an optional predicate function, and will perform some operation involving the elements from the start up to (but not including) the end. Where a predicate function is supplied, the operation performed will use the predicate function.

A predicate function can be an ordinary file-scope function, or a class operator() function. The latter is used where the function needs access to some data that persists between function calls. Rather than use static data, class member data is used, which can be initialised appropriately when the class is constructed. If this still doesn't make much sense, maybe an example will help:


   // Setting all application views to a desired display state (normal, maximised, iconised, etc.):

   #include < algorithm >
   #include "NestedMFCIterator.h"          
   #include "MFCIterators.h"

   using std::for_each;		// Declare that we're using the std library 'for_each' algorithm

   // Create an iterator for all views (as previously described)
   typedef NestedMFCIter < ViewIter, DocIter, CDocTemplate > DocTemplateViewIter;
   typedef NestedMFCIter < DocTemplateViewIter, CDocTemplateIter, CWinApp > AppViewIter;

   // Define a predicate class to set the parent frame of a view to a desired show state
   class DoShowWindow {
   public:
      DoShowWindow(int nCmdShow) : m_CmdShow(nCmdShow) {}	// constructor
      // Predicate function 	
      void operator()(CView* pView) { pView->GetParentFrame()->ShowWindow(m_CmdShow); }
   private:
      int m_CmdShow;	// Stores the desired show state
   };

   // Function that uses DoShowWindow to set the state of all views:
   void MyFunc()
   {
      CWinApp* pApp = AfxGetApp();

      // Show all views normal
      for_each(AppViewIter(pApp), AppViewIter::end(), DoShowWindow(SW_SHOWNORMAL));  
      ...
      // Show all views minimised
      for_each(AppViewIter(pApp), AppViewIter::end(), DoShowWindow(SW_SHOWMINNOACTIVE));  
      ...
   }
Download the demo project (VC++6) for an example, showing the use of all the classes discussed, to minimise or restore the child windows in an MDI application with multiple document templates, documents, and views.

Summary

In this article, I have introduced STL iterators and shown how writing your own iterators for non-STL containers can help simplify their use and make them more compatible with STL algorithms. The examples used provide templated iterator classes for MFC containers with GetFirst/GetNext iteration syntax, and have been expanded to show how they may be used in combination to provide nested container iteration.

Download demo project - 26 KB

Download source - 3 KB

Updated: March 22, 1999



Comments

  • Re:

    Posted by design icons on 12/12/2012 08:24pm

    P.S. Please review our design portfolio for Doors2012.

    Reply
  • Re:

    Posted by icon on 12/12/2012 08:18pm

    P.S. Please review our icons for Windows and windows14icons.

    Reply
  • I finally did it - recommended apos/themes? [Archive] - Touch Arcade

    Posted by nsojhus.com on 12/12/2012 06:09pm

    By WebOsPublisher Pictograms, Icons, and Signs | W. W. Norton & Company W. W. Norton & Company Logo Wishlist Cart Help Signed in as: (Sign Out) Sign In to access your account BooksCollege TextbooksAuthorsReading Guides $ ExtrasAffiliate PublishersAboutAll SubjectsCollege CoursesBestsellersAward WinnersClassicsNew $ ForthcomingBook SeriesGift Books See Larger Image Available: Ships in 1-2 daysOur Retail Price: $31.95 Other Ways to Order International Ordering Secondary Schools Print this page Email this page Pictograms, Icons, and Signs Rayan Abdullah (Author, Academy of Visual Arts), Roger Hübner (Author) A Thames $ Hudson book A copiously illustrated and practical guide to informational graphics. Pictograms and icons are a keystone of nonverbal and multicultural communication. But what precisely are pictograms, and when is it appropriate to use them? What are their advantages? What rules must be followed, and what are the pitfalls that designers of pictograms and icons must take care to avoid?Drawing on a multitude of examples from around the world, the authors outline the history of the pictogram and show how it has been used in commercial and creative fields over the past century, as well as offering invaluable hints and advice to designers.The book features: over 2,000 illustrations organized by theme, including pictograms from all the Olympic Games from 1964 to 2004; tips from successful pictogram designers, with real- life examples to instruct and inspire; a detailed discussion of icons, the "silent servants" of online communities; a chapter by designer Jochen Gros on his quest to create a visual language that crosses all grammatical, semantic, and semiotic boundaries—in effect, to create a "language without words," and more. Book Details Paperback November 2006 ISBN 978-0-500-28635-7 8.4 × 11.7 in / 244 pages Territory Rights: USA and Dependencies, Philippines and Canada. Copyright © W. W. Norton $ Company, Inc. 2012College RepsInternational RepsOrdering InfoPrivacy PolicyCareersContact

    Reply
  • What kind of girls like belly button rings?

    Posted by ifbdiicon.scom on 12/12/2012 04:16pm

    By WebOsPublisher JIDE Software - Preview Icon Set Stock Icon Preview Please flip through the pages to see a preview of all icons in each icon set. We grouped icons into different sections. Each page in the flash below is one section. About Watermarking Please note we added red watermark to the preview icons in the Flash below and also blurred the edge for copyright protection purpose. If you would like to see some of icons in the actual release, please email sales at jidesoft.com and ask for it. VISTA ICON SET Having trouble viewing the flash file above? You can also download the VISTA Icon Set preview zip file. VISTA ICON SET (NETWORK CATEGORY) Having trouble viewing the flash file above? You can also download the VISTA (NETWORK CATEGORY) Icon Set preview zip file. MAC OS X ICON SET Having trouble viewing the flash file above? You can also download the Mac OS X Icon Set preview zip file. MAC OS X ICON SET (NETWORK CATEGORY) Having trouble viewing the flash file above? You can also download the Mac OS X (NETWORK CATEGORY) Icon Set preview zip file. XP ICON SET Having trouble viewing the flash file above? You can also download the XP Icon Set preview zip file.

    Reply
  • Alternative STL-Style Iterators for MFC

    Posted by Legacy on 11/22/1999 12:00am

    Originally posted by: Dave Lorde

    I hate to do this to my own article, but an article describing a superior implementation of STL-style iterators for MFC has been published in the C/C++ User's Journal, September 1999, by Kevin Kostrzewa (tkkost@newsguy.com).

    The article and code should be available on-line from www.cuj.com.

    Dave

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: December 11, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT Market pressures to move more quickly and develop innovative applications are forcing organizations to rethink how they develop and release applications. The combination of public clouds and physical back-end infrastructures are a means to get applications out faster. However, these hybrid solutions complicate DevOps adoption, with application delivery pipelines that span across complex hybrid cloud and non-cloud environments. Check out this …

  • Due to internal controls and regulations, the amount of long term archival data is increasing every year. Since magnetic tape does not need to be periodically operated or connected to a power source, there will be no data loss because of performance degradation due to the drive actuator. Read this white paper to learn about a series of tests that determined magnetic tape is a reliable long-term storage solution for up to 30 years.

Most Popular Programming Stories

More for Developers

RSS Feeds