## Concept Covering and Archetypes

We have discussed how it is important to select the minimal requirements (concepts) for the inputs to a component, but it is equally important to verify that the chosen concepts cover the algorithm. That is, any possible user error should be caught by the concept checks and not let slip through. Concept coverage can be verified through the use of archetype classes. An archetype class is an exact implementation of the interface associated with a particular concept. The run-time behavior of the archetype class is not important, the functions can be left empty. A simple test program can then be compiled with the archetype classes as the inputs to the component. If the program compiles then one can be sure that the concepts cover the component. The following code shows the archetype class for the TrivialIterator concept. Some care must be taken to ensure that the archetype is an exact match to the concept. For example, the concept states that the return type of `operator*()` must be convertible to the value type. It does not state the more stringent requirement that the return type be `T&` or `const T&`. That means it would be a mistake to use `T&` or `const T&` for the return type of the archetype class. The correct approach is to create an artificial return type that is convertible to `T`, as we have done here with `input_proxy`. The validity of the archetype class test is completely dependent on it being an exact match with the concept, which must be verified by careful (manual) inspection.

      template <class T>
      struct input_proxy {
        operator const T&() {
          return static_object<T>::get(); // Get a reference without constructing
        }
      };
      template <class T>
      class trivial_iterator_archetype
      {
        typedef trivial_iterator_archetype self;
      public:
        trivial_iterator_archetype() { }
        trivial_iterator_archetype(const self&) { }
        self& operator=(const self&) { return *this;  }
        friend bool operator==(const self&, const self&) { return true; }
        friend bool operator!=(const self&, const self&) { return true; }
        input_proxy<T> operator*() { return input_proxy<T>(); }
      };

      namespace std {
        template <class T>
        struct iterator_traits< trivial_iterator_archetype<T> >
        {
          typedef T value_type;
        };
      }

Generic algorithms are often tested by being instantiated with a number of common input types. For example, one might apply `std::stable_sort()` with basic pointer types as the iterators. Though appropriate for testing the run-time behavior of the algorithm, this is not helpful for ensuring concept coverage because C++ types never match particular concepts, they often provide much more than the minimal functionality required by any one concept. That is, even though the function template compiles with a given type, the concept requirements may still fall short of covering the functions actual requirements. This is why it is important to compile with archetype classes in addition to testing with common input types.

The following is an excerpt from `stl_concept_covering.cpp` that shows how archetypes can be used to check the requirement documentation for `std::stable_sort()`. In this case, it looks like the CopyConstructible and Assignable requirements were forgotten in the SGI STL documentation (try removing those archetypes). The Boost archetype classes have been designed so that they can be layered. In this example the value type of the iterator is composed out of three archetypes. In the archetype class reference below, template parameters named `Base` indicate where the layered archetype can be used.

      {
        typedef less_than_comparable_archetype<
            sgi_assignable_archetype<> > ValueType;
        random_access_iterator_archetype<ValueType> ri;
        std::stable_sort(ri, ri);
      }

Next: Programming with Concepts
Prev: Creating Concept Checking Classes

Copyright © 2000 | Jeremy Siek, Univ.of Notre Dame (jsiek@lsc.nd.edu)