mirror of https://github.com/CGAL/cgal
221 lines
11 KiB
Plaintext
221 lines
11 KiB
Plaintext
/*!
|
|
|
|
\page chaptraits_classes Traits Classes
|
|
\author Bernd Gärtner (<TT>gaertner@inf.ethz.ch</TT>)
|
|
|
|
The concept of a traits class is central to %CGAL. The name ``traits
|
|
class'' comes from a standard \cpp design pattern
|
|
\cite cgal:m-tnutt-95; you may have heard about iterator traits which
|
|
follow this design pattern.
|
|
The traits class is used in template code to reflect properties (traits)
|
|
of the actual template argument.
|
|
On the lower levels, such as the number types,
|
|
the traits classes in %CGAL indeed follow this pattern.
|
|
However, in higher level packages the term traits class is used in a slightly
|
|
different spirit. The most noticeable change is that the traits class becomes
|
|
the template argument. This allows to bundle several template
|
|
arguments and provides more flexibility as explained in the subsequent
|
|
sections.
|
|
|
|
\section secwhat_is_a_traits_class What are traits classes in %CGAL?
|
|
|
|
The algorithms in CGAL's basic library are implemented as function templates
|
|
or class templates, usually having a template parameter whose name contains
|
|
the word `Traits`. This template parameter
|
|
represents a concept and so has a corresponding set of requirements that
|
|
define the interface between the algorithm and the geometric (or numeric)
|
|
primitives it uses. Any concrete class that serves as a model for this
|
|
concept is a traits class for the given algorithm or data structure.
|
|
|
|
\section secwhy_traits_classes Why are traits classes in %CGAL?
|
|
|
|
Using traits concepts as template parameters allows for customization
|
|
of the behavior of algorithms without changing implementations. At
|
|
least one model for each traits concept should be provided in %CGAL
|
|
(in the simplest case, the kernel models fit; see
|
|
Section \ref seckernel_traits ), but often more than one are provided
|
|
in order to supply certain customizations that users may want. The
|
|
user is also free to supply his or her own class as a model of the
|
|
traits concept when the desired tailoring is not present in the
|
|
library.
|
|
|
|
Traits classes allow for tailoring of algorithms not only at compile
|
|
time but also at run time. Some primitive operations that appear in
|
|
the traits class (in the form of functor types) may need additional
|
|
data that are not known at compile time. A standard example is the
|
|
following: we have three-dimensional points, but we want the convex
|
|
hull of the two-dimensional points that arise after projecting along
|
|
some direction in space, which is computed as the program runs. How
|
|
does the algorithm get to know about this direction? If there is a
|
|
traits class object as a parameter, the information can be provided to
|
|
the proper primitives through a proper initialization of the traits
|
|
class object. For this reason, traits class objects are passed as
|
|
parameters to functions.
|
|
|
|
\section sectraits_class_example An example - planar convex hulls
|
|
|
|
Consider convex hulls in the plane. What are the geometric primitives a
|
|
typical
|
|
convex hull algorithm uses? Of course, this depends on the algorithm, so
|
|
let us consider what is probably the simplest efficient algorithm, the
|
|
so-called Graham Scan. This algorithm first sorts the points from left to right,
|
|
and then builds the convex hull incrementally by adding one point after
|
|
another from the sorted list. To do this, it must at least know about
|
|
some point type, it should have some idea how to sort those points, and
|
|
it must be able to evaluate the orientation of a triple of points. The
|
|
signature of the Graham Scan algorithm in %CGAL (actually a variation
|
|
due to Andrews) is as follows:
|
|
|
|
\anchor ch_grham_andrew
|
|
|
|
\code{.cpp}
|
|
template <class InputIterator, class OutputIterator, class Traits>
|
|
OutputIterator
|
|
ch_graham_andrew( InputIterator first,
|
|
InputIterator beyond,
|
|
OutputIterator result,
|
|
const Traits & ch_traits);
|
|
\endcode
|
|
|
|
You notice that there is a template parameter named `Traits`,
|
|
and you also see a comment that mentions three identifiers (`Point_2`,
|
|
`Left_turn_2` and `Less_xy_2`) that have to be defined in the
|
|
scope of the traits class in order for the algorithm to work.
|
|
As you can guess, `Left_turn_2` is responsible for the orientation
|
|
test, while `Less_xy_2` does the sorting. So, obviously, the traits class
|
|
must provide these three identifiers. The requirements it has to satisfy
|
|
beyond that are documented in full with the concept ConvexHullTraits_2.
|
|
|
|
\subsection subsectraits_class_requirements Traits class requirements
|
|
|
|
Whenever you write a function or class that is parameterized with a traits
|
|
class, you must provide the requirements that class has to fulfill. These
|
|
requirements should be documented as a concept. For the
|
|
example above, if you look in the manual at the description of the concept
|
|
<A HREF="http://www.cgal.org/Manual/doc_html/basic_lib/Convex_hull_2_ref/Concept_ConvexHullTraits_2.html">ConvexHullTraits_2</A>, you will find that the
|
|
traits class itself and the identifiers that are mentioned have to meet the
|
|
following specifications:
|
|
|
|
\code{.cpp}
|
|
concept ConvexHullTraits_2 {
|
|
typename Point_2;
|
|
typename Left_xy_2;
|
|
typename Left_turn_2;
|
|
ConvexHullTraits_2(ConvexHullTraits_2& t);
|
|
|
|
Less_xy_2 less_xy_2_object();
|
|
|
|
Left_turn_2 left_turn_2_object();
|
|
};
|
|
\endcode
|
|
|
|
This ends the copied manual text. Some comments are in order here.
|
|
You might have expected `Less_xy_2` and `Left_turn_2` to be
|
|
simply member functions of the traits class.
|
|
Instead, they are functor types, and
|
|
there are member functions generating instances of these types, <I>i.e.</I>,
|
|
the actual functors. Reasons for this are the following.
|
|
<UL>
|
|
<LI>\em %CGAL is designed to have an \stl-like look-and-feel. All
|
|
algorithms in the \stl that depend on computational primitives
|
|
(like a sorting algorithm depending on a comparison operator),
|
|
receive those primitives via parameters which are functors. (The
|
|
only way to pass an actual function as a parameter would be via
|
|
function pointers.)
|
|
<LI>More flexibility. In contrast to member functions, functors can
|
|
carry data. For example, repeated calls to a function with only
|
|
slightly different parameters might be handled efficiently by
|
|
storing intermediate results. Functors are the natural framework
|
|
here. See \cite hhkps-aegk-01 for more exposition.
|
|
</UL>
|
|
|
|
If you really look up the documentation of the
|
|
<A HREF="http://www.cgal.org/Manual/doc_html/basic_lib/Convex_hull_2_ref/Concept_ConvexHullTraits_2.html">concept</A> in the manual, you will find a larger
|
|
list of requirements.
|
|
A traits class fulfilling this complete list of requirements can be used
|
|
for all of the 2-dimensional convex hull algorithms provided in \cgal.
|
|
For example, there are also
|
|
algorithms that require a sorting of points by angle, and a traits class for
|
|
that algorithm has to supply appropriate predicates for that. Still, to use
|
|
the Graham Scan, a traits class meeting only the specifications listed above
|
|
is sufficient.
|
|
|
|
\subsection subseccgal_traits_classes %CGAL-provided traits classes
|
|
|
|
As mentioned in Section \ref secwhat_is_a_traits_class ,
|
|
the traits class requirements define a concept. An
|
|
actual traits class that complies with these requirements is a model for
|
|
that concept. At least one such model must be provided for all %CGAL
|
|
algorithms.
|
|
Often this is called the default traits class.
|
|
Default traits classes are very
|
|
easy to use, especially when they are invoked via default arguments.
|
|
Look at the function <A HREF="#ch_graham_andrews">`ch_graham_andrews`</A>
|
|
again. The signature does not
|
|
tell the whole story. In reality, the third template parameter defaults
|
|
to the default traits class, and the last function parameter defaults to
|
|
a default instance of the default traits class. Of course, such behavior
|
|
must be specified in the <A HREF="http://www.cgal.org/Manual/doc_html/basic_lib/Convex_hull_2_ref/Function_ch_graham_andrew.html">description of the function</A>.
|
|
|
|
The implication is that a user can call `ch_graham_andrews` with
|
|
just three parameters, which delimit the iterator range to be handled and
|
|
supply the iterator for the result. The types
|
|
and primitives used by the algorithm in this case are the ones from the %CGAL 2D and 3D kernel.
|
|
|
|
In many cases, there are more than one traits classes provided by %CGAL. In the
|
|
case of convex hulls, for example, there are traits classes that interface
|
|
the algorithms with the geometry kernel of \leda. Though the user who has
|
|
a third-party geometric kernel will not be able to profit from the %CGAL or \leda traits, he or she can still provide own traits classes, which meet
|
|
the specified requirements.
|
|
|
|
\section seckernel_traits Kernel as traits
|
|
|
|
Most default traits classes in %CGAL are written in terms of the
|
|
types and classes provided in the %CGAL kernel. So one may wonder
|
|
why it is not possible to plug the kernel in as a traits class
|
|
directly. Ideally, it provides all the primitives an algorithm needs.
|
|
However, some algorithms and data structures require specialized
|
|
predicates that would not be appropriate to add to a general-purpose
|
|
kernel. The traits classes for these algorithms and data structures
|
|
should use kernel primitives wherever possible, and for those
|
|
primitives not provided by the kernel the fixed naming scheme for
|
|
predicates and constructions (Section \ref secnaming_scheme ) should
|
|
be used to make the library more consistent and thus easier to use.
|
|
|
|
\section sectraits_class_req_and_rec Requirements and recommendations
|
|
|
|
This section condenses the previous material into a few guidelines you
|
|
have to observe when you design a traits class yourself.
|
|
<UL>
|
|
<LI>Keep it small and simple. In particular, avoid redundant functionality
|
|
in the traits class requirements. For example, if you require
|
|
`Less_xy_2`, there is no reason to require `Greater_xy_2`,
|
|
because the latter can be constructed from the former. In general,
|
|
designing a good traits class requires a deep understanding of the
|
|
algorithm it is made for. Finding the "right" set of geometric
|
|
primitives required by the algorithm can be a nontrivial task.
|
|
However, spending effort on that task decreases the effort needed
|
|
later to implement traits classes and increases the ease of use of
|
|
the algorithm.
|
|
|
|
<LI>Obey the naming conventions (Section \ref secnaming_scheme ).
|
|
|
|
<LI>Use functors instead of member functions for the predicates
|
|
required. This is not only necessary for the kernel traits, it also
|
|
gives the benefit of more flexibility. For each type you must
|
|
provide a member function to get the actual functor and thus this
|
|
seems to increase the size of the traits class. However, if you
|
|
follow the naming scheme, the signatures of these functions are
|
|
obvious and obtainable mechanically.
|
|
|
|
<LI>Provide at least one model (which should normally be the kernel
|
|
traits class) for every traits concept.
|
|
|
|
<LI>Define and document a default traits class so the user need not
|
|
provide a traits class argument if customization of the algorithm is
|
|
not needed.
|
|
|
|
</UL>
|
|
|
|
*/
|