Go to the first, previous, next, last section, table of contents.
This chapter describes the way that GSL functions report and handle errors. By examining the status information returned by every GSL function you can determine whether it succeeded or failed, and if it failed you can find out what the precise cause of failure was. You can also define your own error handling functions to modify the default behavior of the library.
GSL follows the thread-safe error reporting conventions of the POSIX
Threads library. Functions in GSL return a non-zero error code to
indicate an error and 0
to indicate success.
int status = gsl_function(...) if (status) { /* an error occurred */ ..... /* the value of status specifies the type of error */ }
GSL routines report an error whenever they cannot perform the task requested of them. For example, a root-finding function would return a non-zero error code if could not converge to the requested accuracy, or exceeded a limit on the number of iterations. Situations like this are a normal occurrence when using any mathematical library and you should check the return status of the GSL functions that you call.
Whenever a GSL routine reports an error the return value specifies the
type of error. The return value is analogous to the value of the
variable errno
in the C library. However, the C library's
errno
is a global variable, which is not thread-safe (There can
be only one instance of a global variable per program. Different
threads of execution may overwrite errno
simultaneously).
Returning the error number directly avoids this problem. The caller can
examine the return code and decide what action to take, including
ignoring the error if it is not considered serious.
The error code numbers are defined in the file `gsl_errno.h'. They
all have the prefix GSL_
and expand to non-zero constant integer
values. Many of the error codes use the same base name as a
corresponding error code in C library. Here are some of the most common
error codes,
malloc
.
Here is an example of some code which checks the return value of a function where an error might be reported,
int status = gsl_fft_complex_radix2_forward (data, length); if (status) { if (status == GSL_EINVAL) { fprintf (stderr, "invalid argument, length=%d\n", length); } else { fprintf (stderr, "failed, gsl_errno=%d\n", status); } exit (-1); }
The function gsl_fft_complex_radix2
only accepts integer lengths
which are a power of two. If the variable length
is not a power
of two then the call to the library function will return
GSL_EINVAL
, indicating that the length argument is invalid. The
else
clause catches any other possible errors.
In addition to reporting errors the library also provides an optional error handler. The error handler is called by library functions when they are about to report an error (for example, just before they return). The purpose of the handler is to provide a function where a breakpoint can be set when running under the debugger.
The default behavior of the error handler is to print a short message
and call abort()
whenever an error is reported by the library. If
a library routine reports an error then the whole program will
core-dump. This is a safe default for lazy programmers who do not check
the return status of library routines (we don't encourage you to write
programs this way). If you turn off the default error handler or provide
your own error handler then it is your responsibility to check the
return values of the GSL routines.
All GSL error handlers have the type gsl_error_handler_t
, which is
defined in `gsl_errno.h',
This is the type of GSL error handler functions. An error handler will
be passed three arguments, specifying the reason for the error, the
source file in which it occurred, and the line number in that file.
The source file and line number are set at compile time using
the __FILE__
and __LINE__
directives in the preprocessor.
An error handler function returns type void
. Error handler
functions should be defined like this,
void handler (const char * reason, const char * file, int line)
To request the use of your own error handler you need to call the
function gsl_set_error_handler
which is also declared in
`gsl_errno.h',
This functions sets a new error handler, new_handler, for the GSL library routines. The previous handler is returned (so that you can restore it later). Note that the pointer to a user defined error handler function is stored in a static variable, so there can only be one error handler per program.
old_handler = gsl_set_error_handler (&my_error_handler); ..... /* code uses new handler */ gsl_set_error_handler (old_handler) ; /* restore old handler */
To use the default behavior (abort
on error) set the error
handler to NULL
,
old_handler = gsl_set_error_handler (NULL);
Here is a skeleton outline of a program which defines its own error handler. Imagine that the program does interactive data analysis -- there is a main loop which reads commands from the user and calls library routines with user-supplied arguments,
#include <setjmp.h> #include <gsl/gsl_errno.h> jmp_buf main_loop; void my_error_handler (const char *reason, const char *file, int line); main () { gsl_set_error_handler (&my_error_handler); while (1) { .... /* read command from user */ if (setjmp (main_loop) == 0) { .... /* call GSL routines requested by user */ } else { .... /* my_error_handler bailed out, GSL gave an error */ } } } void my_error_handler (const char *reason, const char *file, int line) { fprintf (stderr, "GSL error: %s\n", reason); longjmp (main_loop, 1); }
Before entering the interactive loop the program uses
gsl_set_error_handler
to provide its own error handler
my_error_handler
for GSL error reports. After this point the
function my_error_handler
will be invoked whenever an error is
reported by GSL. The new error handler prints the cause of the error
(the string reason
) and then does a non-local jump back to the
main loop. This would allow the user to fix the command which
caused the error and try again.
If you are writing numerical functions in a program which also uses GSL code you may find it convenient to adopt the same error reporting conventions as in the library.
To report an error you need to call the function gsl_error
with a
string describing the error and then return an appropriate error code
from gsl_errno.h
, or a special value, such as NaN
. For
convenience `gsl_errno.h' defines two macros to carry out these
steps:
This macro reports an error using the GSL conventions and returns a
status value of gsl_errno
. It expands to the following code fragment,
gsl_error (reason, __FILE__, __LINE__, gsl_errno) ; return gsl_errno ;
The macro definition in `gsl_errno.h' actually wraps the code
in a do { ... } while (0)
block to prevent possible
parsing problems.
Here is an example of how the macro could be used to report that a
routine did not achieve a requested tolerance. To report the error the
routine needs to return the error code GSL_ETOL
.
if (residual > tolerance) { GSL_ERROR("residual exceeds specified tolerance", GSL_ETOL) ; }
This macro is the same as GSL_ERROR
but returns a user-defined
status value of value instead of an error code. It can be used for
mathematical functions that return a floating point value.
Here is an example where a function needs to return a NaN
because
of a mathematical singularity,
if (x == 0) { GSL_ERROR_VAL("argument lies on singularity", GSL_ERANGE, GSL_NAN) ; }
Go to the first, previous, next, last section, table of contents.