Chapter 13
Debugging Tips

Oren Nechushtan (theoren@math.tau.ac.il )

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 13.1), why and how to check your geometric predicates (Section 13.2), and what to do in order to evaluate handles and iterators during the debugging phase (Section 13.3).

13.1   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 Support Library documentation) to develop

13.2   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

bool curve_is_vertical ( X_curve cv) const

A binary cross-checker for this concept might look like

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;

}

and possibly be implemented as

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);
}

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.

13.3   Examining the values of variables

When using an interactive debugger, one often wishes to see the value of a variable, such as the y-value of a segment's source point. Thus one would naturally issue a command such as

    print segment.source().y()
This most often produces disappointingly unrevealing results, e.g., 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:

Note: You can also use watches to continuously examine such values during execution.