diff --git a/bad_error_eg.cpp b/bad_error_eg.cpp new file mode 100644 index 0000000..b801f77 --- /dev/null +++ b/bad_error_eg.cpp @@ -0,0 +1,11 @@ +#include +#include + +struct foo { + bool operator<(const foo&) const { return false; } +}; +int main(int, char*[]) { + std::list v; + std::stable_sort(v.begin(), v.end()); + return 0; +} diff --git a/bibliography.html b/bibliography.html new file mode 100644 index 0000000..554a3a5 --- /dev/null +++ b/bibliography.html @@ -0,0 +1,70 @@ + + + +Boost Graph Library: Bibliography + +C++ Boost + +
+ + +

Bibliography

+ +
+ +

1 +
Andrei Alexandrescu
+Better Template Error Messages.
+C/C++ Users Journal, March, 1999. + + +

2 +
Bjarne Stroustrup
+Design and Evolution of C++.
+Addison-Wesley, 1994 + +

3 +
+M. H. Austern. +
Generic Programming and the STL. +
Professional computing series. Addison-Wesley, 1999. + +

4 +
+David R. Musser and Atul Saini +
STL Tutorial and Reference Guide. +
Professional computing series. Addison-Wesley, 1996. + +

5 +
+A. A. Stepanov and M. Lee +
The Standard Template Library. +
ISO Programming Language C++ Project, May 1994. +
X3J16/94-0095, WG21/N0482 + + + +
+ +
+
+ + +
Copyright © 2000 +Jeremy Siek, Univ.of Notre Dame (jsiek@lsc.nd.edu) +
+ + + diff --git a/concept_checking.html b/concept_checking.html index 0c1a15f..ce970ae 100644 --- a/concept_checking.html +++ b/concept_checking.html @@ -1,6 +1,6 @@ + +

+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.. + +

Motivating Example

+ +We present a simple example to illustrate incorrect usage of a +template library and the resulting error messages. In the code below, +the generic std::stable_sort() algorithm from the Standard +Template Library (STL)[3, 4,5] is applied to +a linked list. + +
+  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. + +

    +
  1. The location of the error, line 9 of bad_error_eg.cpp + is not pointed to by the error message, despite the fact that Gnu C++ + prints up to 4 levels deep in the instantiation stack. +
  2. There is no textual correlation between the error message and the + documented requirements for std::stable_sort() and for + +RandomAccessIterator. +
  3. The error message is overly long, listing functions internal + to the STL that the user does not (and should not!) know or care + about. +
  4. 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. +
+ +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): + +
+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. + +
    +
  • The location of the error, bad_error_eg.cpp:9 is + specified in the error message. +
  • The message refers explicitly to concepts that the user can look + up in the STL documentation ( +RandomAccessIterator). +
  • The error message is now much shorter and does not reveal + internal STL functions. +
  • The presence of concept_checks.hpp and + constraints() in the error message alerts the user to the + fact that the error lies in the user code and not in the library + implementation. +
+ + +

Using Concept Checks

+ +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 REQUIRE macros can be used in function +bodies and the CLASS_REQUIRES macros can be used in class +bodies. The purpose of the macros is to check that the given +type models the specified concept. We use macros so +that the checks can be conditionally removed in situations where +faster compile time is needed. + +
+  REQUIRE(type, concept);
+  REQUIRE2(type1, type2, concept);
+  REQUIRE3(type1, type2, type3, concept);
+  REQUIRE4(type1, type2, type3, type4, concept);
+
+  CLASS_REQUIRES(type, concept);
+  CLASS_REQUIRES2(type1, type2, concept);
+  CLASS_REQUIRES3(type1, type2, type3, concept);
+  CLASS_REQUIRES4(type1, type2, type3, type4, concept);
+
+ +To add concept checks to the std::stable_sort() function the +library implementor would simply insert REQUIRE 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 REQUIRE to check this. + +
+  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);
+    ...
+  }
+
+ +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 REQUIRE indicates which concept checking +class should be used. In the above example, the use of +LessThanComparable indicates that the +LessThanComparable_concept class will be used to check the +value type. The Reference section below +lists the concept checking classes. + + + + +

+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 REQUIRE2 +macro 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;
+    REQUIRE2(ColorMap, Vertex, ReadWritePropertyMap);
+    ...
+  }
+
+ + +As an example of using the CLASS_REQUIRES macro 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(T, Assignable) +at the top of the definition for std::vector. + +
+  namespace std {
+    template <class T>
+    struct vector {
+      CLASS_REQUIRES(T, Assignable);
+      ...
+    };
+  }
+
+ + +

