template <class RandomAccessIterator>
void stable_sort_constraints(RandomAccessIterator i)
{
typename std::iterator_traits<RandomAccessIterator>
::difference_type n;
i += n; // exercise the requirements for RandomAccessIterator
...
}
template <class RandomAccessIterator>
void stable_sort(RandomAccessIterator first, RandomAccessIterator last)
{
typedef void (*fptr_type)(RandomAccessIterator);
fptr_type x = &stable_sort_constraints;
...
}
There is often a large set of requirements that need to be checked,
and it would be cumbersome for the library implementor to write
constraint functions like stable_sort_constraints() for every
public function. Instead, we group sets of valid expressions
together, according to the definitions of the corresponding concepts.
For each concept we define a concept checking class template where the
template parameter is for the type to be checked. The class contains
a contraints() member function which exercises all of the
valid expressions of the concept. The objects used in the constraints
function, such as n and i, are declared as data
members of the concept checking class.
template <class Iter>
struct RandomAccessIterator_concept
{
void constraints()
{
i += n;
...
}
typename std::iterator_traits<RandomAccessIterator>
::difference_type n;
Iter i;
...
};
We can still use the function pointer mechanism to cause instantiation
of the constraints function, however now it will be a member function
pointer. To make it easy for the library implementor to invoke the
concept checks, we wrap the member function pointer mechanism in a
function named function_requires(). The following code
snippet shows how to use function_requires() to make sure
that the iterator is a
RandomAccessIterator.
template <class Iter>
void stable_sort(Iter first, Iter last)
{
function_requires< RandomAccessIteratorConcept<Iter> >();
...
}
The definition of the function_requires() is as follows. The
Concept is the concept checking class that has been
instantiated with the modeling type. We assign the address of the
constraints member function to the function pointer x, which
causes the instantiation of the constraints function and checking of
the concept's valid expressions. We then assign x to
x to avoid unused variable compiler warnings, and wrap
everything in a do-while loop to prevent name collisions.
template <class Concept>
void function_requires()
{
void (Concept::*x)() = BOOST_FPTR Concept::constraints;
ignore_unused_variable_warning(x);
}
To check the type parameters of class templates, we provide the
BOOST_CLASS_REQUIRE macro which can be used inside the body of a
class definition (whereas function_requires() can only be used
inside of a function body). This macro declares a nested class
template, where the template parameter is a function pointer. We then
use the nested class type in a typedef with the function pointer type
of the constraint function as the template argument. We use the
type_var and concept names in the nested class and
typedef names to help prevent name collisions.
#define BOOST_CLASS_REQUIRE(type_var, ns, concept) \
typedef void (ns::concept <type_var>::* func##type_var##concept)(); \
template <func##type_var##concept _Tp1> \
struct concept_checking_##type_var##concept { }; \
typedef concept_checking_##type_var##concept< \
BOOST_FPTR ns::concept<type_var>::constraints> \
concept_checking_typedef_##type_var##concept
In addition, there are versions of BOOST_CLASS_REQUIRE that
take more arguments, to handle concepts that include interactions
between two or more types. BOOST_CLASS_REQUIRE was not used
in the implementation of the BCCL concept checks because some
compilers do not implement template parameters of function pointer
type.
Next: Reference
Prev: Programming With Concepts
| Copyright © 2000 | Jeremy Siek(jsiek@osl.iu.edu) Andrew Lumsdaine(lums@osl.iu.edu) |