Traits Classes and Compile-Time Default Selection


One of the techniques we use extensively to increase the flexibility of the code while maintaining ease of use and a relatively small code base are traits classes [5]. We use traits to add or remove reference/pointer indirection and const specifiers to or from types. We also use them to select sensible iterator category defaults for the views' iterators. We describe the latter in some detail.

In order to identify the maximum iterator category for a view, we implemented a very interesting set of trait templates (see Figure 3). The algorithm is simple: map the two iterator category tags to integers, choose the smaller one, and map it back to the corresponding iterator category tag. Note that this has to be done at compile time. This kind of technique was invented by Todd Veldhuizen [6].

First we set up a template to give us the minimum value of two integers. Since this is a compile-time determination we cannot use the standard library min<> template; instead we use the ?: operator in an enumeration. Next we created the combine_traits template that encapsulates the whole algorithm. Notice that two of the template arguments to combine_traits are templates, map mapping categories to integers, and inv functioning as its inverse. Accordingly, the map template provides an enum x, and inv provides a type named type. Then we create the integer mapping for iterator category tags, specializing each type with a different value, in ascending order of their inclusiveness. The random_access_iterator_tag has the highest value, and input_iterator_tag has the lowest, reflecting their priority for our purpose.

Now when we call combine_iterator_categories with two iterator category tags, we end up with the two specializations for those categories. The combine_traits template specializes on the minimum value, and sets the typedef type to the correct type.

This is a very powerful use of the traits technique. It allows us to select the correct minimum iterator_trait_tag from the container or view specified, at compile time. For example, if a view is at best bidirectional, but the underlying container is random access, we will set the view's iterator catagory to bidirectional. We have used this technique to simplify the encoding of the views templates in a lot of places.