mirror of https://github.com/CGAL/cgal
171 lines
6.3 KiB
Plaintext
171 lines
6.3 KiB
Plaintext
/*!
|
|
|
|
\page chapdebugging Debugging Tips
|
|
\author Oren Nechushtan (<TT>theoren@math.tau.ac.il </TT>)
|
|
|
|
Efficient debugging techniques can become an asset when writing geometric
|
|
libraries such as \cgal.
|
|
This chapter discusses debugging-related issues, like how to use
|
|
the demo as a powerful debugger (Section \ref secgraphical_debugging ),
|
|
why and how to check your geometric predicates
|
|
(Section \ref secdebugging_cross_checker ), and what
|
|
to do in order to evaluate handles and iterators during the debugging phase
|
|
(Section \ref secdebugging_handles_and_it ).
|
|
|
|
\section secgraphical_debugging Graphical debugging
|
|
|
|
\cgal packages usually provide a graphical demo that demonstrates the
|
|
functionality in the package. Many times this demo is simply a fancier
|
|
version of a program that was used in the early stages of development as a
|
|
(graphical) debugging tool. In many cases, the output of a geometric
|
|
algorithm is much easier to interpret in graphical form than numeric
|
|
form. Thus you should use the powerful graphical output capabilities
|
|
of \cgal (see the <A HREF="http://www.cgal.org/Manual/doc_html/frameset/fsSupport.html">Support Library documentation</A>)
|
|
to develop
|
|
<UL>
|
|
<LI>programs that can be used for debugging the internal workings
|
|
of your package (<I>i.e.</I>, things a user may not have access to)
|
|
<LI>interesting and informative demos that highlight the features and,
|
|
at the same time, the absence or presence of bugs in your package.
|
|
Other demo/debugging programs can be found in the <TT>demo</TT>
|
|
directory
|
|
of every internal release and \cgal installation.
|
|
</UL>
|
|
|
|
\section secdebugging_cross_checker Cross-checkers
|
|
|
|
A cross-checker is a powerful means to allow for efficient
|
|
maintenance of your code. A cross-checker for a given concept is a
|
|
model of that concept that is
|
|
constructed from another model or models (one of which is the one you
|
|
wish to check). In order to implement the functionality required by
|
|
the concept, the cross-checker will use functions from the models upon
|
|
which it is built and perform tests for validity, etc. on them. If
|
|
the tests succeed, the cross-checker returns the expected result.
|
|
Otherwise, the cross-checker can generate an assertion violation or a
|
|
warning, depending on the severity of the offense.
|
|
|
|
For example, if you have a version of an algorithm, traits class, or
|
|
kernel that you know works, you can easily use this as an oracle for
|
|
another version of the algorithm, traits class, or kernel that you
|
|
wish to test. This is easily done because the code in \cgal is
|
|
highly templatized. The cross-checker would simply plug in the two
|
|
different versions of, say, your traits class, as the relevant
|
|
template parameters for two different instantiations of a class, say,
|
|
and then compare the results from using the two different
|
|
instantiations.
|
|
|
|
## An example: Traits class binary cross-checker ##
|
|
|
|
As a more concrete example, assume that you have a traits class concept
|
|
that requires a nested type `X_curve` and a function
|
|
|
|
\code{.cpp}
|
|
bool curve_is_vertical(const X_curve & cv) const;
|
|
\endcode
|
|
|
|
A binary cross-checker for this concept might look like
|
|
\code{.cpp}
|
|
template <class Traits1,class Traits2,class Adapter>
|
|
class Binary_traits_checker{
|
|
|
|
Traits1 tr1;
|
|
Traits1 tr2;
|
|
Adapter P;
|
|
|
|
public:
|
|
|
|
typedef typename Traits1::X_curve X_curve;
|
|
|
|
Traits_binary_checker(Traits1 tr1_,Traits2 tr2_,Adapter P_) :
|
|
tr1(tr1_),tr2(tr2_),P(P_){};
|
|
|
|
bool curve_is_vertical(const X_curve & cv) const;
|
|
|
|
}
|
|
\endcode
|
|
|
|
and possibly be implemented as
|
|
|
|
\code{.cpp}
|
|
bool curve_is_vertical(const X_curve & cv) const
|
|
{
|
|
CGAL_assertion(tr1.curve_is_vertical(cv)==tr2.curve_is_vertical(P(cv)));
|
|
return tr1.curve_is_vertical(cv);
|
|
}
|
|
\endcode
|
|
|
|
Notice that the class `Binary_traits_checker` has template parameters
|
|
named `Traits1` and `Traits2`, and a third parameter named
|
|
`Adapter`. One of the traits classes is the one to be tested and the
|
|
other is (presumably) a traits class that always gives the right answer.
|
|
The `Adapter` is needed since the `X_curve` types
|
|
for `Traits1` and `Traits2` might be different.
|
|
This cross-checker does nothing other then asserting that the two traits
|
|
classes return the same values by calling the
|
|
the counterparts in the member traits classes
|
|
(`tr1`,`tr2`) and comparing the results.
|
|
|
|
\section secdebugging_handles_and_it Examining the values of variables
|
|
|
|
When using an interactive debugger, one often wishes to see the value of
|
|
a variable, such as the \f$ y\f$-value of a segment's source point. Thus one
|
|
would naturally issue a command such as
|
|
\code{.cpp}
|
|
print segment.source().y()
|
|
\endcode
|
|
This most often produces disappointingly unrevealing results, <I>e.g.</I>, an
|
|
error message saying the value cannot be evaluated because functions may
|
|
be inlined.
|
|
|
|
We recommend the following approaches to work around (or avoid) this
|
|
and similar problems:
|
|
<UL>
|
|
<LI>Use the `Simple_cartesian` kernel
|
|
|
|
(Chapter \ref chapkernels ), which does not do reference
|
|
counting and uses no handles so data member values can be inspected
|
|
directly.
|
|
|
|
<LI>Print the values by following the pointers in the handles used to
|
|
represent objects. For example, for the segment above, the statement
|
|
\code{.cpp}
|
|
print s.ptr->start->ptr->e1
|
|
\endcode
|
|
is likely to work. This technique can also work for non-kernel
|
|
handles, such as `Halfedge_handle` and `Vertex_handle`.
|
|
One must know, of course, the right names for the data members,
|
|
but this you can find out by printing the things that pointers
|
|
point to. For example,
|
|
\code{.cpp}
|
|
print *s.ptr
|
|
\endcode
|
|
In the case of the planar map package, these handles are actually
|
|
polyhedron iterators.
|
|
If \f$ h\f$ is a halfedge of a planar map and you want to know the curve
|
|
associated with it, then if
|
|
\code{.cpp}
|
|
print h->curve()
|
|
\endcode
|
|
fails, try using
|
|
\code{.cpp}
|
|
print h.nt.node->cv
|
|
\endcode
|
|
instead.
|
|
|
|
For a vertex \f$ v\f$ of a planar map, if
|
|
\code{.cpp}
|
|
print v->point()
|
|
\endcode
|
|
fails, use
|
|
\code{.cpp}
|
|
print v.nt.node->p
|
|
\endcode
|
|
instead.
|
|
</UL>
|
|
|
|
Note: You can also use watches to continuously examine such values during
|
|
execution.
|
|
|
|
*/
|