CGAL 5.6.1 - Manual
|
Much of the CGAL code contains checks. Some are there to check if the code behaves correctly, others check if the user calls routines in an acceptable manner. We describe the different categories of checks (Section Categories of checks), the usage of checks (Section Using checks), and a more selective means of controlling checks (Section Controlling checks at a finer granularity). Finally, a statement about exception handling is given (Section Exception handling).
It is forbidden to call std::abort
, std::exit
or assert
directly from CGAL, as these do not allow the user code to react after the error (application processes are killed). Thus, the default behavior of all checks is to throw exceptions for reporting failures.
There are five types of checks.
CGAL_destructor_assertion
is provided to ensure these checks are not made when the object is being cleaned up during exception handling. The according macro names all have the format CGAL_<check_type>
where <check_type>
can be one of
precondition
postcondition
assertion
warning
static_assertion
destructor_assertion
Failures of the first three types are errors and lead to a halt of the program, failures of the last one only lead to a warning. Checks of four categories can be marked with one or both of the following attributes:
Expensive
checks take considerable time to compute. "Considerable" is an imprecise phrase. Checks that add less than 10 percent to the execution time of their routine are not expensive. Checks that can double the execution time are. Somewhere in between lies the border line.
Exactness
checks rely on exact arithmetic. For example, if the intersection of two lines is computed, the postcondition of this routine may state that the intersection point lies on both lines. However, if the computation is done with double
s as the number type, this may not be the case, due to roundoff errors.
By definition, static assertions are both inexpensive and unaffected by precision management. Thus, the categories do not apply for static assertions.
The format is one of
CGAL_<check_type>
CGAL_expensive_<check_type>
CGAL_exactness_<check_type>
CGAL_expensive_exactness_<check_type>
By default, all standard checks (without any attribute) are enabled, while expensive and exactness checks are disabled. How this can be changed and how checks are actually used in the code are described in the next section.
Additionally, we provide macros CGAL_error()
and CGAL_error_msg(MSG_TEXT)
which are equivalent to always-failing assertions. However, they cannot be disabled.
The checks are implemented as preprocessor macros;
i.e., CGAL_<check_type>(<Cond>)
realizes a check of type <check_type>
that asserts the condition <Cond>
. For example,
checks the precondition that a given iterator range is not empty. If the check fails, an error message similar to
is written to the standard error stream and the program is aborted. If an additional explanation should be given to the user, macros CGAL_<check_type>_msg(<Cond>,<Msg>)
can be used. The text in <Msg>
is just appended to the failure message given above.
In case a check is more complicated and the computation does not fit into a single statement, the additional code can be encapsulated using CGAL_<check_type>_code(<Code>)
. This has the advantage that the computation is not done if the corresponding category is disabled. For an example, suppose an algorithm computes a convex polygon. Thus we want to check the postcondition that the polygon is indeed convex, which we consider an expensive check. The code would look like this.
As already mentioned above, the standard checks are enabled by default. This can be changed through the use of compile-time flags.
By setting the flag CGAL_NO_<CHECK_TYPE>
all checks of type <CHECK_TYPE>
are disabled, e.g. adding -DCGAL_NO_ASSERTIONS
to the compiler call switches off all checks for static and dynamic assertions. To disable all checks in the library, the flag CGAL_NDEBUG
can be set. Note that the standard flag NDEBUG
sets CGAL_NDEBUG
, but it also affects the assert
macro.
To enable expensive and exactness checks, respectively, the preprocessor macros CGAL_CHECK_EXPENSIVE
and CGAL_CHECK_EXACTNESS
have to be defined. However, exactness checks should only be turned on if the computation is done with some exact number type.
The macros and related compile-time flags described so far all operate on the whole library. CGAL offers the possibility to turn checks on and off just for the kernel. Kernel assertions are stated using the macro CGAL_kernel_assertion(<Cond>)
, and they can be disabled by defining the macros CGAL_KERNEL_NO_ASSERTIONS
or CGAL_NO_ASSERTIONS
. See the section Checks in STL Extensions, for details.
Moderns compilers, when their optimizers are activated, sometimes emit warnings about events that may occur. For example:
warning: array subscript is above array bounds [-Warray-bounds]
or:
warning: 'res' may be used uninitialized in this function [-Wmaybe-uninitialized]
Most false positives could be removed if the compiler knew that an integer variable is within given bounds, or that a Boolean variable is true. If CGAL_NDEBUG
is not defined, then a CGAL_assertion
is enough to instruct the compiler that a given condition is fulfilled. But, usually when the compiler optimizers are activated, CGAL_NDEBUG
is also defined, to speed up the generated binary code. In that case, CGAL_assume
can be used.
CGAL_assume
is identical to CGAL_assertion
when CGAL_NDEBUG
is not defined. But, even if CGAL_NDEBUG
is defined, its semantic uses compiler-specific instructions, such as __assume
from MSVC, or __builtin_unreachable
recognized by both clang and g++.
Some parts of the library use exceptions, but there is no general specific policy concerning exception handling in CGAL. It is nevertheless good to target exception safety, as much as possible. Good references on exception safety are: Appendix E of [11] (also available at https://www.stroustrup.com/3rd_safe0.html
), and [1] (also available at https://www.boost.org/community/exception_safety.html
). Any destructor which might throw an exception, including a destructor which uses the CGAL_destructor_assertion
macro, should be marked with the noexcept(!CGAL_ASSERTIONS_ENABLED)
.
Requirements:
assert
macro or the std::abort
or std::exit
functions) for all checks to assure that all CGAL invariant tests can be handled in a uniform way.