Introduction

The composite is a very powerful pattern, allowing a polymorphic tree of
objects to know about its structure. In order for this pattern to be successful,
it is usual to implement a variety of iterators over the hierarchy. This article
deals with the problem of typed iteration. Developing work from a previous
article, I have constructed a set of tree iterators that only return children of
a particular type.

Background

In a previous article on Composites
and Visitors, I dealt with the problem of creating a generic composite tree
and applying different visitation strategies to that tree. In the first of two
articles, I would like to deal with the problem of typed iteration. The second
article will generalize this concept into the use of functors to limit what tree
nodes are returned in an iteration. Typed iteration is particularly useful
because it handles the down-casting of entries into a particular base type.
Also, because the iterators are essentially STL compliant, they can be fed into
STL algorithms as required.

The composite is often used in areas such as expression trees or 3D graphics
applications. In a graphics application, the scene information is often stored
in a composite. A typed iterator would allow you to iterate over the scene
hierarchy picking out only material node, or only geometry nodes. This could be
particularly useful if you are running a triangulation visitor that will
triangulate all geometry nodes.

Using the code

To understand how to use the composite tree, you should read my previous
article. Essentially, the iterators are quite simple. The only major changes
from a conventional iterator are that in the begin and
operator++ functions, a simple while loop iterates
until we get the correctly typed entry. They must also check to ensure we have
not reached the end of the list. We cannot rely on the user comparing against
end() because the last item in the list might not be our required
type; therefore, the iterator has to carry around the value of
end(). This has two small down-sides. The first is that the
iterator is twice the size of a conventional iterator. The second is that if the
list grows during an iteration, the iterators will be invalidated. The second
issue is no different to the problem seen with certain STL containers such as
vectors or the associative containers. Showing the code, I have stripped out a
lot of extraneous details from the classes (such as the const versions), and
reformatted slightly. If you want to see the full original, then please download
the source.

Points of Interest

A few quick points to note. The first is the syntax required for templates
defined inside templates. This fooled me for a while as I wrote this class.
Especially, the syntax when you are defining the function bodies outside the
class. I specifically wrote some functions outside the class definition in the
typed_iterator to demonstrate this point. Note the fact that you
have to use the template <typename T> syntax twice. Once for
the outer class and once for the inner class. Once you realize that, the rest
makes sense.

The second point to note is the use of the member function template in order
to generate the necessary begin and end functions to work with the typed
iterators. This again has some oddities with the syntax, most notably the fact
that, in use, you have to specify the template argument (unlike most other
function templates). This is due to the fact that the begin and end functions
differ from each other only in return type, so the static polymorphism of the
compiler cannot resolve the correct one. In use, you would do iter =
begin<MyType>().

The more astute reader will notice that the typed iterator can be used as a
standard shallow or deep iterator simply by passing in the base type as the
template parameter. At first glance, this appears wasteful; however, there is a
way to more efficiently implement this using explicit template instantiation. I
will not go through the detail here, unless there are specific requests from any
readers.

History

6 Oct 04 : Version 1 released.

10 Oct 04 : Version 2 released to resolve crash in remove functions.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

Share

About the Author

I started programming on 8 bit machines as a teenager, writing my first compiled programming language before I was 16. I went on to study Engineering and Computer Science at Oxford University, getting a first and the University Prize for the best results in Computer Science. Since then I have worked in a variety of roles, involving systems management and development management on a wide variety of platforms. Now I manage a software development company producing CAD software for Windows using C++.

Outside computers, I am also the drummer in a band, The Unbelievers and we have just released our first album. I am a pretty good juggler and close up magician, and in my more insane past, I have cycled from Spain to Eastern Turkey, and cycled across the Namib desert.

>Typed iteration is particularly useful because it handles the downcasting of entries into a particular base type.

I've found this to be the major problem with composites. In the past I've had to either pollute the definition of the base class, or else fill the code with casts. Your method at least restricts the casts to a single location.

Minor point:
>The second point to note is the use of the member function template in order to generate the necessary begin and end functions..

I think you mean "member template function". A member function pointer can be a template argument, so a "member function template" is a bit ambiguous.

More interesting is this:
> We cannot rely on the user comparing against end() because the last item in the list might not be our required type; therefore the iterator has to carry around the value of end().

This isn't clear to me. Wouldn't it be possible to play around with the != operator so that you can compare them even if they are different types? Or is the problem that there is more than one possible iterator "beyond the end of the sequence"?
As it is, doesn't this restrict the available STL algorithms that can be used?

I could imagine: if end() is the right type, then just do a simple !=, but if it's the wrong type, you need to keep doing ++ until you either reach another of the correct type, or hit end(). I think this would let you avoid both restrictions, but maybe it would kill performance, so might not be worthwhile.

1. 'Your method at least restricts the casts to a single location.' - that was my whole idea - glad you like it.
2. 'I think you mean "member template function". ' - the ANSI C++ standard refers to member function templates. Essentially they are simply saying that a function template can be the member of a class. If you turn around the reference to be member template function then you could be refering to a function that is the member of a template. It doesn't really matter though so long as everyone understands you.
3. 'We cannot rely on the user comparing against end() ' - Essentially the problem is stopping the operator++ falling off the end of the list looking for something of the correct type. Obviously your standard loop when iterating over a list looks something like:

The comparison against end() is fine for terminating the list, the the increment also needs to know about the end. As far as I can see there are 2 ways to do this. One is to carry around the end() iterator, the second would be to flag the iterator as being about to end by looking ahead during the operator!=. The problem with the latter method is that it will restrict the ways you can use the not equals operator. Also, carrying around end() was the only way I could think of doing all the operations in constant time.

There are restrictions on the use of algorithms, but this is only similar to the std::vector. In std::vector if you insert an extra element during iteration, you invalidate the iterators. This is because the std::vector can reallocate storage at any time. My typed iterator will also invalidate the iterators in the event of any change in container size, but this time because end() has changed. Your restrictions on algorithms include, for example, the fact that you can't to a typed insert - but that wouldn't really make sense anyway.

This code will continue advancing the iterator until we either hit the end or get a match with the correct type. Unfortunately, in a normal STL iterator, the ++ operator does not know about the end of the list. This is because begin and end are container operations, rather than iterator operations. All the iterator knows about is its current place in the container - not the container itself. What I've had to do is store a reference to the end iterator so that the ++ operator can compare against end in the loop and prevent over-run. You could equally well store a pointer to the container in the iterator and generate the end iterator each time you enter the loop. It would be slightly slower, but may cope slightly better with the container changing during iteration.

I've just had a chance to check the naming of "member function template" or "member template function" in my big reference book. I use Vandevoorde and Josuttis, C++ Templates, which I find an excellent book for dealing with some of the trickier things in tempalte code. In there, he clearly refers to "member function templates" to refer to function templates contained within a class.

BTW, over the next few days, I'm going to put up another article that uses Functors to alter which objects an iterator will return - you may also find that useful for just the same reason as the way the typed iterator is useful.

You can check out the next article if you're interested. It's called Conditional Iteration over a Composite using Functors. I won't include a link in this reply because it is in the unedited reader contributions section at present - but I'm sure you can find it through the "articles by this author" link.