Error Tutorial
Introduction
The Eina error module provides a way to manage errors in a simple but powerful way in libraries and modules. It is also used in Eina itself. It provides facilities for displaying different kind of messages (warning, informations, errors, or debug), which can be tuned by the user, or for registering new errors messages.Basic Usage
The first thing to do when using the error module is to initialize it with eina_error_init() and, when the error module is not used anymore, to shut down it with eina_error_shutdown(). So a basic program would look like that:
#include <stdlib.h> #include <stdio.h> #include <eina_error.h> int main(void) { if (!eina_error_init()) { printf ("Error during the initialization of eina_error module\n"); return EXIT_FAILURE; } eina_error_shutdown(); return EXIT_SUCCESS; }
All program using any module of eina must be compiled with the following command:
gcc -Wall -o my_exe my_source.c `pkg-config --cflags --libs eina`
Now that the error module is initialized, error messages can be displayed. Helper macros are already defined for such purpose:
Here is an example:
#include <stdlib.h> #include <stdio.h> #include <eina_error.h> void test(int i) { EINA_ERROR_PDBG("Entering test\n"); if (i < 0) { EINA_ERROR_PERR("argument is negative\n"); return; } EINA_ERROR_PINFO("argument non negative\n"); EINA_ERROR_PDBG("Exiting test\n"); } int main(void) { if (!eina_error_init()) { printf ("Error during the initialization of eina_error module\n"); return EXIT_FAILURE; } test(-1); test(0); eina_error_shutdown(); return EXIT_SUCCESS; }
If you compiled Eina without debug mode, after executing that program, you will see only 1 message (the argument being negative). Why ? These macros are just wrappers around eina_error_print(). This function only displays messages if the current error level is lesser or equal than the one used by eina_error_print(). By default, the current error level is EINA_ERROR_LEVEL_ERR (in non debug mode), and the macro uses the error values defines by Eina_Error_Level. So as EINA_ERROR_LEVEL_ERR is the smallest value, only EINA_ERROR_PERR() will display the message.
To modify this behavior, you have two solutions:
- Using the environment variable EINA_ERROR_LEVEL. In that case, just set the environment variable to a integer number between 0 (EINA_ERROR_LEVEL_ERR) and 3 (EINA_ERROR_LEVEL_DBG) before executing the program. That environment variable will be read during the first call of eina_error_init().
- Using the function eina_error_log_level_set().
EINA_ERROR_LEVEL=2 ./my_app
To do the same with eina_error_log_level_set(), just add
before the calls of the tests in the above example.
Advanced usage of print callbacks
The error module allows the user to change the way eina_error_print() displays the messages. It suffices to pass to eina_error_print_cb_set() the function used to display the message. That function must be of type Eina_Error_Print_Cb. As a custom data can be passed to that callback, powerful display messages can be displayed.It is suggested to not use __FILE__, __FUNCTION__ or __LINE__ when writing that callback, but when defining macros (like EINA_ERROR_PERR() and other macros).
Here is an example of custom callback, whose behavior can be changed at runtime:
#include <stdlib.h> #include <stdio.h> #include <eina_error.h> #define ERROR(fmt, ...) \ eina_error_print(EINA_ERROR_LEVEL_ERR, __FILE__, __FUNCTION__, __LINE__, fmt, ##__VA_ARGS__) typedef struct _Data Data; struct _Data { int to_stderr; }; void print_cb(Eina_Error_Level level, const char *file, const char *fnc, int line, const char *fmt, void *data, va_list args) { Data *d; FILE *output; char *str; d = (Data *)data; if (d->to_stderr) { output = stderr; str = "stderr"; } else { output = stdout; str = "stdout"; } fprintf(output, "%s:%s (%d) %s: ", file, fnc, line, str); vfprintf(output, fmt, args); } void test(Data *data, int i) { if (i < 0) data->to_stderr = 0; else data->to_stderr = 1; ERROR("error message...\n"); } int main(void) { Data *data; if (!eina_error_init()) { printf ("Error during the initialization of eina_error module\n"); return EXIT_FAILURE; } data = (Data *)malloc(sizeof(Data)); if (!data) { printf ("Error during memory allocation\n"); eina_error_shutdown(); return EXIT_FAILURE; } eina_error_print_cb_set(print_cb, data); test(data, -1); test(data, 0); eina_error_shutdown(); return EXIT_SUCCESS; }
Registering messages
The error module can provide a system that mimic the errno system of the C standard library. It consists in 2 parts:
- a way of registering new messages with eina_error_msg_register() and eina_error_msg_get(),
- a way of setting / getting last error message with eina_error_set() / eina_error_get().
Here is an example of use:
#include <stdlib.h> #include <stdio.h> #include <eina_error.h> Eina_Error MY_ERROR_NEGATIVE; Eina_Error MY_ERROR_NULL; voi *data_new() { eina_error_set(0); eina_error_set(MY_ERROR_NULL); return NULL; } int test(int n) { eina_error_set(0); if (n < 0) { eina_error_set(MY_ERROR_NEGATIVE); return 0; } return 1; } int main(void) { void *data; if (!eina_error_init()) { printf ("Error during the initialization of eina_error module\n"); return EXIT_FAILURE; } MY_ERROR_NEGATIVE = eina_error_msg_register("Negative number"); MY_ERROR_NULL = eina_error_msg_register("NULL pointer"); data = data_new(); if (!data) { Eina_Error err; err = eina_error_get(); if (err) printf("Error during memory allocation: %s\n", eina_error_msg_get(err)); } if (!test(0)) { Eina_Error err; err = eina_error_get(); if (err) printf("Error during test function: %s\n", eina_error_msg_get(err)); } if (!test(-1)) { Eina_Error err; err = eina_error_get(); if (err) printf("Error during test function: %s\n", eina_error_msg_get(err)); } eina_error_shutdown(); return EXIT_SUCCESS; }
Of course, instead of printf(), eina_error_print() can be used to have beautiful error messages.