removed cruft

[SVN r8065]
This commit is contained in:
Jeremy Siek
2000-10-29 21:53:41 +00:00
parent ec8756976a
commit 7c8a3d04b7

View File

@@ -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&amp;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 &lt;class RandomAccessIterator&gt;
void stable_sort_constraints(RandomAccessIterator i) {
typedef typename std::iterator_traits&lt;RandomAccessIterator&gt;
::value_type T;
const T a, b;
bool r = a &lt; b; // make sure less-than operator is defined
...
}
template &lt;class RandomAccessIterator&gt;
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 &lt;class T&gt;
struct LessThanComparable_concept
{
void constraints() {
// require comparison operators returning bool
r = a &lt; b || a &gt; b || a &lt;= b || a &gt;= 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 &lt;class RandomAccessIterator&gt;
void stable_sort(RandomAccessIterator first,
RandomAccessIterator last) {
typedef typename std::iterator_traits&lt;RandomAccessIterator&gt;
::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 &lt;type_var&gt;::*x)() = \
BOOST_FPTR concept##_concept &lt;type_var&gt;::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 &amp; 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 &lt;type_var&gt;::* func##type_var##concept)(); \
template &lt;func##type_var##concept _Tp1&gt; \
struct dummy_struct_##type_var##concept { }; \
typedef dummy_struct_##type_var##concept&lt; \
BOOST_FPTR concept##_concept &lt;type_var&gt;::constraints&gt; \
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 &lt;class InIter, class OutIter&gt;
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 &lt;class Iter&gt;
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 &lt;class InIter, class OutIter&gt;
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 &lt;boost/graph/graph_concepts.hpp&gt;
#include &lt;boost/graph/adjacency_list.hpp&gt;
#include &lt;boost/graph/edge_list.hpp&gt;
using namespace boost;
int main(int,char*[])
{
typedef adjacency_list&lt;listS,vecS,bidirectionalS&gt; AdjList;
REQUIRE(AdjList, VertexAndEdgeListGraph);
REQUIRE(AdjList, BidirectionalGraph);
typedef std::pair&lt;int, int&gt; E;
typedef edge_list&lt;E*, E, int&gt; 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>