diff --git a/concept_checking.html b/concept_checking.html index 57d71a0..accd03e 100644 --- a/concept_checking.html +++ b/concept_checking.html @@ -51,7 +51,8 @@ The documentation is organized into the following sections.
Note that this technique only addresses the syntactic requirements of concepts (the valid expressions and associated types). @@ -234,55 +227,76 @@ RandomAccessIterator).
- REQUIRE(type, concept); - REQUIRE2(type1, type2, concept); - REQUIRE3(type1, type2, type3, concept); - REQUIRE4(type1, type2, type3, type4, concept); + template <class T> struct EqualityComparableConcept; +- CLASS_REQUIRES(type, concept); - CLASS_REQUIRES2(type1, type2, concept); - CLASS_REQUIRES3(type1, type2, type3, concept); - CLASS_REQUIRES4(type1, type2, type3, type4, concept); +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 class_requires. +function_requires() function can be used in function bodies +and the class_requires class 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 class_requires is a class template with a template +parameter for the concept checking class. Accessing the nested +check type within class_requires triggers the the +concept checking. + +
+ struct some_class_using_foo { + typedef class_requires< EqualityComparableConcept<foo> >::check req1; + };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 +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 REQUIRE to check this. +LessThanComparable, so we also use function_requires() to +check this.
template <class RandomAccessIter> void stable_sort(RandomAccessIter first, RandomAccessIter last) { - REQUIRE(RandomAccessIter, RandomAccessIterator); + function_requires< RandomAccessIteratorConcept<RandomAccessIter> >(); typedef typename std::iterator_traits<RandomAccessIter>::value_type value_type; - REQUIRE(value_type, LessThanComparable); + function_requires< LessThanComparableConcept<value_type> >(); ... }-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 function_requires() is used with the ReadWritePropertyMap concept which takes two type parameters: a property map and the key type for the map. @@ -306,24 +320,25 @@ type for the map. Buffer& Q, BFSVisitor vis, ColorMap color) { typedef typename graph_traits<IncidenceGraph>::vertex_descriptor Vertex; - REQUIRE2(ColorMap, Vertex, ReadWritePropertyMap); + function_requires< ReadWritePropertyMap<ColorMap, Vertex> >(); ... } -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 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(T, Assignable) -at the top of the definition for std::vector. +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 { - CLASS_REQUIRES(T, Assignable); + typedef typename class_requires< AssignableConcept<T> >::check req; ... }; } @@ -333,9 +348,10 @@ at the top of the definition for std::vector. 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 the -REQUIRE macro with the type and concept in question. The -file stl_concept_checks.cpp +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: @@ -356,37 +372,37 @@ containers. The file is listed here: typedef std::deque<int> Deque; typedef std::list<int> List; - REQUIRE(Vector, Mutable_RandomAccessContainer); - REQUIRE(Vector, BackInsertionSequence); + function_requires< Mutable_RandomAccessContainer<Vector> >(); + function_requires< BackInsertionSequence<Vector> >(); - REQUIRE(Deque, Mutable_RandomAccessContainer); - REQUIRE(Deque, FrontInsertionSequence); - REQUIRE(Deque, BackInsertionSequence); + function_requires< Mutable_RandomAccessContainer<Deque> >(); + function_requires< FrontInsertionSequence<Deque> >(); + function_requires< BackInsertionSequence<Deque> >(); - REQUIRE(List, Mutable_ReversibleContainer); - REQUIRE(List, FrontInsertionSequence); - REQUIRE(List, BackInsertionSequence); + 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; - REQUIRE(Set, SortedAssociativeContainer); - REQUIRE(Set, SimpleAssociativeContainer); - REQUIRE(Set, UniqueAssociativeContainer); + function_requires< SortedAssociativeContainer<Set> >(); + function_requires< SimpleAssociativeContainer<Set> >(); + function_requires< UniqueAssociativeContainer<Set> >(); - REQUIRE(MultiSet, SortedAssociativeContainer); - REQUIRE(MultiSet, SimpleAssociativeContainer); - REQUIRE(MultiSet, MultipleAssociativeContainer); + function_requires< SortedAssociativeContainer<MultiSet> >(); + function_requires< SimpleAssociativeContainer<MultiSet> >(); + function_requires< MultipleAssociativeContainer<MultiSet> >(); - REQUIRE(Map, SortedAssociativeContainer); - REQUIRE(Map, UniqueAssociativeContainer); - REQUIRE(Map, PairAssociativeContainer); + function_requires< SortedAssociativeContainer<Map> >(); + function_requires< UniqueAssociativeContainer<Map> >(); + function_requires< PairAssociativeContainer<Map> >(); - REQUIRE(MultiMap, SortedAssociativeContainer); - REQUIRE(MultiMap, MultipleAssociativeContainer); - REQUIRE(MultiMap, PairAssociativeContainer); + function_requires< SortedAssociativeContainer<MultiMap> >(); + function_requires< MultipleAssociativeContainer<MultiMap> >(); + function_requires< PairAssociativeContainer<MultiMap> >(); return 0; } @@ -434,10 +450,11 @@ enforce the associated types of the concept. 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); + 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; @@ -739,8 +756,9 @@ 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 +function named function_requires(). The following code +snippet shows how to use function_requires() to make sure +that the iterator is a RandomAccessIterator. @@ -749,155 +767,156 @@ RandomAccessIterator. template <class RandomAccessIter> void stable_sort(RandomAccessIter first, RandomAccessIter last) { - REQUIRE(RandomAccessIter, RandomAccessIterator); + function_requires< RandomAccessIteratorConcept-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. +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.>(); ... }
- #define REQUIRE(type_var, concept) \ - do { \ - void (concept##_concept <type_var>::*x)() = \ - concept##_concept <type_var>::constraints; \ - x = x; \ - } while (0) + templateTo 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. +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); + }
- #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 + 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; + };-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. - +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.
- // Apply concept checks in function definitions. - REQUIRE(type, concept); - REQUIRE2(type1, type2, concept); - REQUIRE3(type1, type2, type3, concept); - REQUIRE4(type1, type2, type3, type4, concept); + template <class Concept> + void function_requires(); ++ +
+ template <class Concept> + struct class_requires { + typedef ... check; + };
- // 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); + // 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 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; + 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 LeftEqualityComparable_concept; - template <class T> struct LessThanComparable_concept; + template <class T, class Left> struct LeftEqualityComparableConcept; + template <class T> struct LessThanComparableConcept;
- 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; + 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 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 {; + 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 Container_concept; - template <class C> struct Mutable_Container_concept; + template <class C> struct ContainerConcept; + template <class C> struct Mutable_ContainerConcept; - template <class C> struct ForwardContainer_concept; - template <class C> struct Mutable_ForwardContainer_concept; + template <class C> struct ForwardContainerConcept; + template <class C> struct Mutable_ForwardContainerConcept; - template <class C> struct ReversibleContainer_concept; - template <class C> struct Mutable_ReversibleContainer_concept; + template <class C> struct ReversibleContainerConcept; + template <class C> struct Mutable_ReversibleContainerConcept; - template <class C> struct RandomAccessContainer_concept; - template <class C> struct Mutable_RandomAccessContainer_concept; + template <class C> struct RandomAccessContainerConcept; + template <class C> struct Mutable_RandomAccessContainerConcept; - template <class C> struct Sequence_concept; - template <class C> struct FrontInsertionSequence_concept; - template <class C> struct BackInsertionSequence_concept; + template <class C> struct SequenceConcept; + template <class C> struct FrontInsertionSequenceConcept; + template <class C> struct BackInsertionSequenceConcept; - 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; + 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;diff --git a/stl_concept_checks.cpp b/stl_concept_checks.cpp index f8a64ee..021e428 100644 --- a/stl_concept_checks.cpp +++ b/stl_concept_checks.cpp @@ -25,11 +25,13 @@ #include