Creating Concept Checking Classes

+ +As an example of how to create a concept checking class, we look +at how to create the corresponding checks for the + +RandomAccessIterator concept. First, as a convention we name the +concept checking class after the concept, and add the prefix +``_concept''. Note that the REQUIRE macro expects +the prefix to be there. Next we must define a member function named +constraints() in which we will exercise the valid expressions +of the concept. The REQUIRE macro expects this function's +signature to appear exactly as it is appears below: a void +non-const member function with no parameters. + +

+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() {
+      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;
+  };
+}
+
+ +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). + + +

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 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);
+  }
+
+ + +

Programming with Concepts

+ +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 requirement minimization principle. + +

+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). + + +

Implementation

+ +Ideally we would like to catch, and indicate, the concept violation at +the point of instantiation. As mentioned in D&E[2], 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 --- but not executed. 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 std::stable_sort() function: + +
+  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 +macro named REQUIRE. The following code snippet shows how to +use REQUIRE to make sure that the iterator is a + +RandomAccessIterator. + +
+  template <class RandomAccessIter>
+  void stable_sort(RandomAccessIter first, RandomAccessIter last)
+  {
+    REQUIRE(RandomAccessIter, RandomAccessIterator);
+    ...
+  }
+
+ +The definition of the REQUIRE is as follows. The +type_var is the type we wish to check, and concept +is the name that corresponds to the concept checking class. 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. + +
+  #define REQUIRE(type_var, concept)                \
+  do {                                              \
+    void (concept##_concept <type_var>::*x)() =     \
+      concept##_concept <type_var>::constraints;    \
+    x = x;                                          \
+  } while (0)
+
+ +To check the type parameters of class templates, we provide the +CLASS_REQUIRES macro which can be used inside the body of a +class definition (whereas the REQUIRES 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 +type_var and concept names in the nested class and +typedef names to help prevent name collisions. + +
+  #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
+
+ +In addition, there are versions of REQUIRE and +CLASS_REQUIRES that take more arguments, to handle concepts +that include interactions between two or more types. +CLASS_REQUIRES was not used in the implementation of the STL +concept checks because several compilers do not implement template +parameters of function pointer type. + + + + +

Reference

+ + +

Macros

+ +
+  // Apply concept checks in function definitions.
+  REQUIRE(type, concept);
+  REQUIRE2(type1, type2, concept);
+  REQUIRE3(type1, type2, type3, concept);
+  REQUIRE4(type1, type2, type3, type4, concept);
+
+ +
+  // Apply concept checks in class definitions.
+  CLASS_REQUIRES(type, concept);
+  CLASS_REQUIRES2(type1, type2, concept);
+  CLASS_REQUIRES3(type1, type2, type3, concept);
+  CLASS_REQUIRES4(type1, type2, type3, type4, concept);
+
+ +
+  // Make sure that type1 and type2 are exactly the same type
+  REQUIRE_SAME_TYPE(type1, type2);
+
+ +

Basic Concept Checking Classes

+ +
+  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;
+
+ +

Iterator Concept Checking Classes

+ +
+  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;
+
+ +

Function Object Concept Checking Classes

+ +
+  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 {;
+
+ +

Container Concept Checking Classes

+ +
+  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;
+
+ + +

Basic Archetype Classes

+ +
+  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;
+
+ +

Iterator Archetype Classes

+ +
+  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;
+
+ +

Function Object Archetype Classes

+ +
+  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;
+
+ +

Container Archetype Classes

+ +
+UNDER CONSTRUCTION
+
+ +

History

+ +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. + +

Publications

+ + + +

Acknowledgements

+ +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[ +2]. +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. + + + + + + + +

@@ -155,11 +1198,7 @@ and publish the corresponding concept checks. Copyright © 2000 Jeremy Siek, Univ.of Notre Dame (jsiek@lsc.nd.edu)
-Lie-Quan Lee, Univ.of Notre Dame (llee1@lsc.nd.edu)
-Andrew Lumsdaine, -Univ.of Notre Dame (lums@lsc.nd.edu) +HREF="mailto:jsiek@lsc.nd.edu">jsiek@lsc.nd.edu) diff --git a/stl_concept_checks.cpp b/stl_concept_checks.cpp index ac74417..f0f0648 100644 --- a/stl_concept_checks.cpp +++ b/stl_concept_checks.cpp @@ -1,29 +1,10 @@ -//======================================================================= -// Copyright 1997, 1998, 1999, 2000 University of Notre Dame. -// Authors: Andrew Lumsdaine, Lie-Quan Lee, Jeremy G. Siek -// -// This file is part of the Boost Graph Library -// -// You should have received a copy of the License Agreement for the -// Boost Graph Library along with the software; see the file LICENSE. -// If not, contact Office of Research, University of Notre Dame, Notre -// Dame, IN 46556. -// -// Permission to modify the code and to distribute modified code is -// granted, provided the text of this NOTICE is retained, a notice that -// the code was modified is included with the above COPYRIGHT NOTICE and -// with the COPYRIGHT NOTICE in the LICENSE file, and that the LICENSE -// file is distributed with the modified code. -// -// LICENSOR MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. -// By way of example, but not limitation, Licensor MAKES NO -// REPRESENTATIONS OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY -// PARTICULAR PURPOSE OR THAT THE USE OF THE LICENSED SOFTWARE COMPONENTS -// OR DOCUMENTATION WILL NOT INFRINGE ANY PATENTS, COPYRIGHTS, TRADEMARKS -// OR OTHER RIGHTS. -//======================================================================= +// (C) Copyright Jeremy Siek 2000. Permission to copy, use, modify, +// sell and distribute this software is granted provided this +// copyright notice appears in all copies. This software is provided +// "as is" without express or implied warranty, and with no claim as +// to its suitability for any purpose. + #include -#include #include #include @@ -38,11 +19,6 @@ int main() { - typedef boost::trivial_iterator_skeleton TrivIterSkeleton; - typedef boost::mutable_trivial_iterator_skeleton MutTrivIterSkeleton; - REQUIRE(TrivIterSkeleton, TrivialIterator); - REQUIRE(MutTrivIterSkeleton, Mutable_TrivialIterator); - #if defined(_ITERATOR_) // VC++ STL implementation is not standard conformant and // fails to pass these concept checks diff --git a/stl_concept_covering.cpp b/stl_concept_covering.cpp new file mode 100644 index 0000000..b9b8fbd --- /dev/null +++ b/stl_concept_covering.cpp @@ -0,0 +1,256 @@ +// (C) Copyright Jeremy Siek 2000. Permission to copy, use, modify, +// sell and distribute this software is granted provided this +// copyright notice appears in all copies. This software is provided +// "as is" without express or implied warranty, and with no claim as +// to its suitability for any purpose. + +#include +#include +#include + +/* + + This file uses the archetype classes to verify whether the concept + requirements documented for the STL algorithms actually *cover* the + algorithms true requirements. + +*/ + +int +main() +{ + using namespace boost; + + //=========================================================================== + // First verify that the archetype classes model the concepts they + // are suppose to. + + { + typedef unary_function_archetype F; + REQUIRE3(F, int, int, UnaryFunction); + } + { + typedef binary_function_archetype F; + REQUIRE4(F, int, int, int, BinaryFunction); + } + { + typedef unary_predicate_archetype F; + REQUIRE2(F, int, UnaryPredicate); + } + { + typedef binary_predicate_archetype F; + REQUIRE3(F, int, int, BinaryPredicate); + } + { + typedef input_iterator_archetype Iter; + REQUIRE(Iter, InputIterator); + } + { + typedef output_iterator_archetype Iter; + REQUIRE2(Iter, int, OutputIterator); + } + { + typedef forward_iterator_archetype Iter; + REQUIRE(Iter, ForwardIterator); + } + { + typedef bidirectional_iterator_archetype Iter; + REQUIRE(Iter, BidirectionalIterator); + } + { + typedef random_access_iterator_archetype Iter; + REQUIRE(Iter, RandomAccessIterator); + } + + //=========================================================================== + // Non-mutating Algorithms + { + input_iterator_archetype< convertible_to_archetype< null_archetype > > in; + unary_function_archetype< null_archetype, null_archetype> f; + std::for_each(in, in, f); + } + { + /* + SGI STL Docs and the C++ standard (25.1.2) requirements for + std::for_each() are broken. They should be specified as follows: + + template + InputIterator find(InputIterator first, InputIterator last, + const LeftEqualityComparable& value) + { + REQUIRE(InputIterator, InputIterator); + typedef typename std::iterator_traits::value_type + value_type; + REQUIRE(LeftEqualityComparable, value_type, LeftEqualityComparable); + ... + } + */ + input_iterator_archetype< null_archetype > in; + left_equality_comparable_archetype< null_archetype > value(dummy_cons); + in = std::find(in, in, value); + } + { + input_iterator_archetype< convertible_to_archetype< null_archetype > > in; + unary_predicate_archetype< null_archetype > pred; + in = std::find_if(in, in, pred); + } + { + forward_iterator_archetype< equality_comparable_archetype<> > fo; + fo = std::adjacent_find(fo, fo); + } + { + forward_iterator_archetype< convertible_to_archetype< null_archetype > > fo; + binary_predicate_archetype pred; + fo = std::adjacent_find(fo, fo, pred); + } + { + /* SGI STL documentation is wrong. The value type of the + input iterator does not need to be equality comparable. + */ + input_iterator_archetype in; + typedef left_equality_comparable_archetype Right; + forward_iterator_archetype fo; + in = std::find_first_of(in, in, fo, fo); + } + { + typedef input_iterator_archetype InIter; + InIter in; + REQUIRE(InIter, InputIterator); + left_equality_comparable_archetype value(dummy_cons); + std::iterator_traits::difference_type + n = std::count(in, in, value); + } + { + typedef input_iterator_archetype InIter; + InIter in; + left_equality_comparable_archetype value(dummy_cons); + unsigned long n; + std::count(in, in, value, n); + } + { + typedef input_iterator_archetype< convertible_to_archetype > InIter; + InIter in; + unary_predicate_archetype pred; + std::iterator_traits::difference_type + n = std::count_if(in, in, pred); + } + { + input_iterator_archetype< convertible_to_archetype > in; + unary_predicate_archetype pred; + unsigned long n; + std::count_if(in, in, pred, n); + } + { + /* + SGI STL Documentation wrong. EqualityComparable not needed. + */ + typedef input_iterator_archetype InIter1; + InIter1 in1; + typedef left_equality_comparable_archetype Right; + typedef input_iterator_archetype InIter2; + InIter2 in2; + std::pair p = std::mismatch(in1, in1, in2); + } + { + typedef input_iterator_archetype< convertible_to_archetype > InIter; + InIter in1, in2; + binary_predicate_archetype pred; + std::pair p = std::mismatch(in1, in1, in2, pred); + } + { + // SGI STL docs: EqualityComparable not needed + input_iterator_archetype in1; + typedef left_equality_comparable_archetype Right; + input_iterator_archetype in2; + bool b = std::equal(in1, in1, in2); + } + { + input_iterator_archetype< convertible_to_archetype > in1, in2; + binary_predicate_archetype pred; + bool b = std::equal(in1, in1, in2, pred); + } + { + // SGI STL docs: EqualityComparable not needed + forward_iterator_archetype fo1; + typedef left_equality_comparable_archetype Right; + forward_iterator_archetype fo2; + fo1 = std::search(fo1, fo1, fo2, fo2); + } + { + // SGI STL docs: LeftEqualityComparable missing + // search() calls find() + typedef convertible_to_archetype Left; + forward_iterator_archetype fo1; + typedef convertible_to_archetype > Right; + forward_iterator_archetype fo2; + binary_predicate_archetype pred; + fo1 = std::search(fo1, fo1, fo2, fo2, pred); + } + { + // SGI STL docs: EqualityComparable not needed + forward_iterator_archetype fo; + left_equality_comparable_archetype value(dummy_cons); + int n; + fo = std::search_n(fo, fo, n, value); + } + { + // SGI STL docs: EqualityComparable not needed + forward_iterator_archetype< convertible_to_archetype > fo; + convertible_to_archetype value(dummy_cons); + binary_predicate_archetype pred; + int n; + fo = std::search_n(fo, fo, n, value, pred); + } + { + forward_iterator_archetype fo1; + typedef left_equality_comparable_archetype Right; + forward_iterator_archetype fo2; + fo1 = std::find_end(fo1, fo1, fo2, fo2); + } + { + // SGI STL docs: LeftEqualityComparable missing + typedef convertible_to_archetype Left; + forward_iterator_archetype fo1; + typedef convertible_to_archetype > Right; + forward_iterator_archetype fo2; + binary_predicate_archetype pred; + fo1 = std::find_end(fo1, fo1, fo2, fo2, pred); + } + + //=========================================================================== + // Mutating Algorithms + + // UNDER CONSTRUCTION + + { + // SGI STL docs missing CopyConstructible and Assignable + typedef equality_comparable_archetype< copy_constructible_archetype< + assignable_archetype<> > > T; + input_iterator_archetype in; + output_iterator_archetype out; + out = std::unique_copy(in, in, out); + } + + //=========================================================================== + // Sorting Algorithms + + // UNDER CONSTRUCTION + + { + typedef less_than_comparable_archetype< + copy_constructible_archetype< + assignable_archetype<> > > ValueType; + random_access_iterator_archetype ri; + std::stable_sort(ri, ri); + } + + //=========================================================================== + // Generalized Numeric Algorithms + + // UNDER CONSTRUCTION + + + return 0; +}