diff --git a/bibliography.html b/bibliography.html deleted file mode 100644 index 554a3a5..0000000 --- a/bibliography.html +++ /dev/null @@ -1,70 +0,0 @@ - - -
-Copyright © 2000 | -Jeremy Siek, Univ.of Notre Dame (jsiek@lsc.nd.edu) - |
-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. - -
-Jeremy Siek -contributed this library. X managed the formal review. - -
-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 per - se 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. - -
-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. - -
-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.. - -
- bad_error_eg.cpp: - 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 } -- -Here, the -std::stable_sort() algorithm is prototyped as follows: -
- template <class RandomAccessIterator> - void stable_sort(RandomAccessIterator first, RandomAccessIterator last); -- -Attempting to compile this code with Gnu C++ produces the following -compiler error. The output from other compilers is listed in the -Appendix. - -
-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 *> &' -- -In this case, the fundamental error is that -std:list::iterator does not model the concept of -RandomAccessIterator. 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. - -
-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. - -
-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 *> &' -- -This message rectifies several of the shortcomings of the standard -error messages. - -
- 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; - } -- - -
-The first part of the constraints() function includes -the requirements that correspond to the refinement relationship -between -RandomAccessIterator and the concepts which it builds upon: - -BidirectionalIterator and - -LessThanComparable. We could have instead used -CLASS_REQUIRES and placed these requirements in the class -body, however CLASS_REQUIRES uses C++ language features that -are less portable. - -
-Next we check that the iterator_category of the iterator is -either std::random_access_iterator_tag or a derived class. -After that we write out some code that corresponds to the valid -expressions of the -RandomAccessIterator concept. Typedefs can also be added to -enforce the associated types of the concept. - -
- template <class Iter> - struct RandomAccessIterator_concept - { - void constraints() { - function_requires< BidirectionalIteratorConcept<Iter> >(); - function_requires< LessThanComparableConcept<Iter> >(); - function_requires< ConvertibleConcept< - typename std::iterator_traits<Iter>::iterator_category, - std::random_access_iterator_tag> >(); - - 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; - }; -} -- -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). - - -
- 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; - }; - } -- -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< - copy_constructible_archetype< - assignable_archetype<> > > ValueType; - random_access_iterator_archetype<ValueType> ri; - std::stable_sort(ri, ri); - } -- - -
-Requirement Minimization Principle: Minimize the requirements -on the input parameters of a component to increase its reusability. - -
-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. - -
-The traditional notions of abstraction 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. - -
-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. - -
-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 -std::stable_sort() function requires that the value type of -the iterator be -LessThanComparable, which not only -includes operator<(), but also operator>(), -operator<=(), and operator>=(). -It turns out that std::stable_sort() only uses -operator<(). The question then arises: should -std::stable_sort() be specified in terms of the concept - -LessThanComparable or in terms of a concept that only -requires operator<()? - -
-We remark first that the use of -LessThanComparable does not really violate the requirement -minimization principle because all of the other operators can be -trivially implemented in terms of operator<(). By -``trivial'' we mean one line of code and a constant run-time cost. -More fundamentally, however, the use of -LessThanComparable does not violate the requirement minimization -principle because all of the comparison operators (<, ->, <=, >=) 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. - -
-For example, -the -maintainer of the std::stable_sort() may some day change the -implementation in places to use operator>() instead of -operator<(), 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 - -LessThanComparable is given as the requirement for -std::stable_sort(), then the maintainer is given a reasonable -amount of flexibility within which to work. - -
-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 -ForwardIterator and - -BidirectionalIterator). 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). - - -
- 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 RandomAccessIter> - void stable_sort(RandomAccessIter first, RandomAccessIter last) - { - function_requires< RandomAccessIteratorConcept- -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- -To check the type parameters of class templates, we provide the -class_requires class which can be used inside the body of a -class definition (whereas function_requires() can only be -used inside of a function body). class_requires 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. - -- void function_requires() - { - void (Concept::*x)() = BOOST_FPTR Concept::constraints; - ignore_unused_variable_warning(x); - } -
- template <class Concept> - class class_requires - { - typedef void (Concept::* function_pointer)(); - - template <function_pointer Fptr> - struct dummy_struct { }; - public: - typedef dummy_struct< BOOST_FPTR Concept::constraints > check; - }; -- -class_requires was not used in the implementation of the -Boost Concept Checking Library concept checks because several -compilers do not implement template parameters of function pointer -type. - - - -
- template <class Concept> - void function_requires(); -- -
- template <class Concept> - struct class_requires { - typedef ... check; - }; -- -
- // Make sure that Type1 and Type2 are exactly the same type. - // If they are not, then the nested typedef for type will - // not exist and cause a compiler error. - template <class Type1, class Type2> - struct require_same { - typedef ... type; - }; - // usage example - typedef typedef require_same- -::type req1; // this will compile OK - typedef typedef require_same ::type req1; // this will cause a compiler error -
- template <class T> struct Integer_concept; // Is T a built-in integer type? - template <class T> struct SignedIntegerConcept; // Is T a built-in signed integer type? - template <class X, class Y> struct ConvertibleConcept; // Is X convertible to Y? - template <class T> struct AssignableConcept; - template <class T> struct DefaultConstructibleConcept; - template <class T> struct CopyConstructibleConcept; - template <class T> struct BooleanConcept; - template <class T> struct EqualityComparableConcept; - // Is class T equality comparable on the left side with type Left? - template <class T, class Left> struct LeftEqualityComparableConcept; - template <class T> struct LessThanComparableConcept; -- -
- template <class Iter> struct TrivialIteratorConcept; - template <class Iter> struct Mutable_TrivialIteratorConcept; - template <class Iter> struct InputIteratorConcept; - template <class Iter, class T> struct OutputIteratorConcept; - template <class Iter> struct ForwardIteratorConcept; - template <class Iter> struct Mutable_ForwardIteratorConcept; - template <class Iter> struct BidirectionalIteratorConcept; - template <class Iter> struct Mutable_BidirectionalIteratorConcept; - template <class Iter> struct RandomAccessIteratorConcept; - template <class Iter> struct Mutable_RandomAccessIteratorConcept; -- -
- template <class Func, class Return> struct GeneratorConcept; - template <class Func, class Return, class Arg> struct UnaryFunctionConcept; - template <class Func, class Return, class First, class Second> struct BinaryFunctionConcept; - template <class Func, class Arg> struct UnaryPredicateConcept; - template <class Func, class First, class Second> struct BinaryPredicateConcept; - template <class Func, class First, class Second> struct Const_BinaryPredicateConcept {; -- -
- template <class C> struct ContainerConcept; - template <class C> struct Mutable_ContainerConcept; - - template <class C> struct ForwardContainerConcept; - template <class C> struct Mutable_ForwardContainerConcept; - - template <class C> struct ReversibleContainerConcept; - template <class C> struct Mutable_ReversibleContainerConcept; - - template <class C> struct RandomAccessContainerConcept; - template <class C> struct Mutable_RandomAccessContainerConcept; - - template <class C> struct SequenceConcept; - template <class C> struct FrontInsertionSequenceConcept; - template <class C> struct BackInsertionSequenceConcept; - - template <class C> struct AssociativeContainerConcept; - template <class C> struct UniqueAssociativeContainerConcept; - template <class C> struct MultipleAssociativeContainerConcept; - template <class C> struct SimpleAssociativeContainerConcept; - template <class C> struct PairAssociativeContainerConcept; - template <class C> struct SortedAssociativeContainerConcept; -- - -
- 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; -- -
- 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; -- -
- 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; -- -
-UNDER CONSTRUCTION -- -
Copyright © 2000 | -Jeremy Siek, -Univ.of Notre Dame (jsiek@lsc.nd.edu) - |