forked from boostorg/concept_check
removed cruft
[SVN r8065]
This commit is contained in:
@@ -905,292 +905,6 @@ 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>
|
||||
|
Reference in New Issue
Block a user