template <class T> struct EqualityComparableConcept;Each concept checking class has a member function named constraints() which contains the valid expressions for the concept. To check whether some type, say foo, is EqualityComparable, we need to instantiate the concept checking class with foo: EqualityComparableConcept<foo> and then find a way to get the compiler to compile the constraints() function without actually calling it. The Boost Concept Checking Library defines two utilities that make this easy: function_requires() and BOOST_CLASS_REQUIRES. function_requires() function can be used in function bodies and the BOOST_CLASS_REQUIRES macro can be used inside class bodies. The function_requires() function takes no arguments, but has a template parameter for the concept checking class. This means that the instantiated concept checking class must be given as an explicit template argument, as shown below.
void some_function_using_foo() {
function_requires< EqualityComparableConcept<foo> >();
// ...
};
The BOOST_CLASS_REQUIRES macro can be used inside a class
definition to check whether some type models a concept.
struct some_class_using_foo {
BOOST_CLASS_REQUIRES(foo, EqualityComparableConcept);
};
To add concept checks to the std::stable_sort() function the
library implementor would simply insert function_requires()
at the top of std::stable_sort() to make sure the template
parameter type models
RandomAccessIterator. In addition, std::stable_sort()
requires that the value_type of the iterators be
LessThanComparable, so we also use function_requires() to
check this.
template <class RandomAccessIter>
void stable_sort(RandomAccessIter first, RandomAccessIter last)
{
function_requires< RandomAccessIteratorConcept<RandomAccessIter> >();
typedef typename std::iterator_traits<RandomAccessIter>::value_type value_type;
function_requires< LessThanComparableConcept<value_type> >();
...
}
Some concepts deal with more than one type. In this case the corresponding concept checking class will have multiple template parameters. The following example shows how function_requires() is used with the ReadWritePropertyMap concept which takes two type parameters: a property map and the key type for the map.
template <class IncidenceGraph, class Buffer, class BFSVisitor,
class ColorMap>
void breadth_first_search(IncidenceGraph& g,
typename graph_traits<IncidenceGraph>::vertex_descriptor s,
Buffer& Q, BFSVisitor vis, ColorMap color)
{
typedef typename graph_traits<IncidenceGraph>::vertex_descriptor Vertex;
function_requires< ReadWritePropertyMap<ColorMap, Vertex> >();
...
}
As an example of using class_requires we look at a concept
check that could be added to std::vector. One requirement
that is placed on the element type is that it must be Assignable.
We can check this by inserting
class_requires<AssignableConcept<T> > at the top
of the definition for std::vector.
namespace std {
template <class T>
struct vector {
typedef typename class_requires< AssignableConcept<T> >::check req;
...
};
}
Although the concept checks are designed for use by generic library
implementors, they can also be useful to end users. Sometimes one may
not be sure whether some type models a particular concept. This can
easily be checked by creating a small program and using
function_requires() with the type and concept in question.
The file stl_concept_checks.cpp
gives and example of applying the concept checks to STL
containers. The file is listed here:
#include <boost/concept_checks.hpp>
#include <iterator>
#include <set>
#include <map>
#include <vector>
#include <list>
#include <deque>
int
main()
{
typedef std::vector<int> Vector;
typedef std::deque<int> Deque;
typedef std::list<int> List;
function_requires< Mutable_RandomAccessContainer<Vector> >();
function_requires< BackInsertionSequence<Vector> >();
function_requires< Mutable_RandomAccessContainer<Deque> >();
function_requires< FrontInsertionSequence<Deque> >();
function_requires< BackInsertionSequence<Deque> >();
function_requires< Mutable_ReversibleContainer<List> >();
function_requires< FrontInsertionSequence<List> >();
function_requires< BackInsertionSequence<List> >();
typedef std::set<int> Set;
typedef std::multiset<int> MultiSet;
typedef std::map<int,int> Map;
typedef std::multimap<int,int> MultiMap;
function_requires< SortedAssociativeContainer<Set> >();
function_requires< SimpleAssociativeContainer<Set> >();
function_requires< UniqueAssociativeContainer<Set> >();
function_requires< SortedAssociativeContainer<MultiSet> >();
function_requires< SimpleAssociativeContainer<MultiSet> >();
function_requires< MultipleAssociativeContainer<MultiSet> >();
function_requires< SortedAssociativeContainer<Map> >();
function_requires< UniqueAssociativeContainer<Map> >();
function_requires< PairAssociativeContainer<Map> >();
function_requires< SortedAssociativeContainer<MultiMap> >();
function_requires< MultipleAssociativeContainer<MultiMap> >();
function_requires< PairAssociativeContainer<MultiMap> >();
return 0;
}