mirror of
https://github.com/boostorg/concept_check.git
synced 2026-01-27 01:02:18 +01:00
1206 lines
50 KiB
HTML
1206 lines
50 KiB
HTML
<HTML>
|
|
<!--
|
|
-- Copyright (c) Jeremy Siek and Andrew Lumsdaine 2000
|
|
--
|
|
-- Permission to use, copy, modify, distribute and sell this software
|
|
-- and its documentation for any purpose is hereby granted without fee,
|
|
-- provided that the above copyright notice appears in all copies and
|
|
-- that both that copyright notice and this permission notice appear
|
|
-- in supporting documentation. We make no
|
|
-- representations about the suitability of this software for any
|
|
-- purpose. It is provided "as is" without express or implied warranty.
|
|
-->
|
|
<Head>
|
|
<Title>Concept Checking</Title>
|
|
<BODY BGCOLOR="#ffffff" LINK="#0000ee" TEXT="#000000" VLINK="#551a8b"
|
|
ALINK="#ff0000">
|
|
<IMG SRC="../../c++boost.gif"
|
|
ALT="C++ Boost" width="277" height="86">
|
|
|
|
<BR Clear>
|
|
|
|
<H1>
|
|
<A NAME="sec:concept-checking"></A>
|
|
header <a href="../../boost/pending/concept_checks.hpp">
|
|
<tt>boost/concept_checks.hpp</tt></a> and <a href="../../boost/pending/concept_archetypes.hpp">
|
|
<tt>boost/concept_archetypes.hpp</tt></a>
|
|
</H1>
|
|
|
|
<p>
|
|
Generic programming in C++ is characterized by the use of template
|
|
parameters to represent abstract data types (or ``concepts'').
|
|
However, the C++ language itself does not provide a mechanism for
|
|
explicitly handling concepts. As a result, it can be difficult to
|
|
insure that a concrete type meets the requirements of the concept it
|
|
is supposed to represent. Error messages resulting from incorrect use
|
|
of a concrete type can be particularly difficult to decipher. The
|
|
Boost Concept Checking Library provides mechanisms for checking
|
|
parameters in C++ template libraries. The mechanisms use standard C++
|
|
and introduce no run-time overhead. The main cost of using the
|
|
mechanism is in compile-time.
|
|
|
|
The documentation is organized into the following sections.
|
|
|
|
<OL>
|
|
<LI><a href="#introduction">Introduction</a></LI>
|
|
<LI><a href="#motivating-example">Motivating Example</a></LI>
|
|
<LI><a href="#using-concept-checks">Using Concept Checks</a></LI>
|
|
<LI><a href="#creating-concept-checks">Creating Concept Checking Classes</a></LI>
|
|
<LI><a href="#concept-covering">Concept Covering and Archetypes</a></LI>
|
|
<LI><a href="#programming-with-concepts">Programming With Concepts</a></LI>
|
|
<LI><a href="#implementation">Implementation</a></LI>
|
|
<LI><a href="#reference">Reference</a></LI>
|
|
<OL>
|
|
<LI><a href="#macros">Macros</a></LI>
|
|
<LI><a href="#basic-concepts">Basic Concept Checking Classes</a></LI>
|
|
<LI><a href="#iterator-concepts">Iterator Concept Checking Classes</a></LI>
|
|
<LI><a href="#function-object-concepts">Function Object Concept Checking Classes</a></LI>
|
|
<LI><a href="#container-concepts">Container Concept Checking Classes</a></LI>
|
|
<LI><a href="#basic-archetype">Basic Archetype Classes</a></LI>
|
|
<LI><a href="#iterator-archetype">Iterator Archetype Classes</a></LI>
|
|
<LI><a href="#function-object-archetype">Function Object Archetype Classes</a></LI>
|
|
<LI><a href="#container-archetype">Container Archetype Classes</a></LI>
|
|
</OL>
|
|
<LI><a href="#history">History</a></LI>
|
|
<LI><a href="#publications">Publications</a></LI>
|
|
<LI><a href="#acknowledgements">Acknowledgements</a></LI>
|
|
</OL>
|
|
|
|
<p>
|
|
<a href="http://www.boost.org/people/jeremy_siek.htm">Jeremy Siek</a>
|
|
contributed this library. X managed the formal review.
|
|
|
|
<h2><a name="introduction">Introduction</a></h2>
|
|
|
|
A <i>concept</i> is a set of requirements (valid expressions,
|
|
associated types, semantic invariants, complexity guarantees, etc.)
|
|
that a type must fulfill to be correctly used as arguments in a call
|
|
to a generic algorithm. In C++, concepts are represented by formal
|
|
template parameters to function templates (generic algorithms).
|
|
However, C++ has no explicit mechanism for representing concepts ---
|
|
template parameters are merely placeholders. By convention, these
|
|
parameters are given names corresponding to the concept that is
|
|
required, but a C++ compiler does not enforce compliance to the
|
|
concept when the template parameter is bound to an actual type.
|
|
|
|
<p>
|
|
Naturally, if a generic algorithm is invoked with a type that does not
|
|
fulfill at least the syntactic requirements of the concept, a
|
|
compile-time error will occur. However, this error will not <i>per
|
|
se</i> reflect the fact that the type did not meet all of the
|
|
requirements of the concept. Rather, the error may occur deep inside
|
|
the instantiation hierarchy at the point where an expression is not
|
|
valid for the type, or where a presumed associated type is not
|
|
available. The resulting error messages are largely uninformative and
|
|
basically impenetrable.
|
|
|
|
<p>
|
|
What is required is a mechanism for enforcing ``concept safety'' at
|
|
(or close to) the point of instantiation. The Boost Concept Checking
|
|
Library uses some standard C++ constructs to enforce early concept
|
|
compliance and that provides more informative error messages upon
|
|
non-compliance.
|
|
|
|
<!--
|
|
Move this stuff to history:
|
|
|
|
We have applied this mechanism to the SGI
|
|
implementation of STL, and the changes are now in the main
|
|
distribution.
|
|
-->
|
|
|
|
<p>
|
|
Note that this technique only addresses the syntactic
|
|
requirements of concepts (the valid expressions and associated types).
|
|
We do not address the semantic invariants or complexity guarantees,
|
|
which are also part of concept requirements..
|
|
|
|
<h2><a name="motivating-example">Motivating Example</a></h2>
|
|
|
|
We present a simple example to illustrate incorrect usage of a
|
|
template library and the resulting error messages. In the code below,
|
|
the generic <tt>std::stable_sort()</tt> algorithm from the Standard
|
|
Template Library (STL)[<a
|
|
href="bibliography.html#austern99:_gener_progr_stl">3</a>, <a
|
|
href="bibliography.html#IB-H965502">4</a>,<a
|
|
href="bibliography.html#stepa.lee-1994:the.s:TR">5</a>] is applied to
|
|
a linked list.
|
|
|
|
<pre>
|
|
<a href="./bad_error_eg.cpp">bad_error_eg.cpp</a>:
|
|
1 #include <list>
|
|
2 #include <algorithm>
|
|
3
|
|
4 struct foo {
|
|
5 bool operator<(const foo&) const { return false; }
|
|
6 };
|
|
7 int main(int, char*[]) {
|
|
8 std::list<foo> v;
|
|
9 std::stable_sort(v.begin(), v.end());
|
|
10 return 0;
|
|
11 }
|
|
</pre>
|
|
|
|
Here, the
|
|
<tt>std::stable_sort()</tt> algorithm is prototyped as follows:
|
|
<pre>
|
|
template <class RandomAccessIterator>
|
|
void stable_sort(RandomAccessIterator first, RandomAccessIterator last);
|
|
</pre>
|
|
|
|
Attempting to compile this code with Gnu C++ produces the following
|
|
compiler error. The output from other compilers is listed in the
|
|
Appendix.
|
|
|
|
<pre>
|
|
stl_algo.h: In function `void __merge_sort_loop<_List_iterator
|
|
<foo,foo &,foo *>, foo *, int>(_List_iterator<foo,foo &,foo *>,
|
|
_List_iterator<foo,foo &,foo *>, foo *, int)':
|
|
stl_algo.h:1448: instantiated from `__merge_sort_with_buffer
|
|
<_List_iterator<foo,foo &,foo *>, foo *, int>(
|
|
_List_iterator<foo,foo &,foo *>, _List_iterator<foo,foo &,foo *>,
|
|
foo *, int *)'
|
|
stl_algo.h:1485: instantiated from `__stable_sort_adaptive<
|
|
_List_iterator<foo,foo &,foo *>, foo *, int>(_List_iterator
|
|
<foo,foo &,foo *>, _List_iterator<foo,foo &,foo *>, foo *, int)'
|
|
stl_algo.h:1524: instantiated from here
|
|
stl_algo.h:1377: no match for `_List_iterator<foo,foo &,foo *> & -
|
|
_List_iterator<foo,foo &,foo *> &'
|
|
</pre>
|
|
|
|
In this case, the fundamental error is that
|
|
<tt>std:list::iterator</tt> does not model the concept of <a
|
|
href="http://www.sgi.com/Technology/STL/RandomAccessIterator.html">
|
|
RandomAccessIterator</a>. The list iterator is only bidirectional, not
|
|
fully random access (as would be a vector iterator). Unfortunately,
|
|
there is nothing in the error message to indicate this to the user.
|
|
|
|
<p>
|
|
To a C++ programmer having enough experience with template libraries
|
|
the error may be obvious. However, for the uninitiated, there are several
|
|
reasons why this message would be hard to understand.
|
|
|
|
<OL>
|
|
<LI> The location of the error, line 9 of <tt>bad_error_eg.cpp</tt>
|
|
is not pointed to by the error message, despite the fact that Gnu C++
|
|
prints up to 4 levels deep in the instantiation stack.
|
|
<LI> There is no textual correlation between the error message and the
|
|
documented requirements for <tt>std::stable_sort()</tt> and for
|
|
<a
|
|
href="http://www.sgi.com/Technology/STL/RandomAccessIterator.html">
|
|
RandomAccessIterator</a>.
|
|
<LI> The error message is overly long, listing functions internal
|
|
to the STL that the user does not (and should not!) know or care
|
|
about.
|
|
<LI> With so many internal library functions listed in the error
|
|
message, the programmer could easily infer that the error is due
|
|
to the library, rather than to his or her own code.
|
|
</OL>
|
|
|
|
The following is an example of what we might expect from a more
|
|
informative message (and is in fact what the Boost Concept Checking
|
|
Library produces):
|
|
|
|
<pre>
|
|
concept_checks.hpp: In method `void LessThanComparable_concept
|
|
<_List_iterator<foo,foo &,foo *> >::constraints()':
|
|
concept_checks.hpp:334: instantiated from `RandomAccessIterator_concept
|
|
<_List_iterator<foo,foo &,foo *> >::constraints()'
|
|
bad_error_eg.cpp:9: instantiated from `stable_sort<_List_iterator
|
|
<foo,foo &,foo *> >(_List_iterator<foo,foo &,foo *>,
|
|
_List_iterator<foo,foo &,foo *>)'
|
|
concept_checks.hpp:209: no match for `_List_iterator<foo,foo &,foo *> &
|
|
< _List_iterator<foo,foo &,foo *> &'
|
|
</pre>
|
|
|
|
This message rectifies several of the shortcomings of the standard
|
|
error messages.
|
|
|
|
<UL>
|
|
<LI> The location of the error, <tt>bad_error_eg.cpp:9</tt> is
|
|
specified in the error message.
|
|
<LI> The message refers explicitly to concepts that the user can look
|
|
up in the STL documentation (<a
|
|
href="http://www.sgi.com/Technology/STL/RandomAccessIterator.html">
|
|
RandomAccessIterator</a>).
|
|
<LI> The error message is now much shorter and does not reveal
|
|
internal STL functions.
|
|
<LI> The presence of <tt>concept_checks.hpp</tt> and
|
|
<tt>constraints()</tt> in the error message alerts the user to the
|
|
fact that the error lies in the user code and not in the library
|
|
implementation.
|
|
</UL>
|
|
|
|
|
|
<h2><a name="using-concept-checks">Using Concept Checks</a></h2>
|
|
|
|
The Boost Concept Checking Library defines several macros that make it
|
|
convenient to ensure that some type (or collection of types) models a
|
|
given concept. The <tt>REQUIRE</tt> macros can be used in function
|
|
bodies and the <tt>CLASS_REQUIRES</tt> macros can be used in class
|
|
bodies. The purpose of the macros is to check that the given
|
|
<i>type</i> models the specified <i>concept</i>. We use macros so
|
|
that the checks can be conditionally removed in situations where
|
|
faster compile time is needed.
|
|
|
|
<pre>
|
|
REQUIRE(<i>type</i>, <i>concept</i>);
|
|
REQUIRE2(<i>type1</i>, <i>type2</i>, <i>concept</i>);
|
|
REQUIRE3(<i>type1</i>, <i>type2</i>, <i>type3</i>, <i>concept</i>);
|
|
REQUIRE4(<i>type1</i>, <i>type2</i>, <i>type3</i>, <i>type4</i>, <i>concept</i>);
|
|
|
|
CLASS_REQUIRES(<i>type</i>, <i>concept</i>);
|
|
CLASS_REQUIRES2(<i>type1</i>, <i>type2</i>, <i>concept</i>);
|
|
CLASS_REQUIRES3(<i>type1</i>, <i>type2</i>, <i>type3</i>, <i>concept</i>);
|
|
CLASS_REQUIRES4(<i>type1</i>, <i>type2</i>, <i>type3</i>, <i>type4</i>, <i>concept</i>);
|
|
</pre>
|
|
|
|
To add concept checks to the <tt>std::stable_sort()</tt> function the
|
|
library implementor would simply insert <tt>REQUIRE</tt> at the top of
|
|
<tt>std::stable_sort()</tt> to make sure the template parameter type
|
|
models <a href="http://www.sgi.com/Technology/STL/RandomAccessIterator.html">
|
|
RandomAccessIterator</a>. In addition, <tt>std::stable_sort()</tt>
|
|
requires that the <tt>value_type</tt> of the iterators be
|
|
<a href="http://www.sgi.com/Technology/STL/LessThanComparable.html">
|
|
LessThanComparable</a>, so we also use <tt>REQUIRE</tt> to check this.
|
|
|
|
<pre>
|
|
template <class RandomAccessIter>
|
|
void stable_sort(RandomAccessIter first, RandomAccessIter last)
|
|
{
|
|
REQUIRE(RandomAccessIter, RandomAccessIterator);
|
|
typedef typename std::iterator_traits<RandomAccessIter>::value_type value_type;
|
|
REQUIRE(value_type, LessThanComparable);
|
|
...
|
|
}
|
|
</pre>
|
|
|
|
The Boost Concept Checking library includes a concept checking class
|
|
for each of the concepts described in the SGI STL documentation. The
|
|
second argument to <tt>REQUIRE</tt> indicates which concept checking
|
|
class should be used. In the above example, the use of
|
|
<tt>LessThanComparable</tt> indicates that the
|
|
<tt>LessThanComparable_concept</tt> class will be used to check the
|
|
value type. The <a href="#reference">Reference</a> section below
|
|
lists the concept checking classes.
|
|
|
|
|
|
<!-- There are a few places where the SGI STL documentation differs
|
|
from the corresponding requirements described in the C++ Standard. In
|
|
these cases we use the definition from the C++ Standard. -->
|
|
|
|
<p>
|
|
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 the <tt>REQUIRE2</tt>
|
|
macro is used with the <a
|
|
href="../property_map/ReadWritePropertyMap.html">ReadWritePropertyMap</a>
|
|
concept which takes two type parameters: a property map and the key
|
|
type for the map.
|
|
|
|
<pre>
|
|
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;
|
|
REQUIRE2(ColorMap, Vertex, ReadWritePropertyMap);
|
|
...
|
|
}
|
|
</pre>
|
|
|
|
|
|
As an example of using the <tt>CLASS_REQUIRES</tt> macro we look at a
|
|
concept check that could be added to <tt>std::vector</tt>. One
|
|
requirement that is placed on the element type is that it must be <a
|
|
href="http://www.sgi.com/Technology/STL/Assignable.html">Assignable</a>.
|
|
We can check this by inserting <tt>CLASS_REQUIRES(T, Assignable)</tt>
|
|
at the top of the definition for <tt>std::vector</tt>.
|
|
|
|
<pre>
|
|
namespace std {
|
|
template <class T>
|
|
struct vector {
|
|
CLASS_REQUIRES(T, Assignable);
|
|
...
|
|
};
|
|
}
|
|
</pre>
|
|
|
|
|
|
<h2><a name="creating-concept-checks">Creating Concept Checking Classes</a></h2>
|
|
|
|
As an example of how to create a concept checking class, we look
|
|
at how to create the corresponding checks for the
|
|
<a href="http://www.sgi.com/Technology/STL/RandomAccessIterator.html">
|
|
RandomAccessIterator</a> concept. First, as a convention we name the
|
|
concept checking class after the concept, and add the prefix
|
|
``<tt>_concept</tt>''. Note that the <tt>REQUIRE</tt> macro expects
|
|
the prefix to be there. Next we must define a member function named
|
|
<tt>constraints()</tt> in which we will exercise the valid expressions
|
|
of the concept. The <tt>REQUIRE</tt> macro expects this function's
|
|
signature to appear exactly as it is appears below: a <tt>void</tt>
|
|
non-const member function with no parameters.
|
|
|
|
<p>
|
|
The first part of the <tt>constraints()</tt> function includes
|
|
the requirements that correspond to the <i>refinement</i> relationship
|
|
between <a href="http://www.sgi.com/Technology/STL/RandomAccessIterator.html">
|
|
RandomAccessIterator</a> and the concepts which it builds upon:
|
|
<a href="http://www.sgi.com/Technology/STL/BidirectionalIterator.html">
|
|
BidirectionalIterator</a> and
|
|
<a href="http://www.sgi.com/Technology/STL/LessThanComparable.html">
|
|
LessThanComparable</a>. We could have instead used
|
|
<tt>CLASS_REQUIRES</tt> and placed these requirements in the class
|
|
body, however <tt>CLASS_REQUIRES</tt> uses C++ language features that
|
|
are less portable.
|
|
|
|
<p>
|
|
Next we check that the <tt>iterator_category</tt> of the iterator is
|
|
either <tt>std::random_access_iterator_tag</tt> or a derived class.
|
|
After that we write out some code that corresponds to the valid
|
|
expressions of the <a
|
|
href="http://www.sgi.com/Technology/STL/RandomAccessIterator.html">
|
|
RandomAccessIterator</a> concept. Typedefs can also be added to
|
|
enforce the associated types of the concept.
|
|
|
|
<pre>
|
|
template <class Iter>
|
|
struct RandomAccessIterator_concept
|
|
{
|
|
void constraints() {
|
|
REQUIRE(Iter, BidirectionalIterator);
|
|
REQUIRE(Iter, LessThanComparable);
|
|
REQUIRE2(typename std::iterator_traits<Iter>::iterator_category,
|
|
std::random_access_iterator_tag, Convertible);
|
|
|
|
i += n;
|
|
i = i + n; i = n + i;
|
|
i -= n;
|
|
i = i - n;
|
|
n = i - j;
|
|
i[n];
|
|
}
|
|
Iter a, b;
|
|
Iter i, j;
|
|
typename std::iterator_traits<Iter>::difference_type n;
|
|
};
|
|
}
|
|
</pre>
|
|
|
|
One potential pitfall in designing concept checking classes is using
|
|
more expressions in the constraint function than necessary. For
|
|
example, it is easy to accidentally use the default constructor to
|
|
create the objects that will be needed in the expressions (and not all
|
|
concepts require a default constructor). This is the reason we write
|
|
the constraint function as a member function of a class. The objects
|
|
involved in the expressions are declared as data members of the class.
|
|
Since objects of the constraints class template are never
|
|
instantiated, the default constructor for the concept checking class
|
|
is never instantiated. Hence the data member's default constructors
|
|
are never instantiated (C++ Standard Section 14.7.1 9).
|
|
|
|
|
|
<h2><a name="concept-covering">Concept Covering and Archetypes</a></h2>
|
|
|
|
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 <i>cover</i> 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 <i>archetype classes</i>. 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 <a
|
|
href="http://www.sgi.com/Technology/STL/trivial.html">TrivialIterator</a>
|
|
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 <tt>operator*()</tt> must be convertible to the value
|
|
type. It does not state the more stringent requirement that the return
|
|
type be <tt>T&</tt> or <tt>const T&</tt>. That means it would be a
|
|
mistake to use <tt>T&</tt> or <tt>const T&</tt> for the return type
|
|
of the archetype class. The correct approach is to create an
|
|
artificial return type that is convertible to <tt>T</tt>, as we have
|
|
done here with <tt>input_proxy</tt>. 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.
|
|
|
|
<pre>
|
|
template <class T>
|
|
struct input_proxy {
|
|
operator T() { return t; }
|
|
static T t;
|
|
};
|
|
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;
|
|
};
|
|
}
|
|
</pre>
|
|
|
|
Generic algorithms are often tested by being instantiated with a
|
|
number of common input types. For example, one might apply
|
|
<tt>std::stable_sort()</tt> 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.
|
|
|
|
<p>
|
|
The following is an excerpt from <a
|
|
href="./stl_concept_covering.cpp"><tt>stl_concept_covering.cpp</tt></a>
|
|
that shows how archetypes can be used to check the requirement
|
|
documentation for
|
|
<a href="http://www.sgi.com/Technology/STL/stable_sort.html">
|
|
<tt>std::stable_sort()</tt></a>. In this case, it looks like the <a
|
|
href="../utility/CopyConstructible.html">CopyConstructible</a> and <a
|
|
href="../utility/Assignable.html">Assignable</a> 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 <tt>Base</tt> indicate where the
|
|
layered archetype can be used.
|
|
|
|
<pre>
|
|
{
|
|
typedef less_than_comparable_archetype<
|
|
copy_constructible_archetype<
|
|
assignable_archetype<> > > ValueType;
|
|
random_access_iterator_archetype<ValueType> ri;
|
|
std::stable_sort(ri, ri);
|
|
}
|
|
</pre>
|
|
|
|
|
|
<h2><a name="programming-with-concepts">Programming with Concepts</a></h2>
|
|
|
|
The process of deciding how to group requirements into concepts and
|
|
deciding which concepts to use in each algorithm is perhaps the most
|
|
difficult (yet most important) part of building a generic library.
|
|
A guiding principle to use during this process is one we
|
|
call the <i>requirement minimization principle</i>.
|
|
|
|
<p>
|
|
<b>Requirement Minimization Principle:</b> Minimize the requirements
|
|
on the input parameters of a component to increase its reusability.
|
|
|
|
<p>
|
|
There is natural tension in this statement. By definition, the input
|
|
parameters must be used by the component in order for the component to
|
|
accomplish its task (by ``component'' we mean a function or class
|
|
template). The challenge then is to implement the component in such a
|
|
way that makes the fewest assumptions (the minimum requirements) about
|
|
the inputs while still accomplishing the task.
|
|
|
|
<p>
|
|
The traditional notions of <i>abstraction</i> tie in directly to the
|
|
idea of minimal requirements. The more abstract the input, the fewer
|
|
the requirements. Thus, concepts are simply the embodiment of generic
|
|
abstract data types in C++ template programming.
|
|
|
|
<p>
|
|
When designing the concepts for some problem domain it is important to
|
|
keep in mind their purpose, namely to express the requirements for the
|
|
input to the components. With respect to the requirement minimization
|
|
principle, this means we want to minimize concepts.
|
|
|
|
<p>
|
|
It is important to note, however, that
|
|
minimizing concepts does not mean simply
|
|
reducing the number of valid expressions
|
|
in the concept.
|
|
For example, the
|
|
<tt>std::stable_sort()</tt> function requires that the value type of
|
|
the iterator be <a
|
|
href="http://www.sgi.com/Technology/STL/LessThanComparable.html">
|
|
LessThanComparable</a>, which not only
|
|
includes <tt>operator<()</tt>, but also <tt>operator>()</tt>,
|
|
<tt>operator<=()</tt>, and <tt>operator>=()</tt>.
|
|
It turns out that <tt>std::stable_sort()</tt> only uses
|
|
<tt>operator<()</tt>. The question then arises: should
|
|
<tt>std::stable_sort()</tt> be specified in terms of the concept
|
|
<a
|
|
href="http://www.sgi.com/Technology/STL/LessThanComparable.html">
|
|
LessThanComparable</a> or in terms of a concept that only
|
|
requires <tt>operator<()</tt>?
|
|
|
|
<p>
|
|
We remark first that the use of <a
|
|
href="http://www.sgi.com/Technology/STL/LessThanComparable.html">
|
|
LessThanComparable</a> does not really violate the requirement
|
|
minimization principle because all of the other operators can be
|
|
trivially implemented in terms of <tt>operator<()</tt>. By
|
|
``trivial'' we mean one line of code and a constant run-time cost.
|
|
More fundamentally, however, the use of <a
|
|
href="http://www.sgi.com/Technology/STL/LessThanComparable.html">
|
|
LessThanComparable</a> does not violate the requirement minimization
|
|
principle because all of the comparison operators (<tt><</tt>,
|
|
<tt>></tt>, <tt><=</tt>, <tt>>=</tt>) are conceptually equivalent (in
|
|
a mathematical sense). Adding conceptually equivalent valid
|
|
expressions is not a violation of the requirement minimization
|
|
principle because no new semantics are being added --- only new
|
|
syntax. The added syntax increases re-usability.
|
|
|
|
<p>
|
|
For example,
|
|
the
|
|
maintainer of the <tt>std::stable_sort()</tt> may some day change the
|
|
implementation in places to use <tt>operator>()</tt> instead of
|
|
<tt>operator<()</tt>, since, after all, they are equivalent. Since the
|
|
requirements are part of the public interface, such a change could
|
|
potentially break client code. If instead
|
|
<a
|
|
href="http://www.sgi.com/Technology/STL/LessThanComparable.html">
|
|
LessThanComparable</a> is given as the requirement for
|
|
<tt>std::stable_sort()</tt>, then the maintainer is given a reasonable
|
|
amount of flexibility within which to work.
|
|
|
|
<p>
|
|
Minimality in concepts is a property associated with the underlying
|
|
semantics of the problem domain being represented. In the problem
|
|
domain of basic containers, requiring traversal in a single direction
|
|
is a smaller requirement than requiring traversal in both directions
|
|
(hence the distinction between <a
|
|
href="http://www.sgi.com/Technology/STL/ForwardIterator.html">
|
|
ForwardIterator</a> and
|
|
<a
|
|
href="http://www.sgi.com/Technology/STL/BidirectionalIterator.html">
|
|
BidirectionalIterator</a>). The semantic difference can be easily seen
|
|
in the difference between the set of concrete data structures that
|
|
have forward iterators versus the set that has bidirectional
|
|
iterators. For example, singly-linked lists would fall in the set of
|
|
data structures having forward iterators, but not bidirectional
|
|
iterators. In addition, the set of algorithms that one can implement
|
|
using only forward iterators is quite different than the set that can
|
|
be implemented with bidirectional iterators. Because of this, it is
|
|
important to factor families of requirements into rather fine-grained
|
|
concepts. For example, the requirements for iterators are factored
|
|
into the six STL iterator concepts (trivial, output, input, forward,
|
|
bidirectional, and random access).
|
|
|
|
|
|
<h2><a name="implementation">Implementation</a></h2>
|
|
|
|
Ideally we would like to catch, and indicate, the concept violation at
|
|
the point of instantiation. As mentioned in D&E[<a
|
|
href="bibliography.html#stroustrup94:_design_evolution">2</a>], the error
|
|
can be caught by exercising all of the requirements needed by the
|
|
function template. Exactly how the requirements (the valid
|
|
expressions in particular) are exercised is a tricky issue, since we
|
|
want the code to be compiled --- <i>but not executed</i>. Our
|
|
approach is to exercise the requirements in a separate function that
|
|
is assigned to a function pointer. In this case, the compiler will
|
|
instantiate the function but will not actually invoke it. In
|
|
addition, an optimizing compiler will remove the pointer assignment as
|
|
``dead code'' (though the run-time overhead added by the assignment
|
|
would be trivial in any case). It might be conceivable for a compiler
|
|
to skip the semantic analysis and compilation of the constraints
|
|
function in the first place, which would make our function pointer
|
|
technique ineffective. However, this is unlikely because removal of
|
|
unnecessary code and functions is typically done in later stages of a
|
|
compiler. We have successfully used the function pointer technique
|
|
with GNU C++, Microsoft Visual C++, and several EDG-based compilers
|
|
(KAI C++, SGI MIPSpro). The following code shows how this technique
|
|
can be applied to the <tt>std::stable_sort()</tt> function:
|
|
|
|
<pre>
|
|
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;
|
|
...
|
|
}
|
|
</pre>
|
|
|
|
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 <tt>stable_sort_constraints()</tt> 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 <tt>contraints()</tt> member function which exercises all of the
|
|
valid expressions of the concept. The objects used in the constraints
|
|
function, such as <tt>n</tt> and <tt>i</tt>, are declared as data
|
|
members of the concept checking class.
|
|
|
|
<pre>
|
|
template <class Iter>
|
|
struct RandomAccessIterator_concept {
|
|
void constraints() {
|
|
i += n;
|
|
...
|
|
}
|
|
typename std::iterator_traits<RandomAccessIterator>
|
|
::difference_type n;
|
|
Iter i;
|
|
...
|
|
};
|
|
</pre>
|
|
|
|
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
|
|
macro named <tt>REQUIRE</tt>. The following code snippet shows how to
|
|
use <tt>REQUIRE</tt> to make sure that the iterator is a
|
|
<a
|
|
href="http://www.sgi.com/Technology/STL/RandomAccessIterator.html">
|
|
RandomAccessIterator</a>.
|
|
|
|
<pre>
|
|
template <class RandomAccessIter>
|
|
void stable_sort(RandomAccessIter first, RandomAccessIter last)
|
|
{
|
|
REQUIRE(RandomAccessIter, RandomAccessIterator);
|
|
...
|
|
}
|
|
</pre>
|
|
|
|
The definition of the <tt>REQUIRE</tt> is as follows. The
|
|
<tt>type_var</tt> is the type we wish to check, and <tt>concept</tt>
|
|
is the name that corresponds to the concept checking class. We assign
|
|
the address of the constraints member function to the function pointer
|
|
<tt>x</tt>, which causes the instantiation of the constraints function
|
|
and checking of the concept's valid expressions. We then assign
|
|
<tt>x</tt> to <tt>x</tt> to avoid unused variable compiler warnings,
|
|
and wrap everything in a do-while loop to prevent name collisions.
|
|
|
|
<pre>
|
|
#define REQUIRE(type_var, concept) \
|
|
do { \
|
|
void (concept##_concept <type_var>::*x)() = \
|
|
concept##_concept <type_var>::constraints; \
|
|
x = x; \
|
|
} while (0)
|
|
</pre>
|
|
|
|
To check the type parameters of class templates, we provide the
|
|
<tt>CLASS_REQUIRES</tt> macro which can be used inside the body of a
|
|
class definition (whereas the <tt>REQUIRES</tt> macro 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
|
|
<tt>type_var</tt> and <tt>concept</tt> names in the nested class and
|
|
typedef names to help prevent name collisions.
|
|
|
|
<pre>
|
|
#define CLASS_REQUIRES(type_var, concept) \
|
|
typedef void (concept##_concept <type_var> \
|
|
::* func##type_var##concept)(); \
|
|
\
|
|
template <func##type_var##concept FuncPtr> \
|
|
struct dummy_struct_##type_var##concept { }; \
|
|
\
|
|
typedef dummy_struct_##type_var##concept< \
|
|
concept##_concept <type_var>::constraints> \
|
|
dummy_typedef_##type_var##concept
|
|
</pre>
|
|
|
|
In addition, there are versions of <tt>REQUIRE</tt> and
|
|
<tt>CLASS_REQUIRES</tt> that take more arguments, to handle concepts
|
|
that include interactions between two or more types.
|
|
<tt>CLASS_REQUIRES</tt> was not used in the implementation of the STL
|
|
concept checks because several compilers do not implement template
|
|
parameters of function pointer type.
|
|
|
|
|
|
|
|
|
|
<h2><a name="reference">Reference</a></h2>
|
|
|
|
|
|
<h3><a name="macros">Macros</a></h3>
|
|
|
|
<pre>
|
|
// Apply concept checks in function definitions.
|
|
REQUIRE(<i>type</i>, <i>concept</i>);
|
|
REQUIRE2(<i>type1</i>, <i>type2</i>, <i>concept</i>);
|
|
REQUIRE3(<i>type1</i>, <i>type2</i>, <i>type3</i>, <i>concept</i>);
|
|
REQUIRE4(<i>type1</i>, <i>type2</i>, <i>type3</i>, <i>type4</i>, <i>concept</i>);
|
|
</pre>
|
|
|
|
<pre>
|
|
// Apply concept checks in class definitions.
|
|
CLASS_REQUIRES(<i>type</i>, <i>concept</i>);
|
|
CLASS_REQUIRES2(<i>type1</i>, <i>type2</i>, <i>concept</i>);
|
|
CLASS_REQUIRES3(<i>type1</i>, <i>type2</i>, <i>type3</i>, <i>concept</i>);
|
|
CLASS_REQUIRES4(<i>type1</i>, <i>type2</i>, <i>type3</i>, <i>type4</i>, <i>concept</i>);
|
|
</pre>
|
|
|
|
<pre>
|
|
// Make sure that <i>type1</i> and <i>type2</i> are exactly the same type
|
|
REQUIRE_SAME_TYPE(<i>type1</i>, <i>type2</i>);
|
|
</pre>
|
|
|
|
<h3><a name="basic-concepts">Basic Concept Checking Classes</a></h3>
|
|
|
|
<pre>
|
|
template <class T> struct Integer_concept; // Is T a built-in integer type?
|
|
template <class T> struct SignedInteger_concept; // Is T a built-in signed integer type?
|
|
template <class X, class Y> struct Convertible_concept; // Is X convertible to Y?
|
|
template <class T> struct Assignable_concept;
|
|
template <class T> struct DefaultConstructible_concept;
|
|
template <class T> struct CopyConstructible_concept;
|
|
template <class T> struct Boolean_concept;
|
|
template <class T> struct EqualityComparable_concept;
|
|
// Is class T equality comparable on the left side with type Left?
|
|
template <class T, class Left> struct LeftEqualityComparable_concept;
|
|
template <class T> struct LessThanComparable_concept;
|
|
</pre>
|
|
|
|
<h3><a name="iterator-concepts">Iterator Concept Checking Classes</a></h3>
|
|
|
|
<pre>
|
|
template <class Iter> struct TrivialIterator_concept;
|
|
template <class Iter> struct Mutable_TrivialIterator_concept;
|
|
template <class Iter> struct InputIterator_concept;
|
|
template <class Iter, class T> struct OutputIterator_concept;
|
|
template <class Iter> struct ForwardIterator_concept;
|
|
template <class Iter> struct Mutable_ForwardIterator_concept;
|
|
template <class Iter> struct BidirectionalIterator_concept;
|
|
template <class Iter> struct Mutable_BidirectionalIterator_concept;
|
|
template <class Iter> struct RandomAccessIterator_concept;
|
|
template <class Iter> struct Mutable_RandomAccessIterator_concept;
|
|
</pre>
|
|
|
|
<h3><a name="function-object-concepts">Function Object Concept Checking Classes</a></h3>
|
|
|
|
<pre>
|
|
template <class Func, class Return> struct Generator_concept;
|
|
template <class Func, class Return, class Arg> struct UnaryFunction_concept;
|
|
template <class Func, class Return, class First, class Second> struct BinaryFunction_concept;
|
|
template <class Func, class Arg> struct UnaryPredicate_concept;
|
|
template <class Func, class First, class Second> struct BinaryPredicate_concept;
|
|
template <class Func, class First, class Second> struct Const_BinaryPredicate_concept {;
|
|
</pre>
|
|
|
|
<h3><a name="container-concepts">Container Concept Checking Classes</a></h3>
|
|
|
|
<pre>
|
|
template <class C> struct Container_concept;
|
|
template <class C> struct Mutable_Container_concept;
|
|
|
|
template <class C> struct ForwardContainer_concept;
|
|
template <class C> struct Mutable_ForwardContainer_concept;
|
|
|
|
template <class C> struct ReversibleContainer_concept;
|
|
template <class C> struct Mutable_ReversibleContainer_concept;
|
|
|
|
template <class C> struct RandomAccessContainer_concept;
|
|
template <class C> struct Mutable_RandomAccessContainer_concept;
|
|
|
|
template <class C> struct Sequence_concept;
|
|
template <class C> struct FrontInsertionSequence_concept;
|
|
template <class C> struct BackInsertionSequence_concept;
|
|
|
|
template <class C> struct AssociativeContainer_concept;
|
|
template <class C> struct UniqueAssociativeContainer_concept;
|
|
template <class C> struct MultipleAssociativeContainer_concept;
|
|
template <class C> struct SimpleAssociativeContainer_concept;
|
|
template <class C> struct PairAssociativeContainer_concept;
|
|
template <class C> struct SortedAssociativeContainer_concept;
|
|
</pre>
|
|
|
|
|
|
<h3><a name="basic-archetype">Basic Archetype Classes</a></h3>
|
|
|
|
<pre>
|
|
class null_archetype; // A type that models no concepts.
|
|
template <class Base = null_archetype> class default_constructible_archetype;
|
|
template <class Base = null_archetype> class assignable_archetype;
|
|
template <class Base = null_archetype> class copy_constructible_archetype;
|
|
template <class Left, class Base = null_archetype> class left_equality_comparable_archetype;
|
|
template <class Base = null_archetype> class equality_comparable_archetype;
|
|
template <class T, class Base = null_archetype> class convertible_to_archetype;
|
|
</pre>
|
|
|
|
<h3><a name="iterator-archetype">Iterator Archetype Classes</a></h3>
|
|
|
|
<pre>
|
|
template <class ValueType> class trivial_iterator_archetype;
|
|
template <class ValueType> class mutable_trivial_iterator_archetype;
|
|
template <class ValueType> class input_iterator_archetype;
|
|
template <class ValueType> class forward_iterator_archetype;
|
|
template <class ValueType> class bidirectional_iterator_archetype;
|
|
template <class ValueType> class random_access_iterator_archetype;
|
|
</pre>
|
|
|
|
<h3><a name="function-object-archetype">Function Object Archetype Classes</a></h3>
|
|
|
|
<pre>
|
|
template <class Arg, class Return> class unary_function_archetype;
|
|
template <class Arg1, class Arg2, class Return> class binary_function_archetype;
|
|
template <class Arg> class predicate_archetype;
|
|
template <class Arg1, class Arg2> class binary_predicate_archetype;
|
|
</pre>
|
|
|
|
<h3><a name="container-archetype">Container Archetype Classes</a></h3>
|
|
|
|
<pre>
|
|
UNDER CONSTRUCTION
|
|
</pre>
|
|
|
|
<h2><a name="history">History</a></h2>
|
|
|
|
An earlier version of this concept checking system was developed by
|
|
the author while working at SGI in their C++ compiler and library
|
|
group. The earlier version is now part of the SGI STL distribution. The
|
|
boost concept checking library differs from the concept checking in
|
|
the SGI STL in that the definition of concept checking classes has
|
|
been greatly simplified, at the price of less helpful verbiage in the
|
|
error messages.
|
|
|
|
<h2><a name="publications">Publications</a></h2>
|
|
|
|
<ul>
|
|
<li><a href="http://www.oonumerics.org/tmpw00/">
|
|
C++ Template Workshop 2000</a>, Concept Checking</li>
|
|
</ul>
|
|
|
|
<h2><a name="acknowledgements">Acknowledgements</a></h2>
|
|
|
|
The idea to use function pointers to cause instantiation is due to
|
|
Alexander Stepanov. I am not sure of the origin of the idea to use
|
|
expressions to do up-front checking of templates, but it did appear in
|
|
D&E[
|
|
<a href="bibliography.html#stroustrup94:_design_evolution">2</a>].
|
|
Thanks to Matt Austern for his excellent documentation and
|
|
organization of the STL concepts, upon which these concept checks
|
|
are based. Thanks to Boost members for helpful comments and
|
|
reviews.
|
|
|
|
<!----
|
|
|
|
|
|
To provide better diagnostics, the user's error needs to be caught
|
|
earlier, at the top of the <tt>std::stable_sort()</tt> function. As
|
|
mentioned in D&E[<a
|
|
href="./bibliography.html#stroustrup94:_design_evolution">2</a>], the
|
|
error can be caught by exercising all of the requirements needed by
|
|
the template function. Exactly how the requirements are exercised is a
|
|
tricky issue, since we want the code to be compiled but not executed
|
|
at run time. The solution we use is to exercise the requirements in a
|
|
separate function, and then assign the function to a function pointer.
|
|
This causes the compiler to instantiate the function without actually
|
|
invoking the function. Additionally, the pointer assignment can be
|
|
categorized as ``dead code'' and removed by optimizing compilers. The
|
|
following code shows how this technique can be applied to the
|
|
<tt>std::stable_sort()</tt> function.
|
|
|
|
<pre>
|
|
template <class RandomAccessIterator>
|
|
void stable_sort_constraints(RandomAccessIterator i) {
|
|
typedef typename std::iterator_traits<RandomAccessIterator>
|
|
::value_type T;
|
|
const T a, b;
|
|
bool r = a < b; // make sure less-than operator is defined
|
|
...
|
|
}
|
|
|
|
template <class RandomAccessIterator>
|
|
void stable_sort(RandomAccessIterator first,
|
|
RandomAccessIterator last) {
|
|
typedef void (*fptr_type)(RandomAccessIterator);
|
|
fptr_type x = &stable_sort_constraints;
|
|
...
|
|
}
|
|
</pre>
|
|
|
|
Many times there is a large set of requirements that need to be
|
|
checked, and it would be cumbersome for the library implementor to
|
|
write a constraints function like <tt>stable_sort_constraints()</tt>
|
|
for every public function. The solution to this is to group
|
|
requirements together in pre-defined constraint functions, which can
|
|
then be used at the top of template functions. The C++ standard and
|
|
the <a href="http://www.sgi.com/Technology/STL">SGI STL
|
|
documentation</a> already define a large number of standard
|
|
requirement groups, called <i>concepts</i>, and these concepts are
|
|
used to document the requirements for the standard generic
|
|
algorithms. The SGI STL implementation now includes constraint
|
|
functions for each of these concepts, and uses the functions to check
|
|
the template arguments of its algorithms.
|
|
|
|
<p>
|
|
There is a concept <a
|
|
href="http://www.sgi.com/Technology/STL/LessThanComparable.html">
|
|
LessThanComparable</a> that we can use to check the parameters of
|
|
<tt>stable_sort()</tt>. The constraints function for this concept is
|
|
shown below. The constraint function is written as a member function
|
|
to provide a convenient place to define the objects involved in the
|
|
expressions without accidentally adding requirements about the
|
|
objects' constructors. The expressions within the
|
|
<tt>constraints()</tt> function correspond to the <i>valid
|
|
expressions</i> section of the concept documentation. Typedefs can also
|
|
be added to enforce the <i>associated types</i> of the concept.
|
|
|
|
<pre>
|
|
template <class T>
|
|
struct LessThanComparable_concept
|
|
{
|
|
void constraints() {
|
|
// require comparison operators returning bool
|
|
r = a < b || a > b || a <= b || a >= b;
|
|
}
|
|
T a, b;
|
|
bool r;
|
|
};
|
|
</pre>
|
|
|
|
To make it easy for the library implementor to invoke the concept
|
|
checks, we wrap the function pointer mechanism in a macro named
|
|
<tt>REQUIRE</tt>. The following code snippet shows how to use
|
|
<tt>REQUIRE</tt> to make sure that the value type of the iterator is
|
|
<a href="http://www.sgi.com/Technology/STL/LessThanComparable.html">
|
|
LessThanComparable</a>}.
|
|
|
|
<pre>
|
|
template <class RandomAccessIterator>
|
|
void stable_sort(RandomAccessIterator first,
|
|
RandomAccessIterator last) {
|
|
typedef typename std::iterator_traits<RandomAccessIterator>
|
|
::value_type ValueType;
|
|
REQUIRE(ValueType, LessThanComparable);
|
|
...
|
|
}
|
|
</pre>
|
|
|
|
The definition of <tt>REQUIRE</tt> macro is shown below. The macro
|
|
makes sure that the <tt>type_var</tt> is a type that models the
|
|
<tt>concept</tt>. The macro creates a member function pointer to the
|
|
<tt>constraints()</tt> function of the given concept checking class,
|
|
which has been instantiated with <tt>type_var</tt>. The macro then
|
|
assigns the pointer to itself to avoid warnings about unused
|
|
variables. Finally, we wrap this in a do-while loop to prevent name
|
|
clashes. The <tt>##</tt> is the preprocessor's concatenation
|
|
operator. Here it is used to append the suffix <tt>_concept</tt> to
|
|
the concept name.
|
|
|
|
<pre>
|
|
#define REQUIRE(type_var, concept) \
|
|
do { \
|
|
void (concept##_concept <type_var>::*x)() = \
|
|
BOOST_FPTR concept##_concept <type_var>::constraints; \
|
|
x = x; } while (0)
|
|
</pre>
|
|
|
|
The macro <tt>BOOST_FPTR</tt> is there to handle a portability
|
|
problem. Some compilers require the use of & to get the address of
|
|
a function, others don't.
|
|
|
|
<pre>
|
|
#if defined(__GNUC__) || defined(__KCC) || defined(__ghs)
|
|
#define BOOST_FPTR &
|
|
#else
|
|
#define BOOST_FPTR
|
|
#endif
|
|
</pre>
|
|
|
|
It is also desirable to check the template arguments to classes,
|
|
however the <tt>REQUIRE</tt> macro can not be used inside the body of
|
|
a class declaration since consists of statements. Also, it is not a
|
|
good idea to use <tt>REQUIRE</tt> in the constructors (or any other
|
|
members of a class) because this does not ensure that the concept
|
|
checks will be compiled. To solve this problem a variation on the
|
|
function pointer technique is used. A nested class template is created
|
|
whose template parameter is a function pointer (yes, this is legal
|
|
C++). The nested class is then instantiated (by using it in a typedef)
|
|
with a pointer to the appropriate <tt>constraints()</tt> member
|
|
function. We use the <tt>type_var</tt> and <tt>concept</tt> names in
|
|
the name of the nested class template to guard against name clashes
|
|
with other concept checks in the same class. The following gives the
|
|
definition of the <tt>CLASS_REQUIRES</tt> macro.
|
|
|
|
<pre>
|
|
#define CLASS_REQUIRES(type_var, concept) \
|
|
typedef void (concept##_concept <type_var>::* func##type_var##concept)(); \
|
|
template <func##type_var##concept _Tp1> \
|
|
struct dummy_struct_##type_var##concept { }; \
|
|
typedef dummy_struct_##type_var##concept< \
|
|
BOOST_FPTR concept##_concept <type_var>::constraints> \
|
|
dummy_typedef_##type_var##concept
|
|
</pre>
|
|
|
|
<tt>CLASS_REQUIRES</tt> was not used in the implementation of the STL
|
|
concept checks because only a few compilers support using function
|
|
pointers in class template parameters.
|
|
|
|
|
|
-->
|
|
|
|
|
|
|
|
|
|
|
|
<!--
|
|
|
|
|
|
<P>
|
|
The C++ language does not provide direct support for ensuring that
|
|
template arguments meet the requirements demanded of them by the
|
|
generic algorithm or template class. This means that if the user makes
|
|
an error, the resulting compiler error will point to somewhere deep
|
|
inside the implementation of the generic algorithm, giving an error
|
|
that may not be easy to match with the cause.
|
|
|
|
<P>
|
|
We have developed a method for forcing the compiler to give better
|
|
error messages. The idea is to exercise all the requirements placed on
|
|
the template arguments at the very beginning of the generic algorithm.
|
|
We have created some macros and a methodology for how to do this.
|
|
|
|
<P>
|
|
Suppose we wish to add concept checks to the STL <a
|
|
href="http://www.sgi.com/Technology/STL/copy.html"><TT>copy()</TT></a>
|
|
algorithm, which has the following prototype.
|
|
<PRE>
|
|
template <class InIter, class OutIter>
|
|
OutIter copy(InIter first, InIter last, OutIter result);
|
|
</PRE>
|
|
We will need to make sure the <TT>InIter</TT> is a model of <a
|
|
href="http://www.sgi.com/Technology/STL/InputIterator.html">InputIterator</a>
|
|
and that <TT>OutIter</TT> is a model of <a
|
|
href="http://www.sgi.com/Technology/STL/OutputIterator.html">OutputIterator</a>. The
|
|
first step is to create the code that will exercise the expressions
|
|
associated with each of these concepts. The following is the concept
|
|
checking class for <a
|
|
href="http://www.sgi.com/Technology/STL/OutputIterator.html">OutputIterator</a>.
|
|
The iterator objects <TT>i</TT> and <TT>j</TT> are data members of the
|
|
concept checking class which would normally require the <TT>Iter</TT>
|
|
type to be <a
|
|
href="http://www.sgi.com/Technology/STL/DefaultConstructible.html">DefaultConstructible</a>. However,
|
|
we never create instances of concept checking classes so this
|
|
requirement is not induced.
|
|
|
|
<P>
|
|
<PRE>
|
|
template <class Iter>
|
|
struct OutputIterator {
|
|
CLASS_REQUIRES(Iter, Assignable);
|
|
void constraints() {
|
|
(void)*i; // require dereference operator
|
|
++i; // require preincrement operator
|
|
i++; // require postincrement operator
|
|
*i++ = *j; // require postincrement and assignment
|
|
}
|
|
Iter i, j;
|
|
};
|
|
</PRE>
|
|
|
|
<P>
|
|
Once the concept checking classes are complete, one simply needs to
|
|
invoke them at the beginning of the generic algorithm using our
|
|
<TT>REQUIRE</TT> macro. Here's what the STL <TT>copy()</TT> algorithm
|
|
looks like with the concept checks inserted.
|
|
|
|
<P>
|
|
<PRE>
|
|
template <class InIter, class OutIter>
|
|
OutIter copy(InIter first, InIter last, OutIter result)
|
|
{
|
|
REQUIRE(OutIter, OutputIterator);
|
|
REQUIRE(InIter, InputIterator);
|
|
return copy_aux(first, last, result, VALUE_TYPE(first));
|
|
}
|
|
</PRE>
|
|
|
|
<P>
|
|
Looking back at the <TT>OutputIterator</TT> concept checking class, you
|
|
might wonder why we used the <TT>CLASS_REQUIRES</TT> macro instead of
|
|
just <TT>REQUIRE</TT>. The reason for this is that different tricks are
|
|
needed to force compilation of the checking code when invoking the
|
|
macro from inside a class definition instead of a function.
|
|
|
|
<P>
|
|
For the most part, the user does not need to know how to create
|
|
concept checks and insert them in generic algorithms, however it is
|
|
good to know what they are. This will make the error messages coming
|
|
from libraries that use concept checks easier to understand. Also if
|
|
you are unsure about whether you can use a certain class with a
|
|
particular algorithm, or whether some custom-made class of your own is
|
|
compatible, a good first check is to invoke the appropriate concept
|
|
checker.
|
|
|
|
<P>
|
|
Here's a quick example program that one could write to see if the
|
|
types <a
|
|
href="../graph/docs/adjacency_list.html"><TT>adjacency_list</TT></a> and <a
|
|
href="../graph/docs/edge_list.html"><TT>edge_list</TT></a> really model the
|
|
concepts they claim to.
|
|
|
|
<P>
|
|
<PRE>
|
|
#include <boost/graph/graph_concepts.hpp>
|
|
#include <boost/graph/adjacency_list.hpp>
|
|
#include <boost/graph/edge_list.hpp>
|
|
|
|
using namespace boost;
|
|
|
|
int main(int,char*[])
|
|
{
|
|
typedef adjacency_list<listS,vecS,bidirectionalS> AdjList;
|
|
REQUIRE(AdjList, VertexAndEdgeListGraph);
|
|
REQUIRE(AdjList, BidirectionalGraph);
|
|
|
|
typedef std::pair<int, int> E;
|
|
typedef edge_list<E*, E, int> EdgeList;
|
|
REQUIRE(EdgeList, EdgeListGraph);
|
|
|
|
return 0;
|
|
}
|
|
</PRE>
|
|
|
|
<P>
|
|
If you create your own generic algorithms we highly encourage you to use
|
|
and publish the corresponding concept checks.
|
|
|
|
-->
|
|
|
|
<br>
|
|
<HR>
|
|
<TABLE>
|
|
<TR valign=top>
|
|
<TD nowrap>Copyright © 2000</TD><TD>
|
|
<A HREF=http://www.boost.org/people/jeremy_siek.htm>Jeremy Siek</A>,
|
|
Univ.of Notre Dame (<A
|
|
HREF="mailto:jsiek@lsc.nd.edu">jsiek@lsc.nd.edu</A>)
|
|
</TD></TR></TABLE>
|
|
|
|
</BODY>
|
|
</HTML>
|