For using the igraph C library
igraph functions can run into various problems preventing them from normal operation. The user might have supplied invalid arguments, e.g. a non-square matrix when a square-matrix was expected, or the program has run out of memory while some more memory allocation is required, etc.
By default igraph aborts the program when it runs into an error. While this behavior might be good enough for smaller programs, it is without doubt avoidable in larger projects. Please read further if your project requires more sophisticated error handling. You can safely skip the rest of this chapter otherwise.
If igraph runs into an error - an invalid argument was supplied to a function, or we've ran out of memory - the control is transferred to the error handler function.
The default error handler is igraph_error_handler_abort
which
prints an error message and aborts the program.
The igraph_set_error_handler()
function can be used to set a new
error handler function of type igraph_error_handler_t
; see the
documentation of this type for details.
There are two other predefined error handler functions,
igraph_error_handler_ignore
and igraph_error_handler_printignore
.
These deallocate the temporarily allocated memory (more about this
later) and return with the error code. The latter also prints an
error message. If you use these error handlers you need to take
care about possible errors yourself by checking the return value of
(almost) every non-void igraph function.
Independently of the error handler installed, all functions in the library do their best to leave their arguments semantically unchanged if an error happens. By semantically we mean that the implementation of an object supplied as an argument might change, but its “meaning” in most cases does not. The rare occasions when this rule is violated are documented in this manual.
typedef void igraph_error_handler_t(const char *reason, const char *file, int line, igraph_error_t igraph_errno);
This is the type of the error handler functions.
Arguments:
|
Textual description of the error. |
|
The source file in which the error is noticed. |
|
The number of the line in the source file which triggered the error |
|
The igraph error code. |
IGRAPH_FUNCATTR_NORETURN igraph_error_handler_t igraph_error_handler_abort;
The default error handler, prints an error message and aborts the program.
Every igraph function which can fail return a
single integer error code. Some functions are very simple and
cannot run into any error, these may return other types, or
void as well. The error codes are defined by the
igraph_error_type_t
enumeration.
typedef igraph_error_type_t igraph_error_t;
This type is used as the return type of igraph functions that return an
error code. It is a type alias because igraph_error_t used to be
an int
, and was used slightly differenly than igraph_error_type_t.
typedef enum { IGRAPH_SUCCESS = 0, IGRAPH_FAILURE = 1, IGRAPH_ENOMEM = 2, IGRAPH_PARSEERROR = 3, IGRAPH_EINVAL = 4, IGRAPH_EXISTS = 5, /* IGRAPH_EINVEVECTOR = 6, */ /* removed in 1.0 */ IGRAPH_EINVVID = 7, IGRAPH_EINVEID = 8, /* used to be IGRAPH_NONSQUARE before 1.0 */ IGRAPH_EINVMODE = 9, IGRAPH_EFILE = 10, IGRAPH_UNIMPLEMENTED = 12, IGRAPH_INTERRUPTED = 13, IGRAPH_DIVERGED = 14, IGRAPH_EARPACK = 15, /* ARPACK error codes from 15 to 36 were moved to igraph_arpack_error_t in 1.0 */ IGRAPH_ENEGLOOP = 37, IGRAPH_EINTERNAL = 38, /* ARPACK error codes from 39 to 41 were moved to igraph_arpack_error_t in 1.0 */ /* IGRAPH_EDIVZERO = 42, */ /* removed in 1.0 */ /* IGRAPH_GLP_EBOUND = 43, */ /* removed in 1.0 */ /* IGRAPH_GLP_EROOT = 44, */ /* removed in 1.0 */ /* IGRAPH_GLP_ENOPFS = 45, */ /* removed in 1.0 */ /* IGRAPH_GLP_ENODFS = 46, */ /* removed in 1.0 */ /* IGRAPH_GLP_EFAIL = 47, */ /* removed in 1.0 */ /* IGRAPH_GLP_EMIPGAP = 48, */ /* removed in 1.0 */ /* IGRAPH_GLP_ETMLIM = 49, */ /* removed in 1.0 */ /* IGRAPH_GLP_ESTOP = 50, */ /* removed in 1.0 */ /* IGRAPH_EATTRIBUTES = 51, */ /* rempved in 1.0 */ IGRAPH_EATTRCOMBINE = 52, /* IGRAPH_ELAPACK = 53, */ /* removed in 1.0 */ /* IGRAPH_EDRL = 54, */ /* deprecated in 0.10.2, removed in 1.0 */ IGRAPH_EOVERFLOW = 55, /* IGRAPH_EGLP = 56, */ /* removed in 1.0 */ /* IGRAPH_CPUTIME = 57, */ /* removed in 1.0 */ IGRAPH_EUNDERFLOW = 58, IGRAPH_ERWSTUCK = 59, IGRAPH_STOP = 60, IGRAPH_ERANGE = 61, IGRAPH_ENOSOL = 62 } igraph_error_type_t;
These are the possible values returned by igraph functions.
Note that these are interesting only if you defined an error handler
with igraph_set_error_handler()
. Otherwise the program is aborted
and the function causing the error never returns.
Values:
|
The function successfully completed its task. |
|
Something went wrong. You'll almost never meet this error as normally more specific error codes are used. |
|
There wasn't enough memory to allocate on the heap. |
|
A parse error was found in a file. |
|
A parameter's value is invalid. E.g. negative number was specified as the number of vertices. |
|
A graph/vertex/edge attribute is already installed with the given name. |
|
Invalid vertex ID, negative or too big. |
|
Invalid edge ID, negative or too big. |
|
Invalid mode parameter. |
|
A file operation failed. E.g. a file doesn't exist, or the user has no rights to open it. |
|
Attempted to call an unimplemented or disabled (at compile-time) function. |
|
A numeric algorithm failed to converge. |
|
An error happened inside a calculation implemented in ARPACK. The calculation involved is most likely an eigenvector-related calculation. |
|
Negative loop detected while calculating shortest paths. |
|
Internal error, likely a bug in igraph. |
|
Unimplemented attribute combination method for the given attribute type. |
|
Integer or double overflow. |
|
Integer or double underflow. |
|
Random walk got stuck. |
|
Maximum vertex or edge count exceeded. |
|
Input problem has no solution. |
igraph_warning_handler_t
— The type of igraph warning handler functions.igraph_set_warning_handler
— Installs a warning handler.IGRAPH_WARNING
— Triggers a warning.IGRAPH_WARNINGF
— Triggers a warning, with printf-like syntax.igraph_warning
— Reports a warning.igraph_warningf
— Reports a warning, printf-like version.igraph_warning_handler_ignore
— Ignores all warnings.igraph_warning_handler_print
— Prints all warnings to the standard error.igraph also supports warning messages in addition to error messages. Warning messages typically do not terminate the program, but they are usually crucial to the user.
igraph warnings are handled similarly to errors. There is a
separate warning handler function that is called whenever
an igraph function triggers a warning. This handler can be
set by the igraph_set_warning_handler()
function. There are
two predefined simple warning handlers,
igraph_warning_handler_ignore()
and
igraph_warning_handler_print()
, the latter being the default.
To trigger a warning, igraph functions typically use the
IGRAPH_WARNING()
macro, the igraph_warning()
function,
or if more flexibility is needed, igraph_warningf()
.
typedef void igraph_warning_handler_t(const char *reason, const char *file, int line);
Currently it is defined to have the same type as
igraph_error_handler_t
, although the last (error code)
argument is not used.
igraph_warning_handler_t *igraph_set_warning_handler(igraph_warning_handler_t *new_handler);
Install the supplied warning handler function.
Arguments:
|
The new warning handler function to install. Supply a null pointer here to uninstall the current warning handler, without installing a new one. |
Returns:
The current warning handler function. |
#define IGRAPH_WARNING(reason)
This is the usual way of triggering a warning from an igraph
function. It calls igraph_warning()
.
Arguments:
|
The warning message. |
#define IGRAPH_WARNINGF(reason, ...)
igraph functions can use this macro when they notice a warning and
want to pass on extra information to the user about what went wrong.
It calls igraph_warningf()
with the proper parameters and no
error code.
Arguments:
|
Textual description of the warning, a template string with the same syntax as the standard printf C library function. |
|
The additional arguments to be substituted into the template string. |
void igraph_warning(const char *reason, const char *file, int line);
Call this function if you want to trigger a warning from within a function that uses igraph.
Arguments:
|
Textual description of the warning. |
|
The source file in which the warning was noticed. |
|
The number of line in the source file which triggered the warning. |
|
Warnings could have potentially error codes as well, but this is currently not used in igraph. |
Returns:
The supplied error code. |
void igraph_warningf(const char *reason, const char *file, int line, ...);
This function is similar to igraph_warning()
, but
uses a printf-like syntax. It substitutes the additional arguments
into the reason
template string and calls igraph_warning()
.
Arguments:
|
Textual description of the warning, a template string with the same syntax as the standard printf C library function. |
|
The source file in which the warning was noticed. |
|
The number of line in the source file which triggered the warning. |
|
Warnings could have potentially error codes as well, but this is currently not used in igraph. |
|
The additional arguments to be substituted into the template string. |
void igraph_warning_handler_ignore(const char *reason, const char *file, int line);
This warning handler function simply ignores all warnings.
Arguments:
|
Textual description of the warning. |
|
The source file in which the warning was noticed. |
|
The number of line in the source file which triggered the warning.. |
|
Warnings could have potentially error codes as well, but this is currently not used in igraph. |
void igraph_warning_handler_print(const char *reason, const char *file, int line);
This warning handler function simply prints all warnings to the standard error.
Arguments:
|
Textual description of the warning. |
|
The source file in which the warning was noticed. |
|
The number of line in the source file which triggered the warning.. |
|
Warnings could have potentially error codes as well, but this is currently not used in igraph. |
The contents of the rest of this chapter might be useful only for those who want to create an interface to igraph from another language, or use igraph from a GUI application. Most readers can safely skip to the next chapter.
You can write and install error handlers simply by defining a
function of type igraph_error_handler_t
and calling
igraph_set_error_handler()
. This feature is useful for interface
writers, as igraph will have the chance to
signal errors the appropriate way. For example, the R interface uses
R's native printing facilities to communicate errors, while the Python
interface converts them into Python exceptions.
The two main tasks of the error handler are to report the error
(i.e. print the error message) and ensure proper resource cleanup.
This is ensured by calling IGRAPH_FINALLY_FREE()
, which deallocates
some of the temporary memory to avoid memory leaks. Note that this may
invalidate the error message buffer reason
passed to the error handler.
Do not access it after having called IGRAPH_FINALLY_FREE()
.
As of igraph 0.10, temporary memory is dellocated in stages, through
multiple calls to the error handler (and indirectly to IGRAPH_FINALLY_FREE()
).
Therefore, error handlers that do not abort the program
immediately are expected to return. The error handler should not perform
a longjmp
, as this may lead to some of the memory not
getting freed.
igraph_error_handler_t *igraph_set_error_handler(igraph_error_handler_t *new_handler);
Installs a new error handler. If called with NULL
, it installs the
default error handler (which is currently igraph_error_handler_abort
).
Arguments:
|
The error handler function to install. |
Returns:
The old error handler function. This should be saved and
restored if |
IGRAPH_ERROR
— Triggers an error.IGRAPH_ERRORF
— Triggers an error, with printf-like syntax.igraph_error
— Reports an error.igraph_errorf
— Reports an error, printf-like version.IGRAPH_CHECK
— Checks the return value of a function call.IGRAPH_CHECK_CALLBACK
— Checks the return value of a callback.
If an error happens, the functions in the library call the
IGRAPH_ERROR()
macro with a textual description of the error and an
igraph error code. This macro calls (through the igraph_error()
function) the installed error handler. Another useful
macro is IGRAPH_CHECK()
. This checks the return value of its
argument, which is normally a function call, and calls IGRAPH_ERROR()
if it is not IGRAPH_SUCCESS
.
#define IGRAPH_ERROR(reason, igraph_errno)
igraph functions usually use this macro when they notice an error.
It calls
igraph_error()
with the proper parameters and if that returns
the macro returns the "calling" function as well, with the error
code. If for some (suspicious) reason you want to call the error
handler without returning from the current function, call
igraph_error()
directly.
Arguments:
|
Textual description of the error. This should be
something more descriptive than the text associated with the error
code. E.g. if the error code is |
|
The igraph error code. |
#define IGRAPH_ERRORF(reason, igraph_errno, ...)
igraph functions can use this macro when they notice an error and
want to pass on extra information to the user about what went wrong.
It calls igraph_errorf()
with the proper parameters and if that
returns the macro returns the "calling" function as well, with the
error code. If for some (suspicious) reason you want to call the
error handler without returning from the current function, call
igraph_errorf()
directly.
Arguments:
|
Textual description of the error, a template string
with the same syntax as the standard printf C library function.
This should be something more descriptive than the text associated
with the error code. E.g. if the error code is |
|
The igraph error code. |
|
The additional arguments to be substituted into the template string. |
igraph_error_t igraph_error(const char *reason, const char *file, int line, igraph_error_t igraph_errno);
igraph functions usually call this function (most often via the
IGRAPH_ERROR
macro) if they notice an error.
It calls the currently installed error handler function with the
supplied arguments.
Arguments:
|
Textual description of the error. |
|
The source file in which the error was noticed. |
|
The number of line in the source file which triggered the error. |
|
The igraph error code. |
Returns:
the error code (if it returns) |
See also:
igraph_error_t igraph_errorf(const char *reason, const char *file, int line, igraph_error_t igraph_errno, ...);
Arguments:
|
Textual description of the error, interpreted as
a |
|
The source file in which the error was noticed. |
|
The line in the source file which triggered the error. |
|
The igraph error code. |
|
Additional parameters, the values to substitute into the format string. |
See also:
#define IGRAPH_CHECK(expr)
Arguments:
|
An expression, usually a function call. It is guaranteed to be evaluated only once. |
Executes the expression and checks its value. If this is not
IGRAPH_SUCCESS
, it calls IGRAPH_ERROR
with
the value as the error code. Here is an example usage:
IGRAPH_CHECK(vector_push_back(&v, 100));
There is only one reason to use this macro when writing
igraph functions. If the user installs an error handler which
returns to the auxiliary calling code (like igraph_error_handler_ignore
and igraph_error_handler_printignore
), and the igraph function
signalling the error is called from another igraph function
then we need to make sure that the error is propagated back to
the auxiliary (i.e. non-igraph) calling function. This is achieved
by using IGRAPH_CHECK
on every igraph
call which can return an error code.
#define IGRAPH_CHECK_CALLBACK(expr, code)
Identical to IGRAPH_CHECK
, but treats IGRAPH_STOP
as a normal
(non-erroneous) return code. This macro is used in some igraph functions
that allow the user to hook into a long-running calculation with a callback
function. When the user-defined callback function returns IGRAPH_SUCCESS
,
the calculation will proceed normally. Returning IGRAPH_STOP
from the
callback will terminate the calculation without reporting an error. Returning
any other value from the callback is treated as an error code, and igraph
will trigger the necessary cleanup functions before exiting the function.
Note that IGRAPH_CHECK_CALLBACK
does not handle IGRAPH_STOP
by any
means except returning it in the variable pointed to by code
. It is the
responsibility of the caller to handle IGRAPH_STOP
accordingly.
Arguments:
|
An expression, usually a call to a user-defined callback function. It is guaranteed to be evaluated only once. |
|
Pointer to an optional variable of type igraph_error_t; the value of this variable will be set to the error code if it is not a null pointer. |
If a function runs into an error (and the program is not aborted)
the error handler should deallocate all temporary memory. This is
done by storing the address and the destroy function of all temporary
objects in a stack. The IGRAPH_FINALLY
function declares an object as
temporary by placing its address in the stack. If an igraph function returns
with success it calls IGRAPH_FINALLY_CLEAN()
with the
number of objects to remove from the stack. If an error happens
however, the error handler should call IGRAPH_FINALLY_FREE()
to
deallocate each object added to the stack. This means that the
temporary objects allocated in the calling function (and etc.) will
be freed as well.
#define IGRAPH_FINALLY(func, ptr)
This macro places the address of an object, together with the
address of its destructor in a stack. This stack is used if an
error happens to deallocate temporarily allocated objects to
prevent memory leaks. After manual deallocation, objects are removed
from the stack using IGRAPH_FINALLY_CLEAN()
.
Arguments:
|
The function which is normally called to destroy the object. |
|
Pointer to the object itself. |
void IGRAPH_FINALLY_CLEAN(int num);
Removes the specified number of objects from the stack of temporarily allocated objects. It is typically called immediately after manually destroying the objects:
igraph_vector_t vector; igraph_vector_init(&vector, 10); IGRAPH_FINALLY(igraph_vector_destroy, &vector); // use vector igraph_vector_destroy(&vector); IGRAPH_FINALLY_CLEAN(1);
Arguments:
|
The number of objects to remove from the bookkeeping stack. |
void IGRAPH_FINALLY_FREE(void);
Calls the destroy function for all objects in the current level
of the stack of temporarily allocated objects, i.e. up to the
nearest mark set by IGRAPH_FINALLY_ENTER()
.
This function must only be called from an error handler.
It is not appropriate to use it
instead of destroying each unneeded object of a function, as it
destroys the temporary objects of the caller function (and so on)
as well.
There are some simple rules to keep in order to have functions
behaving well in erroneous situations. First, check the arguments
of the functions and call IGRAPH_ERROR()
if they are invalid. Second,
call IGRAPH_FINALLY
on each dynamically allocated object and call
IGRAPH_FINALLY_CLEAN()
with the proper argument before returning. Third, use
IGRAPH_CHECK
on all igraph function calls which can generate errors.
The size of the stack used for this bookkeeping is fixed, and
small. If you want to allocate several objects, write a destroy
function which can deallocate all of these. See the
adjlist.c
file in the
igraph source for an example.
For some functions these mechanisms are simply not flexible enough. These functions should define their own error handlers and restore the error handler before they return.
Example 5.1. File examples/simple/igraph_contract_vertices.c
#include <igraph.h> /* Create the condensation of a directed graph. * See https://en.wikipedia.org/wiki/Strongly_connected_component#Definitions * This example demonstrates how to write a basic igraph function, complete * with error handling. */ igraph_error_t condensation(const igraph_t *graph, igraph_t *cond) { igraph_vector_int_t membership; /* Data structures such as vector must be initialized in igraph before use. */ IGRAPH_CHECK(igraph_vector_int_init(&membership, 0)); /* Adding the initialized vector to the "finally" stack ensures that it will * be automatically destroyed if an error occurs. */ IGRAPH_FINALLY(igraph_vector_int_destroy, &membership); /* Functions that return an error code can be wrapped in IGRAPH_CHECK to pass that error * up to the caller. */ IGRAPH_CHECK(igraph_connected_components(graph, &membership, /* csize */ NULL, /* no */ NULL, IGRAPH_STRONG)); /* To compute the condensation, we simply contract strongly connected components. * Since igraph_contract_vertices() modifies graphs in-place, we make a copy first. */ IGRAPH_CHECK(igraph_copy(cond, graph)); /* Since we are not done creating the condensation yet, we add 'cond' to the * "finally" stack, so that it will be destroyed if an error occurs. */ IGRAPH_FINALLY(igraph_destroy, cond); /* Contract strongly connected components. */ IGRAPH_CHECK(igraph_contract_vertices(cond, &membership, NULL)); /* igraph_contract_vertices() preserves all edges, some of which become * parallel edges or self-loops after the contraction. We simplify these. */ IGRAPH_CHECK(igraph_simplify(cond, /* remove_multiple */ true, /* remove_loops */ true, NULL)); /* Data structures that are no longer needed must be explicitly destroyed. * If they were added to the "finally" stack, they must be removed explicitly, * in the opposite order to how they were added. IGRAPH_FINALLY_CLEAN removes * the indicated number of entries from the "finally" stack. We remove * 'membership' because it was destroyed, and 'cond' because the responsibility * to destroy it is now with the caller. */ igraph_vector_int_destroy(&membership); IGRAPH_FINALLY_CLEAN(2); return IGRAPH_SUCCESS; /* return with no error */ } int main(void) { igraph_t graph, cond; /* Create a random directed graph with mean degree 2 and compute its condensation. */ igraph_erdos_renyi_game_gnm(&graph, 100, 200, IGRAPH_DIRECTED, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE); condensation(&graph, &cond); printf("Number of vertices in the condensation: %" IGRAPH_PRId "\n", igraph_vcount(&cond)); igraph_write_graph_edgelist(&cond, stdout); /* Destroy data structures that are no longer needed. */ igraph_destroy(&graph); igraph_destroy(&cond); return 0; }
igraph_fatal_handler_t
— The type of igraph fatal error handler functions.igraph_set_fatal_handler
— Installs a fatal error handler.igraph_fatal_handler_abort
— Abort program in case of fatal error.IGRAPH_FATAL
— Triggers a fatal error.IGRAPH_FATALF
— Triggers a fatal error, with printf-like syntax.IGRAPH_ASSERT
— igraph-specific replacement for assert()
.igraph_fatal
— Triggers a fatal error.igraph_fatalf
— Triggers a fatal error, printf-like syntax.In some rare situations, igraph may encounter an internal error that cannot be fully handled. In this case, it will call the current fatal error handler. The default fatal error handler simply prints the error and aborts the program.
Fatal error handlers do not return. Typically, they might abort the
the program immediately, or in the case of the high-level igraph
interfaces, they might return to the top level using a
longjmp()
. The fatal error handler is only called when
a serious error has occurred, and as a result igraph may be in an
inconsistent state. The purpose of returning to the top level is to
give the user a chance to save their work instead of aborting immediately.
However, the program session should be restarted as soon as possible.
Most projects that use igraph will use the default fatal error handler.
typedef void igraph_fatal_handler_t(const char *reason, const char *file, int line);
Functions of this type must not return. Typically they
call abort()
or do a longjmp()
.
Arguments:
|
Textual description of the error. |
|
The source file in which the error is noticed. |
|
The number of the line in the source file which triggered the error. |
igraph_fatal_handler_t *igraph_set_fatal_handler(igraph_fatal_handler_t *new_handler);
Installs the supplied fatal error handler function.
Fatal error handler functions must not return. Typically, the fatal
error handler would either call abort()
or longjmp()
.
Arguments:
|
The new fatal error handler function to install. Supply a null pointer here to uninstall the current fatal error handler, without installing a new one. |
Returns:
The current fatal error handler function. |
IGRAPH_FUNCATTR_NORETURN igraph_fatal_handler_t igraph_fatal_handler_abort;
The default fatal error handler, prints an error message and aborts the program.
#define IGRAPH_FATAL(reason)
This is the usual way of triggering a fatal error from an igraph
function. It calls igraph_fatal()
.
Use this macro only in situations where the error cannot be handled.
The normal way to handle errors is IGRAPH_ERROR()
.
Arguments:
|
The error message. |
#define IGRAPH_FATALF(reason, ...)
igraph functions can use this macro when a fatal error occurs and
want to pass on extra information to the user about what went wrong.
It calls igraph_fatalf()
with the proper parameters.
Arguments:
|
Textual description of the error, a template string with the same syntax as the standard printf C library function. |
|
The additional arguments to be substituted into the template string. |
#define IGRAPH_ASSERT(condition)
This macro is like the standard assert()
, but instead of
calling abort()
, it calls igraph_fatal()
. This allows for returning
the control to the calling program, e.g. returning to the top level in a high-level
igraph interface.
Unlike assert()
, IGRAPH_ASSERT()
is not disabled
when the NDEBUG
macro is defined.
This macro is meant for internal use by igraph.
Since a typical fatal error handler does a longjmp()
, avoid using this
macro in C++ code. With most compilers, destructor will not be called when
longjmp()
leaves the current scope.
Arguments:
|
The condition to be checked. |
void igraph_fatal(const char *reason, const char *file, int line);
This function triggers a fatal error. Typically it is called indirectly through
IGRAPH_FATAL()
or IGRAPH_ASSERT()
.
Arguments:
|
Textual description of the error. |
|
The source file in which the error was noticed. |
|
The number of line in the source file which triggered the error. |
void igraph_fatalf(const char *reason, const char *file, int line, ...);
This function is similar to igraph_fatal()
, but
uses a printf-like syntax. It substitutes the additional arguments
into the reason
template string and calls igraph_fatal()
.
Arguments:
|
Textual description of the error. |
|
The source file in which the error was noticed. |
|
The number of line in the source file which triggered the error. |
|
The additional arguments to be substituted into the template string. |
← Chapter 4. Basic data types and interface | Chapter 6. Memory (de)allocation → |